• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用JSVM-API接口创建多个引擎执行JS代码并销毁
2<!--Kit: NDK Development-->
3<!--Subsystem: arkcompiler-->
4<!--Owner: @yuanxiaogou; @string_sz-->
5<!--Designer: @knightaoko-->
6<!--Tester: @test_lzz-->
7<!--Adviser: @fang-jinxu-->
8
9## 场景介绍
10
11开发者通过createJsCore方法来创建一个新的JS运行时环境,并通过该方法获得一个CoreID。然后,通过evaluateJS方法使用CoreID对应的运行环境来运行JS代码,在JS代码中创建promise并异步执行函数。最后,使用releaseJsCore方法来销毁CoreID对应的运行环境。
12
13## 使用示例
14
15JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应C++相关代码进行展示。
16
17创建多个JS运行时环境并运行JS代码
18
19  ```cpp
20#include <map>
21#include <mutex>
22#include <deque>
23using namespace std;
24// 定义map管理每个独立vm环境
25static map<int, JSVM_VM *> g_vmMap;
26static map<int, JSVM_Env *> g_envMap;
27static map<int, JSVM_CallbackStruct *> g_callBackStructMap;
28static uint32_t ENVTAG_NUMBER = 0;
29static std::mutex envMapLock;
30static int g_aa = 0;
31
32#define CHECK_COND(cond)                                                                                               \
33    do {                                                                                                               \
34        if (!(cond)) {                                                                                                 \
35            OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = false", __FILE__, __LINE__);      \
36            return -1;                                                                                                 \
37        }                                                                                                              \
38    } while (0)
39
40class Task {
41public:
42    virtual ~Task() = default;
43    virtual void Run() = 0;
44};
45static map<int, deque<Task *>> g_taskQueueMap;
46
47// 自定义Consoleinfo方法
48static JSVM_Value Consoleinfo(JSVM_Env env, JSVM_CallbackInfo info) {
49    size_t argc = 1;
50    JSVM_Value args[1];
51    char log[256] = "";
52    size_t log_length = 0;
53    JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
54
55    JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, args[0], log, 255, &log_length));
56    log[255] = 0;
57    OH_LOG_INFO(LOG_APP, "JSVM API TEST: %{public}s", log);
58    return nullptr;
59}
60
61// 自定义创建Promise方法用以在JS代码中创建Promise
62static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info) {
63    OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise start");
64    int envID = -1;
65    // 通过当前env获取envID
66    for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
67        if (*it->second == env) {
68            envID = it->first;
69            break;
70        }
71    }
72    if (envID == -1) {
73        OH_LOG_ERROR(LOG_APP, "JSVM API TEST: CreatePromise envID failed");
74        return nullptr;
75    }
76    JSVM_Value promise;
77    JSVM_Deferred deferred;
78    JSVM_CALL(OH_JSVM_CreatePromise(env, &deferred, &promise));
79    // 设计ReadTask类用以将promise对象的deferred加入执行队列
80    class ReadTask : public Task {
81    public:
82        ReadTask(JSVM_Env env, JSVM_Deferred deferred, int envNum) : env_(env), envID_(envNum), deferred_(deferred) {}
83        void Run() override {
84            // string str = "TEST RUN OH_JSVM_ResolveDeferred";
85            int envID = 0;
86            for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
87                if (*it->second == env_) {
88                    envID = it->first;
89                    break;
90                }
91            }
92            OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise %{public}d", envID);
93            JSVM_Value result;
94            if (OH_JSVM_CreateInt32(env_, envID, &result) != JSVM_OK) {
95                return;
96            }
97            if (OH_JSVM_ResolveDeferred(env_, deferred_, result) != JSVM_OK) {
98                return;
99            }
100        }
101
102    private:
103        JSVM_Env env_;
104        int envID_;
105        JSVM_Deferred deferred_;
106    };
107    g_taskQueueMap[envID].push_back(new ReadTask(env, deferred, envID));
108    OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise end");
109    return promise;
110}
111
112// 自定义Add方法
113static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) {
114    size_t argc = 2;
115    JSVM_Value args[2];
116    JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
117    double num1 = 0, num2 = 0;
118    JSVM_CALL(OH_JSVM_GetValueDouble(env, args[0], &num1));
119    JSVM_CALL(OH_JSVM_GetValueDouble(env, args[1], &num2));
120    JSVM_Value sum = nullptr;
121    JSVM_CALL(OH_JSVM_CreateDouble(env, num1 + num2, &sum));
122    return sum;
123}
124
125// 自定义AssertEqual方法
126static JSVM_Value AssertEqual(JSVM_Env env, JSVM_CallbackInfo info) {
127    size_t argc = 2;
128    JSVM_Value args[2];
129    JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
130
131    bool isStrictEquals = false;
132    JSVM_CALL(OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals));
133
134    if (isStrictEquals) {
135        OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS");
136    } else {
137        OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: FAILED");
138    }
139    return nullptr;
140}
141
142static int fromOHStringValue(JSVM_Env &env, JSVM_Value &value, std::string &result) {
143    size_t size = 0;
144    CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size));
145    char *resultStr = new char[size + 1];
146    CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size));
147    result = resultStr;
148    delete[] resultStr;
149    return 0;
150}
151
152// 提供创建JSVM运行环境的对外接口并返回对应唯一ID
153static int CreateJsCore(uint32_t *result) {
154    OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore START");
155    g_taskQueueMap[ENVTAG_NUMBER] = deque<Task *>{};
156
157    if (g_aa == 0) {
158        JSVM_InitOptions init_options;
159        memset(&init_options, 0, sizeof(init_options));
160        CHECK(OH_JSVM_Init(&init_options));
161        g_aa++;
162    }
163    std::lock_guard<std::mutex> lock_guard(envMapLock);
164
165    // 虚拟机实例
166    g_vmMap[ENVTAG_NUMBER] = new JSVM_VM;
167    JSVM_CreateVMOptions options;
168    JSVM_VMScope vmScope;
169    memset(&options, 0, sizeof(options));
170    CHECK(OH_JSVM_CreateVM(&options, g_vmMap[ENVTAG_NUMBER]));
171    CHECK(OH_JSVM_OpenVMScope(*g_vmMap[ENVTAG_NUMBER], &vmScope));
172
173    // 新环境
174    g_envMap[ENVTAG_NUMBER] = new JSVM_Env;
175    g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[4];
176
177    // 注册用户提供的本地函数的回调函数指针和数据,通过JSVM-API暴露给js
178    for (int i = 0; i < 4; i++) {
179        g_callBackStructMap[ENVTAG_NUMBER][i].data = nullptr;
180    }
181    g_callBackStructMap[ENVTAG_NUMBER][0].callback = Consoleinfo;
182    g_callBackStructMap[ENVTAG_NUMBER][1].callback = Add;
183    g_callBackStructMap[ENVTAG_NUMBER][2].callback = AssertEqual;
184    g_callBackStructMap[ENVTAG_NUMBER][3].callback = CreatePromise;
185    JSVM_PropertyDescriptor descriptors[] = {
186        {"consoleinfo", NULL, &g_callBackStructMap[ENVTAG_NUMBER][0], NULL, NULL, NULL, JSVM_DEFAULT},
187        {"add", NULL, &g_callBackStructMap[ENVTAG_NUMBER][1], NULL, NULL, NULL, JSVM_DEFAULT},
188        {"assertEqual", NULL, &g_callBackStructMap[ENVTAG_NUMBER][2], NULL, NULL, NULL, JSVM_DEFAULT},
189        {"createPromise", NULL, &g_callBackStructMap[ENVTAG_NUMBER][3], NULL, NULL, NULL, JSVM_DEFAULT},
190    };
191    CHECK(OH_JSVM_CreateEnv(*g_vmMap[ENVTAG_NUMBER], sizeof(descriptors) / sizeof(descriptors[0]), descriptors,
192                            g_envMap[ENVTAG_NUMBER]));
193    CHECK(OH_JSVM_CloseVMScope(*g_vmMap[ENVTAG_NUMBER], vmScope));
194
195    OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore END");
196    *result = ENVTAG_NUMBER;
197    ENVTAG_NUMBER++;
198    return 0;
199}
200
201// 对外提供释放JSVM环境接口,通过envId释放对应环境
202static int ReleaseJsCore(uint32_t coreEnvId) {
203    std::lock_guard<std::mutex> lock_guard(envMapLock);
204
205    OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore START");
206    CHECK_COND(g_envMap.count(coreEnvId) != 0 && g_envMap[coreEnvId] != nullptr);
207
208    CHECK(OH_JSVM_DestroyEnv(*g_envMap[coreEnvId]));
209    g_envMap[coreEnvId] = nullptr;
210    g_envMap.erase(coreEnvId);
211    CHECK(OH_JSVM_DestroyVM(*g_vmMap[coreEnvId]));
212    g_vmMap[coreEnvId] = nullptr;
213    g_vmMap.erase(coreEnvId);
214    delete[] g_callBackStructMap[coreEnvId];
215    g_callBackStructMap[coreEnvId] = nullptr;
216    g_callBackStructMap.erase(coreEnvId);
217    g_taskQueueMap.erase(coreEnvId);
218
219    OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore END");
220    return 0;
221}
222
223static std::mutex mutexLock;
224// 对外提供执行JS代码接口,通过coreID在对应的JSVN环境中执行JS代码
225static int EvaluateJS(uint32_t envId, const char *source, std::string &res) {
226    OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS START");
227
228    CHECK_COND(g_envMap.count(envId) != 0 && g_envMap[envId] != nullptr);
229
230    JSVM_Env env = *g_envMap[envId];
231    JSVM_VM vm = *g_vmMap[envId];
232    JSVM_VMScope vmScope;
233    JSVM_EnvScope envScope;
234    JSVM_HandleScope handleScope;
235    JSVM_Value result;
236
237    std::lock_guard<std::mutex> lock_guard(mutexLock);
238    {
239        // 创建JSVM环境
240        CHECK_RET(OH_JSVM_OpenVMScope(vm, &vmScope));
241        CHECK_RET(OH_JSVM_OpenEnvScope(*g_envMap[envId], &envScope));
242        CHECK_RET(OH_JSVM_OpenHandleScope(*g_envMap[envId], &handleScope));
243
244        // 通过script调用测试函数
245        JSVM_Script script;
246        JSVM_Value jsSrc;
247        CHECK_RET(OH_JSVM_CreateStringUtf8(env, source, JSVM_AUTO_LENGTH, &jsSrc));
248        CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
249        CHECK_RET(OH_JSVM_RunScript(env, script, &result));
250
251        JSVM_ValueType type;
252        CHECK_RET(OH_JSVM_Typeof(env, result, &type));
253        OH_LOG_INFO(LOG_APP, "JSVM API TEST type: %{public}d", type);
254        // Execute tasks in the current env event queue
255        while (!g_taskQueueMap[envId].empty()) {
256            auto task = g_taskQueueMap[envId].front();
257            g_taskQueueMap[envId].pop_front();
258            task->Run();
259            delete task;
260        }
261
262        if (type == JSVM_STRING) {
263            CHECK_COND(fromOHStringValue(env, result, res) != -1);
264        } else if (type == JSVM_BOOLEAN) {
265            bool ret = false;
266            CHECK_RET(OH_JSVM_GetValueBool(env, result, &ret));
267            ret ? res = "true" : res = "false";
268        } else if (type == JSVM_NUMBER) {
269            int32_t num = 0;
270            CHECK_RET(OH_JSVM_GetValueInt32(env, result, &num));
271            res = std::to_string(num);
272        } else if (type == JSVM_OBJECT) {
273            JSVM_Value objResult;
274            CHECK_RET(OH_JSVM_JsonStringify(env, result, &objResult));
275            CHECK_COND(fromOHStringValue(env, objResult, res) != -1);
276        }
277    }
278    {
279        bool aal = false;
280        CHECK_RET(OH_JSVM_PumpMessageLoop(*g_vmMap[envId], &aal));
281        CHECK_RET(OH_JSVM_PerformMicrotaskCheckpoint(*g_vmMap[envId]));
282        CHECK_RET(OH_JSVM_CloseHandleScope(*g_envMap[envId], handleScope));
283        CHECK_RET(OH_JSVM_CloseEnvScope(*g_envMap[envId], envScope));
284        CHECK_RET(OH_JSVM_CloseVMScope(*g_vmMap[envId], vmScope));
285    }
286    OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS END");
287    return 0;
288}
289
290static int32_t TestJSVM() {
291    const char source1[] = "{\
292        let a = \"hello World\";\
293        consoleinfo(a);\
294        const mPromise = createPromise();\
295        mPromise.then((result) => {\
296          assertEqual(result, 0);\
297        });\
298        a;\
299    };";
300
301    const char source2[] = "{\
302        let a = \"second hello\";\
303        consoleinfo(a);\
304        let b = add(99, 1);\
305        assertEqual(100, b);\
306        assertEqual(add(99, 1), 100);\
307        createPromise().then((result) => {\
308            assertEqual(result, 1);\
309        });\
310        a;\
311    };";
312
313    // 创建首个运行环境,并绑定TS回调
314    uint32_t coreId1 = 0;
315    CHECK_COND(CreateJsCore(&coreId1) == 0);
316    OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId1);
317    // 在首个运行环境中执行JS代码
318    std::string result1;
319    CHECK_COND(EvaluateJS(coreId1, source1, result1) == 0);
320    OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result1.c_str());
321
322    // 创建第二个运行环境,并绑定TS回调
323    uint32_t coreId2 = 0;
324    CHECK_COND(CreateJsCore(&coreId2) == 0);
325    OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId2);
326    // 在第二个运行环境中执行JS代码
327    std::string result2;
328    CHECK_COND(EvaluateJS(coreId2, source2, result2) == 0);
329    OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result2.c_str());
330
331    // 释放首个运行环境
332    CHECK_COND(ReleaseJsCore(coreId1) == 0);
333    // 释放第二个运行环境
334    CHECK_COND(ReleaseJsCore(coreId2) == 0);
335    OH_LOG_INFO(LOG_APP, "Test NAPI end");
336
337    return 0;
338}
339```
340<!-- @[runtime_task](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmDebug/runtimetask/src/main/cpp/hello.cpp) -->
341预计的输出结果:
342```
343JSVM CreateJsCore START
344JSVM CreateJsCore END
345TEST coreId: 0
346JSVM EvaluateJS START
347JSVM API TEST: hello World
348JSVM API TEST: CreatePromise start
349JSVM API TEST: CreatePromise end
350JSVM API TEST type: 4
351JSVM API TEST: CreatePromise 0
352JSVM API TEST RESULT: PASS
353JSVM EvaluateJS END
354TEST evaluateJS: hello World
355JSVM CreateJsCore START
356JSVM CreateJsCore END
357TEST coreId: 1
358JSVM EvaluateJS START
359JSVM API TEST: second hello
360JSVM API TEST RESULT: PASS
361JSVM API TEST RESULT: PASS
362JSVM API TEST: CreatePromise start
363JSVM API TEST: CreatePromise end
364JSVM API TEST type: 4
365JSVM API TEST: CreatePromise 1
366JSVM API TEST RESULT: PASS
367JSVM EvaluateJS END
368TEST evaluateJS: second hello
369JSVM ReleaseJsCore START
370JSVM ReleaseJsCore END
371JSVM ReleaseJsCore START
372JSVM ReleaseJsCore END
373Test NAPI end
374```
375