• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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