• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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