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 LOGE("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
ARKTSInner_CJArrayBufferDeleter(void *,void * buffer,void * lambdaId)110 void ARKTSInner_CJArrayBufferDeleter(void*, void* buffer, void* lambdaId)
111 {
112 if (!g_cjModuleCallbacks) {
113 LOGE("native ark-interop library has no registered module");
114 return;
115 }
116 g_cjModuleCallbacks->deleteArrayBufferRawData(buffer,
117 reinterpret_cast<int64_t>(lambdaId));
118 }
119
ARKTSInner_CJExternalDeleter(void *,void * data,void * env)120 void ARKTSInner_CJExternalDeleter(void*, void* data, void* env)
121 {
122 if (!g_cjModuleCallbacks) {
123 LOGE("native ark-interop library has no registered module");
124 return;
125 }
126 g_cjModuleCallbacks->deleteExternal(
127 reinterpret_cast<int64_t>(data),
128 reinterpret_cast<ARKTS_Env>(env)
129 );
130 }
131
ARKTSInner_CJLambdaInvoker(ARKTS_CallInfo callInfo,int64_t lambdaId)132 ARKTS_Result ARKTSInner_CJLambdaInvoker(ARKTS_CallInfo callInfo, int64_t lambdaId)
133 {
134 auto env = ARKTS_GetCallEnv(callInfo);
135 auto scope = ARKTS_OpenScope(env);
136
137 if (!g_cjModuleCallbacks) {
138 LOGE("native ark-interop library has no registered module");
139 return ARKTS_Return(env, scope, ARKTS_CreateUndefined());
140 }
141
142 auto result = g_cjModuleCallbacks->invokerLambda(callInfo, lambdaId);
143 return ARKTS_Return(env, scope, result);
144 }
145
ARKTSInner_CJLambdaDeleter(ARKTS_Env env,int64_t lambdaId)146 void ARKTSInner_CJLambdaDeleter(ARKTS_Env env, int64_t lambdaId)
147 {
148 if (!g_cjModuleCallbacks) {
149 LOGE("native ark-interop library has no registered module");
150 return;
151 }
152
153 g_cjModuleCallbacks->deleteLambda(env, lambdaId);
154 }
155
ARKTSInner_CJAsyncCallback(ARKTS_Env env,void * data)156 void ARKTSInner_CJAsyncCallback(ARKTS_Env env, void* data)
157 {
158 if (!g_cjModuleCallbacks) {
159 LOGE("native ark-interop library has no registered module");
160 return;
161 }
162
163 g_cjModuleCallbacks->invokeAsyncLambda(env, reinterpret_cast<int64_t>(data));
164 }
165
ARKTS_DisposeJSContext(ARKTS_Env env)166 void ARKTS_DisposeJSContext(ARKTS_Env env)
167 {
168 if (!g_cjModuleCallbacks) {
169 LOGE("native ark-interop library has no registered module");
170 return;
171 }
172 g_cjModuleCallbacks->deleteJSContext(env);
173 }
174