1 /*
2 * Copyright (c) 2024 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
16 #include <unordered_map>
17
18 #include <node_api.h>
19
20 #include "ark_interop_internal.h"
21 #include "ark_interop_external.h"
22 #include "ark_interop_log.h"
23 #include "cj_envsetup.h"
24
25 struct ARKTS_ModuleCallbacks {
26 ARKTS_Value (*exportModule)(ARKTS_Env env, const char* dllName, ARKTS_Value exports) = nullptr;
27 bool (*hasModuleHandle)(const char* dllName) = nullptr;
28 void (*throwJSError)(ARKTS_Env env, ARKTS_Value) = nullptr;
29 void (*throwNativeError)(const char*) = nullptr;
30 void (*deleteArrayBufferRawData)(void* buffer, int64_t lambdaId) = nullptr;
31 void (*deleteExternal)(int64_t id, ARKTS_Env env) = nullptr;
32 ARKTS_Value (*invokerLambda)(ARKTS_CallInfo, int64_t lambdaId) = nullptr;
33 void (*deleteLambda)(ARKTS_Env env, int64_t lambdaId) = nullptr;
34 void (*invokeAsyncLambda)(ARKTS_Env env, int64_t lambdaId) = nullptr;
35 void (*deleteJSContext)(ARKTS_Env env) = nullptr;
36 };
37
38 namespace {
39 // each native register only be available during module loading
40 ARKTS_ModuleCallbacks* g_cjModuleCallbacks = nullptr;
41 }
42
ARKTSInner_ThrowJSErrorToCJ(ARKTS_Env env,ARKTS_Value error)43 bool ARKTSInner_ThrowJSErrorToCJ(ARKTS_Env env, ARKTS_Value error)
44 {
45 if (!g_cjModuleCallbacks) {
46 LOGE("napi module depends on another napi module is forbidden");
47 return false;
48 }
49 g_cjModuleCallbacks->throwJSError(env, error);
50 return true;
51 }
52
ARKTSInner_ThrowNativeErrorToCJ(const char * error)53 bool ARKTSInner_ThrowNativeErrorToCJ(const char* error)
54 {
55 if (!g_cjModuleCallbacks) {
56 LOGE("napi module depends on another napi module is forbidden");
57 return false;
58 }
59 g_cjModuleCallbacks->throwNativeError(error);
60 return true;
61 }
62
ARKTS_SetCJModuleCallback(ARKTS_ModuleCallbacks * callback)63 void ARKTS_SetCJModuleCallback(ARKTS_ModuleCallbacks* callback)
64 {
65 if (!callback) {
66 LOGE("register empty module callback is senseless");
67 return;
68 }
69 if (g_cjModuleCallbacks) {
70 LOGE("should never happen");
71 return;
72 }
73 g_cjModuleCallbacks = new ARKTS_ModuleCallbacks(*callback);
74 }
75
76 // export but only for internal
ARKTS_LoadModule(ARKTS_Env env,const char * dllName)77 panda::JSValueRef* ARKTS_LoadModule(ARKTS_Env env, const char* dllName)
78 {
79 LOGD("ARKTS_LoadCJModule start: %{public}s", dllName);
80 // HandleScope
81 auto scope = ARKTS_OpenScope(env);
82
83 auto undefined = ARKTS_CreateUndefined();
84 auto runtime = OHOS::CJEnv::LoadInstance();
85 if (!runtime) {
86 return ARKTSInner_Escape(env, scope, undefined);
87 }
88
89 if (!runtime->startRuntime()) {
90 LOGE("start cj runtime failed");
91 return ARKTSInner_Escape(env, scope, undefined);
92 }
93 if (!g_cjModuleCallbacks || !g_cjModuleCallbacks->hasModuleHandle(dllName)) {
94 if (!runtime->loadCJModule(dllName)) {
95 LOGE("load cj library failed, try load as native");
96 return ARKTSInner_Escape(env, scope, undefined);
97 }
98 }
99
100 if (!g_cjModuleCallbacks) {
101 LOGE("cj module must dependent on ohos_ark_interop");
102 return ARKTSInner_Escape(env, scope, undefined);
103 }
104
105 auto exports = ARKTS_CreateObject(env);
106
107 exports = g_cjModuleCallbacks->exportModule(env, dllName, exports);
108
109 return ARKTSInner_Escape(env, scope, exports);
110 }
111
ARKTS_LoadModuleByNapiEnv(void * env,const char * dllName)112 EXPORT panda::JSValueRef* ARKTS_LoadModuleByNapiEnv(void* env, const char* dllName)
113 {
114 if (!env) {
115 LOGE("env is null");
116 return nullptr;
117 }
118 if (!dllName) {
119 LOGE("dllName is null");
120 return nullptr;
121 }
122 auto engine = reinterpret_cast<NativeEngine*>(env);
123 auto vm = const_cast<EcmaVM*>(engine->GetEcmaVm());
124 if (!vm) {
125 LOGE("vm is null");
126 return nullptr;
127 }
128 auto loop = engine->GetUVLoop();
129 if (!loop) {
130 LOGE("uvloop is null");
131 return nullptr;
132 }
133 if (!ARKTSInner_InitLoop(reinterpret_cast<napi_env>(env), reinterpret_cast<ARKTS_Env>(vm), loop)) {
134 LOGE("init loop failed");
135 return nullptr;
136 }
137
138 return ARKTS_LoadModule(reinterpret_cast<ARKTS_Env>(vm), dllName);
139 }
140
ARKTSInner_CJArrayBufferDeleter(void *,void * buffer,void * lambdaId)141 void ARKTSInner_CJArrayBufferDeleter(void*, void* buffer, void* lambdaId)
142 {
143 if (!g_cjModuleCallbacks) {
144 LOGE("native ark-interop library has no registered module");
145 return;
146 }
147 g_cjModuleCallbacks->deleteArrayBufferRawData(buffer,
148 reinterpret_cast<int64_t>(lambdaId));
149 }
150
ARKTSInner_CJExternalDeleter(void *,void * data,void * env)151 void ARKTSInner_CJExternalDeleter(void*, void* data, void* env)
152 {
153 if (!g_cjModuleCallbacks) {
154 LOGE("native ark-interop library has no registered module");
155 return;
156 }
157 g_cjModuleCallbacks->deleteExternal(
158 reinterpret_cast<int64_t>(data),
159 reinterpret_cast<ARKTS_Env>(env)
160 );
161 }
162
ARKTSInner_CJLambdaInvoker(ARKTS_CallInfo callInfo,int64_t lambdaId)163 ARKTS_Result ARKTSInner_CJLambdaInvoker(ARKTS_CallInfo callInfo, int64_t lambdaId)
164 {
165 auto env = ARKTS_GetCallEnv(callInfo);
166 auto scope = ARKTS_OpenScope(env);
167
168 if (!g_cjModuleCallbacks) {
169 LOGE("native ark-interop library has no registered module");
170 return ARKTS_Return(env, scope, ARKTS_CreateUndefined());
171 }
172
173 auto result = g_cjModuleCallbacks->invokerLambda(callInfo, lambdaId);
174 return ARKTS_Return(env, scope, result);
175 }
176
ARKTSInner_CJLambdaDeleter(ARKTS_Env env,int64_t lambdaId)177 void ARKTSInner_CJLambdaDeleter(ARKTS_Env env, int64_t lambdaId)
178 {
179 if (!g_cjModuleCallbacks) {
180 LOGE("native ark-interop library has no registered module");
181 return;
182 }
183
184 g_cjModuleCallbacks->deleteLambda(env, lambdaId);
185 }
186
ARKTSInner_CJAsyncCallback(ARKTS_Env env,void * data)187 void ARKTSInner_CJAsyncCallback(ARKTS_Env env, void* data)
188 {
189 if (!g_cjModuleCallbacks) {
190 LOGE("native ark-interop library has no registered module");
191 return;
192 }
193
194 g_cjModuleCallbacks->invokeAsyncLambda(env, reinterpret_cast<int64_t>(data));
195 }
196
ARKTS_DisposeJSContext(ARKTS_Env env)197 void ARKTS_DisposeJSContext(ARKTS_Env env)
198 {
199 if (!g_cjModuleCallbacks) {
200 LOGE("native ark-interop library has no registered module");
201 return;
202 }
203 g_cjModuleCallbacks->deleteJSContext(env);
204 }
205