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