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 <native_engine/native_engine.h>
17
18 #include "node_api.h"
19 #include "ark_interop_log.h"
20 #include "cj_envsetup.h"
21
22 #include <mutex>
23 #include <map>
24
25 #ifdef USE_LIBS_ARM64
26 #define LIBS_NAME "arm64"
27 #elif defined(USE_LIBS_ARM)
28 #define LIBS_NAME "arm"
29 #elif defined(USE_LIBS_x86_64)
30 #define LIBS_NAME "x86_64"
31 #else
32 #error current platform not supported
33 #endif
34
35 static std::mutex g_mutex;
36 static bool g_isInited = false;
37 static std::map<napi_env, std::map<std::string, napi_ref>> envMap = {};
38
ParseLoadParams(napi_env env,napi_callback_info info,char * nameBuf,size_t & size)39 static bool ParseLoadParams(napi_env env, napi_callback_info info, char* nameBuf, size_t& size)
40 {
41 napi_value libNameValue;
42 size_t argCount = 1;
43 auto status = napi_get_cb_info(env, info, &argCount, &libNameValue, nullptr, nullptr);
44 if (status != napi_ok) {
45 napi_throw_error(env, "napi call fail", "napi_get_cb_info failed");
46 return false;
47 }
48
49 if (argCount != 1) {
50 napi_throw_error(env, "argument invalid", "need one argument");
51 return false;
52 }
53
54 napi_valuetype valuetype;
55 status = napi_typeof(env, libNameValue, &valuetype);
56 if (status != napi_ok) {
57 napi_throw_error(env, "napi call fail", "napi_typeof failed");
58 return false;
59 }
60 if (valuetype != napi_string) {
61 napi_throw_error(env, "argument invalid", "need a string");
62 return false;
63 }
64
65 status = napi_get_value_string_utf8(env, libNameValue, nameBuf, size, &size);
66 if (status != napi_ok) {
67 napi_throw_error(env, "argument invalid", "napi_get_value_string_utf8 not ok");
68 return false;
69 }
70 return true;
71 }
72
LoadArkCJModule(void * handle,napi_env env,const char * libName,napi_value * result)73 static bool LoadArkCJModule(void *handle, napi_env env, const char* libName, napi_value* result)
74 {
75 auto engine = reinterpret_cast<NativeEngine*>(env);
76 std::lock_guard<std::mutex> lock(g_mutex);
77 std::string str(libName);
78 if (envMap.find(env) != envMap.end() && envMap[env].find(str) != envMap[env].end()) {
79 napi_value callback = nullptr;
80 napi_get_reference_value(env, envMap[env][str], &callback);
81 *result = callback;
82 return true;
83 }
84 auto runtime = OHOS::CJEnv::LoadInstance();
85 if (handle == nullptr) {
86 LOGE("get handle failed");
87 return false;
88 }
89 if (runtime == nullptr) {
90 LOGE("load Instance failed");
91 return false;
92 }
93 if (auto symbol = runtime->getSymbol(handle, "ARKTS_LoadModuleByNapiEnv")) {
94 auto loader = reinterpret_cast<napi_value(*)(napi_env, const char*)>(symbol);
95 *result = loader(env, libName);
96 } else if (auto symbol = runtime->getSymbol(handle, "ARKTS_LoadModule")) {
97 auto loader = reinterpret_cast<napi_value(*)(EcmaVM*, const char*)>(symbol);
98 auto vm = const_cast<EcmaVM*>(engine->GetEcmaVm());
99 *result = loader(vm, libName);
100 } else {
101 return false;
102 }
103 napi_ref callbackRef = nullptr;
104 napi_create_reference(env, *result, 1, &callbackRef);
105 envMap[env][str] = callbackRef;
106
107 return true;
108 }
109
GetArkInteropLibHandle(napi_env env)110 static void *GetArkInteropLibHandle(napi_env env)
111 {
112 const char* targetName;
113 #ifdef __OHOS__
114 targetName = "libark_interop.z.so";
115 #elif defined(__WINDOWS__)
116 targetName = "libark_interop.dll";
117 #elif defined(__LINUX__)
118 targetName = "libark_interop.so";
119 #endif
120 auto runtime = OHOS::CJEnv::LoadInstance();
121 if (!runtime) {
122 LOGE("load Instance failed");
123 return nullptr;
124 }
125 auto handle = runtime->loadLibrary(0, targetName);
126 if (!handle) {
127 LOGE("open '%{public}s' failed", targetName);
128 return nullptr;
129 }
130 return handle;
131 }
132
RegisterStackInfoCallbacks(void * arkInteropLibHandle)133 static bool RegisterStackInfoCallbacks(void *arkInteropLibHandle)
134 {
135 auto runtime = OHOS::CJEnv::LoadInstance();
136 if (!runtime) {
137 LOGE("load Instance failed");
138 return false;
139 }
140 auto updateStackInfoFunc = runtime->getSymbol(arkInteropLibHandle, "ARKTS_UpdateStackInfo");
141 if (updateStackInfoFunc == nullptr) {
142 LOGE("load symbol ARKTS_UpdateStackInfo failed");
143 return false;
144 }
145 runtime->registerStackInfoCallbacks(
146 reinterpret_cast<void(*)(unsigned long long, void *, unsigned int)>(updateStackInfoFunc)
147 );
148 return true;
149 }
150
LoadCJModule(napi_env env,napi_callback_info info)151 static napi_value LoadCJModule(napi_env env, napi_callback_info info)
152 {
153 napi_value result;
154 if (napi_get_undefined(env, &result) != napi_ok) {
155 return result;
156 }
157 constexpr size_t BUF_SIZE = 256;
158 char nameBuf[BUF_SIZE];
159 size_t realSize = BUF_SIZE;
160 void *arkInteropHandle = nullptr;
161 if (!ParseLoadParams(env, info, nameBuf, realSize)) {
162 return result;
163 }
164 if (!g_isInited) {
165 std::lock_guard<std::mutex> lock(g_mutex);
166 if (!g_isInited) {
167 auto runtime = OHOS::CJEnv::LoadInstance();
168 if (!runtime) {
169 LOGE("load Instance failed");
170 return result;
171 }
172 runtime->initCJChipSDKNS("/system/lib64/chipset-pub-sdk");
173 runtime->initCJAppNS("/data/storage/el1/bundle/libs/" LIBS_NAME);
174 runtime->initCJSDKNS("/data/storage/el1/bundle/libs/" LIBS_NAME "/ohos:"
175 "/data/storage/el1/bundle/libs/" LIBS_NAME "/runtime");
176 runtime->initCJSysNS("/system/lib64:/system/lib64/platformsdk:/system/lib64/module:/system/lib64/ndk");
177 if (!runtime->startRuntime()) {
178 LOGE("start cjruntime failed");
179 return result;
180 }
181 arkInteropHandle = GetArkInteropLibHandle(env);
182 RegisterStackInfoCallbacks(arkInteropHandle);
183 auto engine = reinterpret_cast<NativeEngine*>(env);
184 auto vm = const_cast<EcmaVM*>(engine->GetEcmaVm());
185 auto vmAddr = static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(vm));
186 runtime->registerArkVMInRuntime(vmAddr);
187 if (!runtime->startUIScheduler()) {
188 LOGE("start cj ui context failed");
189 return result;
190 }
191 g_isInited = true;
192 }
193 }
194 if (!arkInteropHandle) {
195 arkInteropHandle = GetArkInteropLibHandle(env);
196 }
197 LoadArkCJModule(arkInteropHandle, env, nameBuf, &result);
198 return result;
199 }
200
ExportLoadCJModule(napi_env env,napi_value exports)201 static napi_value ExportLoadCJModule(napi_env env, napi_value exports)
202 {
203 static napi_property_descriptor desc[] = {
204 {"requireCJLib", nullptr, LoadCJModule, nullptr, nullptr, nullptr, napi_default, nullptr}
205 };
206 napi_define_properties(env, exports, sizeof(desc) / sizeof(napi_property_descriptor), desc);
207 return exports;
208 }
209
LoadCJModuleRegister()210 extern "C" __attribute__((constructor)) void LoadCJModuleRegister()
211 {
212 static napi_module callbackModule = {
213 .nm_version = 1,
214 .nm_flags = 0,
215 .nm_filename = nullptr,
216 .nm_register_func = ExportLoadCJModule,
217 .nm_modname = "cjLibLoader",
218 .nm_priv = nullptr,
219 .reserved = { 0 },
220 };
221 napi_module_register(&callbackModule);
222 }
223