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