1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <string>
16 #include <vector>
17 #include <map>
18
19 #ifdef KOALA_INTEROP_MODULE
20 #undef KOALA_INTEROP_MODULE
21 #endif
22
23 #define KOALA_INTEROP_MODULE InteropNativeModule
24 #include "common-interop.h"
25 #include "interop-logging.h"
26 #include "dynamic-loader.h"
27 #include "securec.h"
28
29 #ifdef KOALA_FOREIGN_NAPI
30 #ifndef KOALA_FOREIGN_NAPI_OHOS
31 #include <node_api.h>
32 #else
33 #include <native_api.h>
34 #include <native_node_api.h>
35 #endif
36 #endif
37
38 #if KOALA_INTEROP_PROFILER
39 #include "profiler.h"
40
41 InteropProfiler* InteropProfiler::_instance = nullptr;
42
43 #endif
44
45 using std::string;
46
47 #ifdef KOALA_NAPI
48 // Callback dispatcher MOVED to convertors-napi.cc.
49 // Let's keep platform-specific parts of the code together
50
51 typedef void (*hold_t)(KInt);
52
impl_MaterializeBuffer(KNativePointer data,KLong length,KInt resourceId,KNativePointer holdPtr,KNativePointer releasePtr)53 KInteropBuffer impl_MaterializeBuffer(KNativePointer data, KLong length, KInt resourceId, KNativePointer holdPtr, KNativePointer releasePtr) {
54 auto hold = reinterpret_cast<void(*)(KInt)>(holdPtr);
55 auto release = reinterpret_cast<void(*)(KInt)>(releasePtr);
56 hold(resourceId);
57 return KInteropBuffer { length, data, resourceId, release };
58 }
KOALA_INTEROP_5(MaterializeBuffer,KInteropBuffer,KNativePointer,KLong,KInt,KNativePointer,KNativePointer)59 KOALA_INTEROP_5(MaterializeBuffer, KInteropBuffer, KNativePointer, KLong, KInt, KNativePointer, KNativePointer)
60
61 KNativePointer impl_GetNativeBufferPointer(KInteropBuffer buffer) {
62 return buffer.data;
63 }
64 KOALA_INTEROP_1(GetNativeBufferPointer, KNativePointer, KInteropBuffer)
65
66 #endif
67
68 #ifdef KOALA_ETS_NAPI
69 #include "etsapi.h"
70
71 static struct {
72 ets_class clazz = nullptr;
73 ets_method method = nullptr;
74 } g_koalaEtsNapiCallbackDispatcher;
75
setKoalaEtsNapiCallbackDispatcher(EtsEnv * etsEnv,ets_class clazz,const char * dispatcherMethodName,const char * dispactherMethodSig)76 bool setKoalaEtsNapiCallbackDispatcher(
77 EtsEnv* etsEnv,
78 ets_class clazz,
79 const char* dispatcherMethodName,
80 const char* dispactherMethodSig
81 ) {
82 g_koalaEtsNapiCallbackDispatcher.clazz = clazz;
83 etsEnv->NewGlobalRef(clazz);
84 ets_method method = etsEnv->GetStaticp_method(
85 clazz, dispatcherMethodName, dispactherMethodSig
86 );
87 if (method == nullptr) {
88 return false;
89 }
90 g_koalaEtsNapiCallbackDispatcher.method = method;
91 return true;
92 }
93
getKoalaEtsNapiCallbackDispatcher(ets_class * clazz,ets_method * method)94 void getKoalaEtsNapiCallbackDispatcher(ets_class* clazz, ets_method* method) {
95 *clazz = g_koalaEtsNapiCallbackDispatcher.clazz;
96 *method = g_koalaEtsNapiCallbackDispatcher.method;
97 }
98 #endif
99
100
101 // TODO: move callback dispetchers to convertors-<flavour>.cc.
102 #ifdef KOALA_JNI
103 #include "jni.h"
104 static struct {
105 jclass clazz = nullptr;
106 jmethodID method = nullptr;
107 } g_koalaJniCallbackDispatcher;
108
setKoalaJniCallbackDispatcher(JNIEnv * jniEnv,jclass clazz,const char * dispatcherMethodName,const char * dispactherMethodSig)109 bool setKoalaJniCallbackDispatcher(
110 JNIEnv* jniEnv,
111 jclass clazz,
112 const char* dispatcherMethodName,
113 const char* dispactherMethodSig
114 ) {
115 g_koalaJniCallbackDispatcher.clazz = clazz;
116 jniEnv->NewGlobalRef(clazz);
117 jmethodID method = jniEnv->GetStaticMethodID(
118 clazz, dispatcherMethodName, dispactherMethodSig
119 );
120 if (method == nullptr) {
121 return false;
122 }
123 g_koalaJniCallbackDispatcher.method = method;
124 return true;
125 }
126
getKoalaJniCallbackDispatcher(jclass * clazz,jmethodID * method)127 void getKoalaJniCallbackDispatcher(jclass* clazz, jmethodID* method) {
128 *clazz = g_koalaJniCallbackDispatcher.clazz;
129 *method = g_koalaJniCallbackDispatcher.method;
130 }
131 #endif
132
impl_StringLength(KNativePointer ptr)133 KInt impl_StringLength(KNativePointer ptr) {
134 string* s = reinterpret_cast<string*>(ptr);
135 return s->length();
136 }
KOALA_INTEROP_1(StringLength,KInt,KNativePointer)137 KOALA_INTEROP_1(StringLength, KInt, KNativePointer)
138
139 void impl_StringData(KNativePointer ptr, KByte* bytes, KUInt size) {
140 string* s = reinterpret_cast<string*>(ptr);
141 if (s) memcpy_s(bytes, size, s->c_str(), size);
142 }
KOALA_INTEROP_V3(StringData,KNativePointer,KByte *,KUInt)143 KOALA_INTEROP_V3(StringData, KNativePointer, KByte*, KUInt)
144
145
146 #ifdef KOALA_JNI
147 // For Java only yet.
148 KInteropBuffer impl_StringDataBytes(KVMContext vmContext, KNativePointer ptr) {
149 string* s = reinterpret_cast<std::string*>(ptr);
150 KInteropBuffer result = { (int32_t)s->length(), (void*)s->c_str()};
151 return result;
152 }
KOALA_INTEROP_CTX_1(StringDataBytes,KInteropBuffer,KNativePointer)153 KOALA_INTEROP_CTX_1(StringDataBytes, KInteropBuffer, KNativePointer)
154 #endif
155
156 KNativePointer impl_StringMake(const KStringPtr& str) {
157 return new string(str.c_str());
158 }
KOALA_INTEROP_1(StringMake,KNativePointer,KStringPtr)159 KOALA_INTEROP_1(StringMake, KNativePointer, KStringPtr)
160
161 // For slow runtimes w/o fast encoders.
162 KInt impl_ManagedStringWrite(const KStringPtr& string, KSerializerBuffer buffer, KInt offset) {
163 if (memcpy_s((uint8_t*)buffer + offset, string.length() + 1, string.c_str(), string.length() + 1) != 0) {
164 return 0;
165 }
166 return string.length() + 1;
167 }
KOALA_INTEROP_3(ManagedStringWrite,KInt,KStringPtr,KSerializerBuffer,KInt)168 KOALA_INTEROP_3(ManagedStringWrite, KInt, KStringPtr, KSerializerBuffer, KInt)
169
170 void stringFinalizer(string* ptr) {
171 delete ptr;
172 }
impl_GetStringFinalizer()173 KNativePointer impl_GetStringFinalizer() {
174 return fnPtr<string>(stringFinalizer);
175 }
KOALA_INTEROP_0(GetStringFinalizer,KNativePointer)176 KOALA_INTEROP_0(GetStringFinalizer, KNativePointer)
177
178 void impl_InvokeFinalizer(KNativePointer obj, KNativePointer finalizer) {
179 auto finalizer_f = reinterpret_cast<void (*)(KNativePointer)>(finalizer);
180 finalizer_f(obj);
181 }
KOALA_INTEROP_V2(InvokeFinalizer,KNativePointer,KNativePointer)182 KOALA_INTEROP_V2(InvokeFinalizer, KNativePointer, KNativePointer)
183
184 KInt impl_GetPtrVectorSize(KNativePointer ptr) {
185 return reinterpret_cast<std::vector<void*>*>(ptr)->size();
186 }
KOALA_INTEROP_1(GetPtrVectorSize,KInt,KNativePointer)187 KOALA_INTEROP_1(GetPtrVectorSize, KInt, KNativePointer)
188
189 KNativePointer impl_GetPtrVectorElement(KNativePointer ptr, KInt index) {
190 auto vector = reinterpret_cast<std::vector<void*>*>(ptr);
191 auto element = vector->at(index);
192 return nativePtr(element);
193 }
KOALA_INTEROP_2(GetPtrVectorElement,KNativePointer,KNativePointer,KInt)194 KOALA_INTEROP_2(GetPtrVectorElement, KNativePointer, KNativePointer, KInt)
195
196 inline KUInt unpackUInt(const KByte* bytes) {
197 return (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24));
198 }
199
makeStringVector(KStringArray strArray)200 std::vector<KStringPtr> makeStringVector(KStringArray strArray) {
201 if (strArray == nullptr) {
202 return std::vector<KStringPtr>(0);
203 }
204 KUInt arraySize = unpackUInt(strArray);
205 std::vector<KStringPtr> res(arraySize);
206 size_t offset = sizeof(KUInt);
207 for (KUInt i = 0; i < arraySize; ++i) {
208 int len = unpackUInt(strArray + offset);
209 res[i].assign((const char*)(strArray + offset + sizeof(KUInt)), len);
210 offset += len + sizeof(KUInt);
211 }
212 return res;
213 }
214
makeStringVector(KNativePointerArray arr,KInt length)215 std::vector<KStringPtr> makeStringVector(KNativePointerArray arr, KInt length) {
216 if (arr == nullptr) {
217 return std::vector<KStringPtr>(0);
218 } else {
219 std::vector<KStringPtr> res(length);
220 char** strings = reinterpret_cast<char**>(arr);
221 for (KInt i = 0; i < length; ++i) {
222 const char* str = reinterpret_cast<const char*>(strings[i]);
223 res[i].assign(str);
224 }
225 return res;
226 }
227 }
228
impl_GetGroupedLog(KInt index)229 KNativePointer impl_GetGroupedLog(KInt index) {
230 return new std::string(GetDefaultLogger()->getGroupedLog(index));
231 }
KOALA_INTEROP_1(GetGroupedLog,KNativePointer,KInt)232 KOALA_INTEROP_1(GetGroupedLog, KNativePointer, KInt)
233
234 void impl_StartGroupedLog(KInt index) {
235 GetDefaultLogger()->startGroupedLog(index);
236 }
KOALA_INTEROP_V1(StartGroupedLog,KInt)237 KOALA_INTEROP_V1(StartGroupedLog, KInt)
238
239 void impl_StopGroupedLog(KInt index) {
240 GetDefaultLogger()->stopGroupedLog(index);
241 }
KOALA_INTEROP_V1(StopGroupedLog,KInt)242 KOALA_INTEROP_V1(StopGroupedLog, KInt)
243
244 void impl_AppendGroupedLog(KInt index, const KStringPtr& message) {
245 if (GetDefaultLogger()->needGroupedLog(index))
246 GetDefaultLogger()->appendGroupedLog(index, message.c_str());
247 }
KOALA_INTEROP_V2(AppendGroupedLog,KInt,KStringPtr)248 KOALA_INTEROP_V2(AppendGroupedLog, KInt, KStringPtr)
249
250 void impl_PrintGroupedLog(KInt index) {
251 #ifdef KOALA_OHOS
252 LOGI("%" LOG_PUBLIC "s", GetDefaultLogger()->getGroupedLog(index));
253 #else
254 fprintf(stdout, "%s\n", GetDefaultLogger()->getGroupedLog(index));
255 fflush(stdout);
256 #endif
257 }
KOALA_INTEROP_V1(PrintGroupedLog,KInt)258 KOALA_INTEROP_V1(PrintGroupedLog, KInt)
259
260 int32_t callCallback(KVMContext context, int32_t methodId, uint8_t* argsData, int32_t argsLength) {
261 #if KOALA_USE_NODE_VM || KOALA_USE_HZ_VM || KOALA_USE_PANDA_VM || KOALA_USE_JAVA_VM || KOALA_CJ
262 KOALA_INTEROP_CALL_INT(context, methodId, argsLength, argsData);
263 return 0;
264 #else
265 return 0;
266 #endif
267 }
268
269 struct ForeignVMContext {
270 KVMContext vmContext;
271 int32_t (*callSync)(KVMContext vmContext, int32_t callback, uint8_t* data, int32_t length);
272 };
273 typedef KInt (*LoadVirtualMachine_t)(KInt vmKind, const char* classPath, const char* libraryPath, const struct ForeignVMContext* foreignVM);
274 typedef KNativePointer (*StartApplication_t)(const char* appUrl, const char* appParams);
275 typedef KBoolean (*RunApplication_t)(const KInt arg0, const KInt arg1);
276 typedef const char* (*EmitEvent_t)(const KInt type, const KInt target, const KInt arg0, const KInt arg1);
277 typedef void (*RestartWith_t)(const char* page);
278
getImpl(const char * path,const char * name)279 void* getImpl(const char* path, const char* name) {
280 static void* lib = nullptr;
281 if (!lib && name) {
282 auto name =
283 #ifndef KOALA_OHOS // dlopen on OHOS doesn't like paths
284 std::string(path) + "/" +
285 #endif
286 libName("vmloader");
287 lib = loadLibrary(name);
288 if (!lib) {
289 fprintf(stderr, "Ensure vmloader library %s was built\n", name.c_str());
290 }
291 }
292 return findSymbol(lib, name);
293 }
294
impl_LoadVirtualMachine(KVMContext vmContext,KInt vmKind,const KStringPtr & classPath,const KStringPtr & libraryPath)295 KInt impl_LoadVirtualMachine(KVMContext vmContext, KInt vmKind, const KStringPtr& classPath, const KStringPtr& libraryPath) {
296 const char* envClassPath = std::getenv("PANDA_CLASS_PATH");
297 if (envClassPath) {
298 LOGI("CLASS PATH updated from env var PANDA_CLASS_PATH, %" LOG_PUBLIC "s", envClassPath);
299 }
300 const char* appClassPath = envClassPath ? envClassPath : classPath.c_str();
301 const char* nativeLibPath = envClassPath ? envClassPath : libraryPath.c_str();
302
303 static LoadVirtualMachine_t impl = nullptr;
304 if (!impl) impl = reinterpret_cast<LoadVirtualMachine_t>(getImpl(nativeLibPath, "LoadVirtualMachine"));
305 if (!impl) KOALA_INTEROP_THROW_STRING(vmContext, "Cannot load VM", -1);
306 const ForeignVMContext foreignVM = {
307 vmContext, &callCallback
308 };
309 return impl(vmKind, appClassPath, nativeLibPath, &foreignVM);
310 }
KOALA_INTEROP_CTX_3(LoadVirtualMachine,KInt,KInt,KStringPtr,KStringPtr)311 KOALA_INTEROP_CTX_3(LoadVirtualMachine, KInt, KInt, KStringPtr, KStringPtr)
312
313 KNativePointer impl_StartApplication(const KStringPtr& appUrl, const KStringPtr& appParams) {
314 static StartApplication_t impl = nullptr;
315 if (!impl) impl = reinterpret_cast<StartApplication_t>(getImpl(nullptr, "StartApplication"));
316 return impl(appUrl.c_str(), appParams.c_str());
317 }
KOALA_INTEROP_2(StartApplication,KNativePointer,KStringPtr,KStringPtr)318 KOALA_INTEROP_2(StartApplication, KNativePointer, KStringPtr, KStringPtr)
319
320 KBoolean impl_RunApplication(const KInt arg0, const KInt arg1) {
321 static RunApplication_t impl = nullptr;
322 if (!impl) impl = reinterpret_cast<RunApplication_t>(getImpl(nullptr, "RunApplication"));
323 return impl(arg0, arg1);
324 }
KOALA_INTEROP_2(RunApplication,KBoolean,KInt,KInt)325 KOALA_INTEROP_2(RunApplication, KBoolean, KInt, KInt)
326
327 KStringPtr impl_EmitEvent(KVMContext vmContext, KInt type, KInt target, KInt arg0, KInt arg1) {
328 static EmitEvent_t impl = nullptr;
329 if (!impl) impl = reinterpret_cast<EmitEvent_t>(getImpl(nullptr, "EmitEvent"));
330 const char* out = impl(type, target, arg0, arg1);
331 auto size = std::string(out).size();
332 KStringPtr result(out, size, true);
333 return result;
334 }
KOALA_INTEROP_CTX_4(EmitEvent,KStringPtr,KInt,KInt,KInt,KInt)335 KOALA_INTEROP_CTX_4(EmitEvent, KStringPtr, KInt, KInt, KInt, KInt)
336
337 void impl_RestartWith(const KStringPtr& page) {
338 static RestartWith_t impl = nullptr;
339 if (!impl) impl = reinterpret_cast<RestartWith_t>(getImpl(nullptr, "RestartWith"));
340 impl(page.c_str());
341 }
KOALA_INTEROP_V1(RestartWith,KStringPtr)342 KOALA_INTEROP_V1(RestartWith, KStringPtr)
343
344 KNativePointer impl_Malloc(KLong length) {
345 return malloc(length);
346 }
KOALA_INTEROP_DIRECT_1(Malloc,KNativePointer,KLong)347 KOALA_INTEROP_DIRECT_1(Malloc, KNativePointer, KLong)
348
349 void impl_Free(KNativePointer data) {
350 return free(data);
351 }
KOALA_INTEROP_DIRECT_V1(Free,KNativePointer)352 KOALA_INTEROP_DIRECT_V1(Free, KNativePointer)
353
354 KInt impl_ReadByte(KNativePointer data, KLong index, KLong length) {
355 if (index >= length) INTEROP_FATAL("impl_ReadByte: index %lld is equal or greater than length %lld", (long long)index, (long long) length);
356 uint8_t* ptr = reinterpret_cast<uint8_t*>(data);
357 return ptr[index];
358 }
KOALA_INTEROP_DIRECT_3(ReadByte,KInt,KNativePointer,KLong,KLong)359 KOALA_INTEROP_DIRECT_3(ReadByte, KInt, KNativePointer, KLong, KLong)
360
361 void impl_WriteByte(KNativePointer data, KInt index, KLong length, KInt value) {
362 if (index >= length) INTEROP_FATAL("impl_WriteByte: index %lld is equal or greater than length %lld", (long long)index, (long long) length);
363 uint8_t* ptr = reinterpret_cast<uint8_t*>(data);
364 ptr[index] = value;
365 }
KOALA_INTEROP_DIRECT_V4(WriteByte,KNativePointer,KLong,KLong,KInt)366 KOALA_INTEROP_DIRECT_V4(WriteByte, KNativePointer, KLong, KLong, KInt)
367
368 void impl_CopyArray(KNativePointer data, KLong length, KByte* array) {
369 if (!array || !data) {
370 INTEROP_FATAL("CopyArray called with incorrect nullptr args (array, data):(%p, %p)", array, data);
371 }
372
373 memcpy_s(data, length, array, length);
374 }
375 KOALA_INTEROP_V3(CopyArray, KNativePointer, KLong, KByte*)
376
377 static Callback_Caller_t g_callbackCaller = nullptr;
setCallbackCaller(Callback_Caller_t callbackCaller)378 void setCallbackCaller(Callback_Caller_t callbackCaller) {
379 g_callbackCaller = callbackCaller;
380 }
381
impl_CallCallback(KInt callbackKind,KSerializerBuffer args,KInt argsSize)382 void impl_CallCallback(KInt callbackKind, KSerializerBuffer args, KInt argsSize) {
383 if (g_callbackCaller) {
384 g_callbackCaller(callbackKind, args, argsSize);
385 }
386 }
387 KOALA_INTEROP_V3(CallCallback, KInt, KSerializerBuffer, KInt)
388
389 static Callback_Caller_Sync_t g_callbackCallerSync = nullptr;
setCallbackCallerSync(Callback_Caller_Sync_t callbackCallerSync)390 void setCallbackCallerSync(Callback_Caller_Sync_t callbackCallerSync) {
391 g_callbackCallerSync = callbackCallerSync;
392 }
393
impl_CallCallbackSync(KVMContext vmContext,KInt callbackKind,KSerializerBuffer args,KInt argsSize)394 void impl_CallCallbackSync(KVMContext vmContext, KInt callbackKind, KSerializerBuffer args, KInt argsSize) {
395 if (g_callbackCallerSync) {
396 g_callbackCallerSync(vmContext, callbackKind, args, argsSize);
397 }
398 }
KOALA_INTEROP_CTX_V3(CallCallbackSync,KInt,KSerializerBuffer,KInt)399 KOALA_INTEROP_CTX_V3(CallCallbackSync, KInt, KSerializerBuffer, KInt)
400
401 void impl_CallCallbackResourceHolder(KNativePointer holder, KInt resourceId) {
402 reinterpret_cast<void(*)(KInt)>(holder)(resourceId);
403 }
KOALA_INTEROP_V2(CallCallbackResourceHolder,KNativePointer,KInt)404 KOALA_INTEROP_V2(CallCallbackResourceHolder, KNativePointer, KInt)
405
406 void impl_CallCallbackResourceReleaser(KNativePointer releaser, KInt resourceId) {
407 reinterpret_cast<void(*)(KInt)>(releaser)(resourceId);
408 }
KOALA_INTEROP_V2(CallCallbackResourceReleaser,KNativePointer,KInt)409 KOALA_INTEROP_V2(CallCallbackResourceReleaser, KNativePointer, KInt)
410
411 KInt impl_CallForeignVM(KNativePointer foreignContextRaw, KInt function, KByte* data, KInt length) {
412 const ForeignVMContext* foreignContext = (const ForeignVMContext*)foreignContextRaw;
413 // TODO: set actuall callbacks caller/holder/releaser.
414 /*
415 *(int64_t*)(data + 8) = impl_CallCallbackSync;
416 *(int64_t*)(data + 16) = 0;
417 *(int64_t*)(data + 24) = 0; */
418 return foreignContext->callSync(foreignContext->vmContext, function, data, length);
419 }
420 KOALA_INTEROP_4(CallForeignVM, KInt, KNativePointer, KInt, KByte*, KInt)
421
422 #ifdef KOALA_FOREIGN_NAPI
423 KVMContext g_foreignVMContext = nullptr;
424 #endif
impl_SetForeignVMContext(KNativePointer foreignVMContextRaw)425 void impl_SetForeignVMContext(KNativePointer foreignVMContextRaw) {
426 #ifdef KOALA_FOREIGN_NAPI
427 if (foreignVMContextRaw == nullptr) {
428 g_foreignVMContext = nullptr;
429 } else {
430 auto foreignContext = (const ForeignVMContext*)foreignVMContextRaw;
431 g_foreignVMContext = foreignContext->vmContext;
432 }
433 #endif
434
435 /* supress unused private fields */
436 (void)foreignVMContextRaw;
437 }
KOALA_INTEROP_V1(SetForeignVMContext,KNativePointer)438 KOALA_INTEROP_V1(SetForeignVMContext, KNativePointer)
439
440 #define __QUOTE(x) #x
441 #define QUOTE(x) __QUOTE(x)
442
443 void impl_NativeLog(const KStringPtr& str) {
444 #ifdef KOALA_OHOS
445 LOGI("%{public}s: %{public}s", QUOTE(INTEROP_LIBRARY_NAME), str.c_str());
446 #else
447 fprintf(stdout, "%s: %s\n", QUOTE(INTEROP_LIBRARY_NAME), str.c_str());
448 fflush(stdout);
449 #endif
450 }
KOALA_INTEROP_V1(NativeLog,KStringPtr)451 KOALA_INTEROP_V1(NativeLog, KStringPtr)
452
453 void resolveDeferred(KVMDeferred* deferred, uint8_t* argsData, int32_t argsLength) {
454 #ifdef KOALA_NAPI
455 auto status = napi_call_threadsafe_function((napi_threadsafe_function)deferred->handler, deferred, napi_tsfn_nonblocking);
456 if (status != napi_ok) LOGE("cannot call thread-safe function; status=%d", status);
457 napi_release_threadsafe_function((napi_threadsafe_function)deferred->handler, napi_tsfn_release);
458 #endif
459 }
460
rejectDeferred(KVMDeferred * deferred,const char * message)461 void rejectDeferred(KVMDeferred* deferred, const char* message) {
462 #ifdef KOALA_NAPI
463 napi_release_threadsafe_function((napi_threadsafe_function)deferred->handler, napi_tsfn_release);
464 delete deferred;
465 #endif
466 }
467
468 #ifdef KOALA_NAPI
resolveDeferredImpl(napi_env env,napi_value js_callback,KVMDeferred * deferred,void * data)469 void resolveDeferredImpl(napi_env env, napi_value js_callback, KVMDeferred* deferred, void* data) {
470 napi_value undefined = nullptr;
471 napi_get_undefined(env, &undefined);
472 auto status = napi_resolve_deferred(env, (napi_deferred)deferred->context, undefined);
473 if (status != napi_ok) LOGE("cannot resolve deferred; status=%d", status);
474 delete deferred;
475 }
476 #endif
477
CreateDeferred(KVMContext vmContext,KVMObjectHandle * promiseHandle)478 KVMDeferred* CreateDeferred(KVMContext vmContext, KVMObjectHandle* promiseHandle) {
479 KVMDeferred* deferred = new KVMDeferred();
480 deferred->resolve = resolveDeferred;
481 deferred->reject = rejectDeferred;
482 #ifdef KOALA_NAPI
483 // TODO: move to interop!
484 napi_env env = (napi_env)vmContext;
485 napi_value promise;
486 napi_value resourceName;
487 napi_create_string_utf8(env, "Async", 5, &resourceName);
488 auto status = napi_create_promise(env, (napi_deferred*)&deferred->context, &promise);
489 if (status != napi_ok) LOGE("cannot make a promise; status=%d", status);
490 status = napi_create_threadsafe_function(env,
491 nullptr,
492 nullptr,
493 resourceName,
494 0,
495 1,
496 nullptr,
497 nullptr,
498 deferred,
499 (napi_threadsafe_function_call_js)resolveDeferredImpl,
500 (napi_threadsafe_function*)&deferred->handler);
501 if (status != napi_ok) LOGE("cannot make threadsafe function; status=%d", status);
502 *promiseHandle = (KVMObjectHandle)promise;
503 #endif
504 return deferred;
505 }
506
507 class KoalaWork {
508 protected:
509 InteropVMContext vmContext;
510 #ifdef KOALA_FOREIGN_NAPI
511 KVMContext foreignVMContext;
512 #endif
513 void* vmWork;
514 void* handle;
515 void (*execute)(void* handle);
516 void (*complete)(void* handle);
517 public:
518 KoalaWork(InteropVMContext vmContext,
519 InteropNativePointer handle,
520 void (*execute)(InteropNativePointer handle),
521 void (*complete)(InteropNativePointer handle));
522 void Queue();
523 void Execute();
524 void Cancel();
525 void Complete();
526 };
DoQueue(void * handle)527 static void DoQueue(void* handle) {
528 ((KoalaWork*)handle)->Queue();
529 }
DoCancel(void * handle)530 static void DoCancel(void* handle) {
531 ((KoalaWork*)handle)->Cancel();
532 }
533
koalaCreateWork(InteropVMContext vmContext,InteropNativePointer handle,void (* execute)(InteropNativePointer handle),void (* complete)(InteropNativePointer handle))534 InteropAsyncWork koalaCreateWork(
535 InteropVMContext vmContext,
536 InteropNativePointer handle,
537 void (*execute)(InteropNativePointer handle),
538 void (*complete)(InteropNativePointer handle)
539 ) {
540 return {
541 new KoalaWork(vmContext, handle, execute, complete),
542 DoQueue,
543 DoCancel,
544 };
545 }
546
GetAsyncWorker()547 const InteropAsyncWorker* GetAsyncWorker() {
548 static InteropAsyncWorker worker = {
549 koalaCreateWork
550 };
551 return &worker;
552 }
553
554 #if defined(KOALA_NAPI)
DoExecute(napi_env env,void * handle)555 static void DoExecute(napi_env env, void* handle) {
556 ((KoalaWork*)handle)->Execute();
557 }
DoComplete(napi_env env,napi_status status,void * handle)558 static void DoComplete(napi_env env, napi_status status, void* handle) {
559 ((KoalaWork*)handle)->Complete();
560 }
KoalaWork(InteropVMContext vmContext,InteropNativePointer handle,void (* execute)(InteropNativePointer handle),void (* complete)(InteropNativePointer handle))561 KoalaWork::KoalaWork(InteropVMContext vmContext,
562 InteropNativePointer handle,
563 void (*execute)(InteropNativePointer handle),
564 void (*complete)(InteropNativePointer handle)
565 ): vmContext(vmContext), handle(handle), execute(execute), complete(complete) {
566 napi_env env = (napi_env)vmContext;
567 napi_value resourceName = nullptr;
568 napi_create_string_utf8(env, "KoalaAsyncOperation", NAPI_AUTO_LENGTH, &resourceName);
569 napi_create_async_work(env, nullptr, resourceName, DoExecute, DoComplete, this, (napi_async_work*)&vmWork);
570 /* supress unused private fields */
571 (void)vmContext;
572 (void)vmWork;
573 }
Queue()574 void KoalaWork::Queue() {
575 napi_env env = (napi_env)vmContext;
576 napi_queue_async_work(env, (napi_async_work)vmWork);
577 }
Execute()578 void KoalaWork::Execute() {
579 execute(handle);
580 }
Cancel()581 void KoalaWork::Cancel() {
582 napi_env env = (napi_env)vmContext;
583 napi_cancel_async_work(env, (napi_async_work)vmWork);
584 }
Complete()585 void KoalaWork::Complete() {
586 complete(handle);
587 delete this;
588 }
589 #else
590 #ifdef KOALA_FOREIGN_NAPI
DoExecute(napi_env env,void * handle)591 static void DoExecute(napi_env env, void* handle) {
592 ((KoalaWork*)handle)->Execute();
593 }
DoComplete(napi_env env,napi_status status,void * handle)594 static void DoComplete(napi_env env, napi_status status, void* handle) {
595 ((KoalaWork*)handle)->Complete();
596 }
597 #endif
KoalaWork(InteropVMContext vmContext,InteropNativePointer handle,void (* execute)(InteropNativePointer handle),void (* complete)(InteropNativePointer handle))598 KoalaWork::KoalaWork(InteropVMContext vmContext,
599 InteropNativePointer handle,
600 void (*execute)(InteropNativePointer handle),
601 void (*complete)(InteropNativePointer handle)
602 ): vmContext(vmContext), handle(handle), execute(execute), complete(complete) {
603 #ifdef KOALA_FOREIGN_NAPI
604 if (g_foreignVMContext == nullptr)
605 INTEROP_FATAL("Can not launch async work while foreign VM context is not available. Please ensure you have called SetForeignVMContext");
606 foreignVMContext = g_foreignVMContext;
607 napi_env env = (napi_env)foreignVMContext;
608 napi_value resourceName = nullptr;
609 napi_create_string_utf8(env, "KoalaAsyncOperation", NAPI_AUTO_LENGTH, &resourceName);
610 napi_create_async_work(env, nullptr, resourceName, DoExecute, DoComplete, this, (napi_async_work*)&vmWork);
611 #endif
612 /* supress unused private fields */
613 (void)vmContext;
614 (void)vmWork;
615 }
Queue()616 void KoalaWork::Queue() {
617 #ifdef KOALA_FOREIGN_NAPI
618 napi_env env = (napi_env)foreignVMContext;
619 napi_queue_async_work(env, (napi_async_work)vmWork);
620 #else
621 Execute();
622 Complete();
623 #endif
624 }
Execute()625 void KoalaWork::Execute() {
626 execute(handle);
627 }
Cancel()628 void KoalaWork::Cancel() {
629 #ifdef KOALA_FOREIGN_NAPI
630 napi_env env = (napi_env)foreignVMContext;
631 napi_cancel_async_work(env, (napi_async_work)vmWork);
632 #else
633 INTEROP_FATAL("Cancelling async work is disabled for any VM except of Node");
634 #endif
635 }
Complete()636 void KoalaWork::Complete() {
637 complete(handle);
638 delete this;
639 }
640 #endif
641
642
643 #if defined(KOALA_ETS_NAPI) || defined(KOALA_ANI)
impl_Utf8ToString(KVMContext vmContext,KNativePointer data,KInt offset,KInt length)644 KStringPtr impl_Utf8ToString(KVMContext vmContext, KNativePointer data, KInt offset, KInt length) {
645 KStringPtr result((const char*)data + offset, length, false);
646 return result;
647 }
KOALA_INTEROP_CTX_3(Utf8ToString,KStringPtr,KNativePointer,KInt,KInt)648 KOALA_INTEROP_CTX_3(Utf8ToString, KStringPtr, KNativePointer, KInt, KInt)
649 #elif defined(KOALA_NAPI) || defined(KOALA_JNI) || defined(KOALA_CJ)
650 // Allocate, so CTX versions.
651 KStringPtr impl_Utf8ToString(KVMContext vmContext, KByte* data, KInt offset, KInt length) {
652 KStringPtr result((const char*)(data + offset), length, false);
653 return result;
654 }
655 KOALA_INTEROP_CTX_3(Utf8ToString, KStringPtr, KByte*, KInt, KInt)
656 #endif
657
658 #if defined(KOALA_NAPI) || defined(KOALA_JNI) || defined(KOALA_CJ) || defined(KOALA_ETS_NAPI) || defined(KOALA_ANI)
659 KStringPtr impl_StdStringToString(KVMContext vmContext, KNativePointer stringPtr) {
660 std::string* string = reinterpret_cast<std::string*>(stringPtr);
661 KStringPtr result(string->c_str(), string->size(), false);
662 return result;
663 }
KOALA_INTEROP_CTX_1(StdStringToString,KStringPtr,KNativePointer)664 KOALA_INTEROP_CTX_1(StdStringToString, KStringPtr, KNativePointer)
665
666 KInteropReturnBuffer impl_RawReturnData(KVMContext vmContext, KInt v1, KInt v2) {
667 void* data = new int8_t[v1];
668 memset(data, v2, v1);
669 KInteropReturnBuffer buffer = { v1, data, [](KNativePointer ptr, KInt) { delete[] (int8_t*)ptr; }};
670 return buffer;
671 }
KOALA_INTEROP_CTX_2(RawReturnData,KInteropReturnBuffer,KInt,KInt)672 KOALA_INTEROP_CTX_2(RawReturnData, KInteropReturnBuffer, KInt, KInt)
673
674 KInteropNumber impl_IncrementNumber(KInteropNumber number) {
675 if (number.tag == 102)
676 number.i32++;
677 else
678 number.f32 += 1.f;
679 return number;
680 }
681 KOALA_INTEROP_1(IncrementNumber, KInteropNumber, KInteropNumber)
682
683 #endif
684