1 /*
2 * Copyright (c) 2025 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 "napi/native_api.h"
17 #include "hilog/log.h"
18 #include "ark_runtime/jsvm.h"
19
20 // [Start runtime_task]
21 #include <map>
22 #include <mutex>
23 #include <deque>
24 using namespace std;
25 // 定义map管理每个独立vm环境
26 static map<int, JSVM_VM *> g_vmMap;
27 static map<int, JSVM_Env *> g_envMap;
28 static map<int, JSVM_CallbackStruct *> g_callBackStructMap;
29 static uint32_t g_envtagNumber = 0;
30 static std::mutex g_envMapLock;
31 // [StartExclude runtime_task]
32
33 #define LOG_DOMAIN 0x3200
34 #define LOG_TAG "APP"
35
36 static int g_aa = 0;
37
38 #define CHECK_RET(theCall) \
39 do { \
40 JSVM_Status cond = theCall; \
41 if ((cond) != JSVM_OK) { \
42 const JSVM_ExtendedErrorInfo *info; \
43 OH_JSVM_GetLastErrorInfo(env, &info); \
44 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \
45 __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \
46 return -1; \
47 } \
48 } while (0)
49
50 #define CHECK(theCall) \
51 do { \
52 JSVM_Status cond = theCall; \
53 if ((cond) != JSVM_OK) { \
54 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d", __FILE__, __LINE__, \
55 cond); \
56 return -1; \
57 } \
58 } while (0)
59
60 // 用于调用theCall并检查其返回值是否为JSVM_OK。
61 // 如果不是,则调用OH_JSVM_GetLastErrorInfo处理错误并返回retVal。
62 #define JSVM_CALL_BASE(env, theCall, retVal) \
63 do { \
64 JSVM_Status cond = theCall; \
65 if (cond != JSVM_OK) { \
66 const JSVM_ExtendedErrorInfo *info; \
67 OH_JSVM_GetLastErrorInfo(env, &info); \
68 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \
69 __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \
70 return retVal; \
71 } \
72 } while (0)
73
74 // JSVM_CALL_BASE的简化版本,返回nullptr
75 #define JSVM_CALL(theCall) JSVM_CALL_BASE(env, theCall, nullptr)
76 // [EndExclude runtime_task]
77
78 #define CHECK_COND(cond) \
79 do { \
80 if (!(cond)) { \
81 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = false", __FILE__, __LINE__); \
82 return -1; \
83 } \
84 } while (0)
85
86 class Task {
87 public:
88 virtual ~Task() = default;
89 virtual void Run() = 0;
90 };
91 static map<int, deque<Task *>> g_taskQueueMap;
92
93 // 自定义Consoleinfo方法
Consoleinfo(JSVM_Env env,JSVM_CallbackInfo info)94 static JSVM_Value Consoleinfo(JSVM_Env env, JSVM_CallbackInfo info)
95 {
96 size_t argc = 1;
97 JSVM_Value args[1];
98 #define MAX_LOG_LENGTH 255
99 char log[MAX_LOG_LENGTH + 1] = "";
100 size_t logLength = 0;
101 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
102
103 JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, args[0], log, MAX_LOG_LENGTH, &logLength));
104 log[MAX_LOG_LENGTH] = 0;
105 OH_LOG_INFO(LOG_APP, "JSVM API TEST: %{public}s", log);
106 return nullptr;
107 }
108
109 // 自定义创建Promise方法用以在JS代码中创建Promise
CreatePromise(JSVM_Env env,JSVM_CallbackInfo info)110 static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info)
111 {
112 OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise start");
113 int envID = -1;
114 // 通过当前env获取envID
115 for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
116 if (*it->second == env) {
117 envID = it->first;
118 break;
119 }
120 }
121 if (envID == -1) {
122 OH_LOG_ERROR(LOG_APP, "JSVM API TEST: CreatePromise envID failed");
123 return nullptr;
124 }
125 JSVM_Value promise;
126 JSVM_Deferred deferred;
127 JSVM_CALL(OH_JSVM_CreatePromise(env, &deferred, &promise));
128 // 设计ReadTask类用以将promise对象的deferred加入执行队列
129 class ReadTask : public Task {
130 public:
131 ReadTask(JSVM_Env env, JSVM_Deferred deferred, int envNum) : env_(env), envID_(envNum), deferred_(deferred) {}
132 void Run() override
133 {
134 // string str = "TEST RUN OH_JSVM_ResolveDeferred";
135 int envID = 0;
136 for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
137 if (*it->second == env_) {
138 envID = it->first;
139 break;
140 }
141 }
142 OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise %{public}d", envID);
143 JSVM_Value result;
144 if (OH_JSVM_CreateInt32(env_, envID, &result) != JSVM_OK) {
145 return;
146 }
147 if (OH_JSVM_ResolveDeferred(env_, deferred_, result) != JSVM_OK) {
148 return;
149 }
150 }
151
152 private:
153 JSVM_Env env_;
154 int envID_;
155 JSVM_Deferred deferred_;
156 };
157 g_taskQueueMap[envID].push_back(new ReadTask(env, deferred, envID));
158 OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise end");
159 return promise;
160 }
161
162 // 自定义Add方法
Add(JSVM_Env env,JSVM_CallbackInfo info)163 static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info)
164 {
165 size_t argc = 2;
166 JSVM_Value args[2];
167 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
168 double num1 = 0;
169 double num2 = 0;
170 JSVM_CALL(OH_JSVM_GetValueDouble(env, args[0], &num1));
171 JSVM_CALL(OH_JSVM_GetValueDouble(env, args[1], &num2));
172 JSVM_Value sum = nullptr;
173 JSVM_CALL(OH_JSVM_CreateDouble(env, num1 + num2, &sum));
174 return sum;
175 }
176
177 // 自定义AssertEqual方法
AssertEqual(JSVM_Env env,JSVM_CallbackInfo info)178 static JSVM_Value AssertEqual(JSVM_Env env, JSVM_CallbackInfo info)
179 {
180 size_t argc = 2;
181 JSVM_Value args[2];
182 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
183
184 bool isStrictEquals = false;
185 JSVM_CALL(OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals));
186
187 if (isStrictEquals) {
188 OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS");
189 } else {
190 OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: FAILED");
191 }
192 return nullptr;
193 }
194
fromOHStringValue(JSVM_Env & env,JSVM_Value & value,std::string & result)195 static int fromOHStringValue(JSVM_Env &env, JSVM_Value &value, std::string &result)
196 {
197 size_t size = 0;
198 CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size));
199 char resultStr[size + 1];
200 CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size));
201 result = resultStr;
202 return 0;
203 }
204
205 // 提供创建JSVM运行环境的对外接口并返回对应唯一ID
CreateJsCore(uint32_t * result)206 static int CreateJsCore(uint32_t *result)
207 {
208 OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore START");
209 g_taskQueueMap[g_envtagNumber] = deque<Task*> {};
210
211 if (g_aa == 0) {
212 JSVM_InitOptions init_options;
213 memset(&init_options, 0, sizeof(init_options));
214 CHECK(OH_JSVM_Init(&init_options));
215 g_aa++;
216 }
217 std::lock_guard<std::mutex> lock_guard(g_envMapLock);
218
219 // 虚拟机实例
220 g_vmMap[g_envtagNumber] = new JSVM_VM;
221 JSVM_CreateVMOptions options;
222 JSVM_VMScope vmScope;
223 memset(&options, 0, sizeof(options));
224 CHECK(OH_JSVM_CreateVM(&options, g_vmMap[g_envtagNumber]));
225 CHECK(OH_JSVM_OpenVMScope(*g_vmMap[g_envtagNumber], &vmScope));
226
227 // 新环境
228 g_envMap[g_envtagNumber] = new JSVM_Env;
229 g_callBackStructMap[g_envtagNumber] = new JSVM_CallbackStruct[4];
230
231 // 注册用户提供的本地函数的回调函数指针和数据,通过JSVM-API暴露给js
232 constexpr int kLoopCount = 4;
233 for (int i = 0; i < kLoopCount; i++) {
234 g_callBackStructMap[g_envtagNumber][i].data = nullptr;
235 }
236 const int consoleinfoIndex = 0;
237 const int addIndex = 1;
238 const int assertEqualIndex = 2;
239 const int createPromiseIndex = 3;
240 g_callBackStructMap[g_envtagNumber][consoleinfoIndex].callback = Consoleinfo;
241 g_callBackStructMap[g_envtagNumber][addIndex].callback = Add;
242 g_callBackStructMap[g_envtagNumber][assertEqualIndex].callback = AssertEqual;
243 g_callBackStructMap[g_envtagNumber][createPromiseIndex].callback = CreatePromise;
244 JSVM_PropertyDescriptor descriptors[] = {
245 { "consoleinfo", NULL, &g_callBackStructMap[g_envtagNumber][consoleinfoIndex], NULL, NULL, NULL, JSVM_DEFAULT },
246 { "add", NULL, &g_callBackStructMap[g_envtagNumber][addIndex], NULL, NULL, NULL, JSVM_DEFAULT },
247 { "assertEqual", NULL, &g_callBackStructMap[g_envtagNumber][assertEqualIndex], NULL, NULL, NULL, JSVM_DEFAULT },
248 { "createPromise", NULL, &g_callBackStructMap[g_envtagNumber][createPromiseIndex], NULL, NULL, NULL,
249 JSVM_DEFAULT },
250 };
251 CHECK(OH_JSVM_CreateEnv(
252 *g_vmMap[g_envtagNumber], sizeof(descriptors) / sizeof(descriptors[0]), descriptors, g_envMap[g_envtagNumber]));
253 CHECK(OH_JSVM_CloseVMScope(*g_vmMap[g_envtagNumber], vmScope));
254
255 OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore END");
256 *result = g_envtagNumber;
257 g_envtagNumber++;
258 return 0;
259 }
260
261 // 对外提供释放JSVM环境接口,通过envId释放对应环境
ReleaseJsCore(uint32_t coreEnvId)262 static int ReleaseJsCore(uint32_t coreEnvId)
263 {
264 OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore START");
265 CHECK_COND(g_envMap.count(coreEnvId) != 0 && g_envMap[coreEnvId] != nullptr);
266
267 std::lock_guard<std::mutex> lock_guard(g_envMapLock);
268
269 CHECK(OH_JSVM_DestroyEnv(*g_envMap[coreEnvId]));
270 g_envMap[coreEnvId] = nullptr;
271 g_envMap.erase(coreEnvId);
272 CHECK(OH_JSVM_DestroyVM(*g_vmMap[coreEnvId]));
273 g_vmMap[coreEnvId] = nullptr;
274 g_vmMap.erase(coreEnvId);
275 delete[] g_callBackStructMap[coreEnvId];
276 g_callBackStructMap[coreEnvId] = nullptr;
277 g_callBackStructMap.erase(coreEnvId);
278 g_taskQueueMap.erase(coreEnvId);
279
280 OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore END");
281 return 0;
282 }
283
284 static std::mutex g_mutexLock;
285 // 对外提供执行JS代码接口,通过coreID在对应的JSVN环境中执行JS代码
EvaluateJS(uint32_t envId,const char * source,std::string & res)286 static int EvaluateJS(uint32_t envId, const char *source, std::string &res)
287 {
288 OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS START");
289
290 CHECK_COND(g_envMap.count(envId) != 0 && g_envMap[envId] != nullptr);
291
292 JSVM_Env env = *g_envMap[envId];
293 JSVM_VM vm = *g_vmMap[envId];
294 JSVM_VMScope vmScope;
295 JSVM_EnvScope envScope;
296 JSVM_HandleScope handleScope;
297 JSVM_Value result;
298
299 std::lock_guard<std::mutex> lock_guard(g_mutexLock);
300 {
301 // 创建JSVM环境
302 CHECK_RET(OH_JSVM_OpenVMScope(vm, &vmScope));
303 CHECK_RET(OH_JSVM_OpenEnvScope(*g_envMap[envId], &envScope));
304 CHECK_RET(OH_JSVM_OpenHandleScope(*g_envMap[envId], &handleScope));
305
306 // 通过script调用测试函数
307 JSVM_Script script;
308 JSVM_Value jsSrc;
309 CHECK_RET(OH_JSVM_CreateStringUtf8(env, source, JSVM_AUTO_LENGTH, &jsSrc));
310 CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
311 CHECK_RET(OH_JSVM_RunScript(env, script, &result));
312
313 JSVM_ValueType type;
314 CHECK_RET(OH_JSVM_Typeof(env, result, &type));
315 OH_LOG_INFO(LOG_APP, "JSVM API TEST type: %{public}d", type);
316 // Execute tasks in the current env event queue
317 while (!g_taskQueueMap[envId].empty()) {
318 auto task = g_taskQueueMap[envId].front();
319 g_taskQueueMap[envId].pop_front();
320 task->Run();
321 delete task;
322 }
323
324 if (type == JSVM_STRING) {
325 CHECK_COND(fromOHStringValue(env, result, res) != -1);
326 } else if (type == JSVM_BOOLEAN) {
327 bool ret = false;
328 CHECK_RET(OH_JSVM_GetValueBool(env, result, &ret));
329 ret ? res = "true" : res = "false";
330 } else if (type == JSVM_NUMBER) {
331 int32_t num;
332 CHECK_RET(OH_JSVM_GetValueInt32(env, result, &num));
333 res = std::to_string(num);
334 } else if (type == JSVM_OBJECT) {
335 JSVM_Value objResult;
336 CHECK_RET(OH_JSVM_JsonStringify(env, result, &objResult));
337 CHECK_COND(fromOHStringValue(env, objResult, res) != -1);
338 }
339 }
340 {
341 bool aal = false;
342 CHECK_RET(OH_JSVM_PumpMessageLoop(*g_vmMap[envId], &aal));
343 CHECK_RET(OH_JSVM_PerformMicrotaskCheckpoint(*g_vmMap[envId]));
344 CHECK_RET(OH_JSVM_CloseHandleScope(*g_envMap[envId], handleScope));
345 CHECK_RET(OH_JSVM_CloseEnvScope(*g_envMap[envId], envScope));
346 CHECK_RET(OH_JSVM_CloseVMScope(*g_vmMap[envId], vmScope));
347 }
348 OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS END");
349 return 0;
350 }
351
TestJSVM()352 static int32_t TestJSVM()
353 {
354 const char source1[] = "{\
355 let a = \"hello World\";\
356 consoleinfo(a);\
357 const mPromise = createPromise();\
358 mPromise.then((result) => {\
359 assertEqual(result, 0);\
360 });\
361 a;\
362 };";
363
364 const char source2[] = "{\
365 let a = \"second hello\";\
366 consoleinfo(a);\
367 let b = add(99, 1);\
368 assertEqual(100, b);\
369 assertEqual(add(99, 1), 100);\
370 createPromise().then((result) => {\
371 assertEqual(result, 1);\
372 });\
373 a;\
374 };";
375
376 // 创建首个运行环境,并绑定TS回调
377 uint32_t coreId1 = 0;
378 CHECK_COND(CreateJsCore(&coreId1) == 0);
379 OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId1);
380 // 在首个运行环境中执行JS代码
381 std::string result1;
382 CHECK_COND(EvaluateJS(coreId1, source1, result1) == 0);
383 OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result1.c_str());
384
385 // 创建第二个运行环境,并绑定TS回调
386 uint32_t coreId2 = 0;
387 CHECK_COND(CreateJsCore(&coreId2) == 0);
388 OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId2);
389 // 在第二个运行环境中执行JS代码
390 std::string result2;
391 CHECK_COND(EvaluateJS(coreId2, source2, result2) == 0);
392 OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result2.c_str());
393
394 // 释放首个运行环境
395 CHECK_COND(ReleaseJsCore(coreId1) == 0);
396 // 释放第二个运行环境
397 CHECK_COND(ReleaseJsCore(coreId2) == 0);
398 OH_LOG_INFO(LOG_APP, "Test NAPI end");
399
400 return 0;
401 }
402 // [End runtime_task]
403
RunTest(napi_env env,napi_callback_info info)404 static napi_value RunTest(napi_env env, napi_callback_info info)
405 {
406 TestJSVM();
407 return nullptr;
408 }
409
410 // 模块注册信息,供arkts侧调用
411 EXTERN_C_START
Init(napi_env env,napi_value exports)412 static napi_value Init(napi_env env, napi_value exports)
413 {
414 napi_property_descriptor desc[] = {{"runTest", nullptr, RunTest, nullptr, nullptr, nullptr, napi_default, nullptr}};
415 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
416 return exports;
417 }
418 EXTERN_C_END
419
420 static napi_module demoModule = {
421 .nm_version = 1,
422 .nm_flags = 0,
423 .nm_filename = nullptr,
424 .nm_register_func = Init,
425 .nm_modname = "runtimetask",
426 .nm_priv = ((void *)0),
427 .reserved = {0},
428 };
429
RegisterEntryModule(void)430 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
431