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