1# Creating and Destroying JSVMs Using JSVM-API
2
3## When to Use
4
5Use **createJsCore** to create a JavaScript virtual machine (JSVM), which a runtime environment for executing JS code. The **createJsCore** returns a core ID, which uniquely identifies a VM.
6
7Use **evaluateJS** to run JS code in the VM of the specified core ID and define a promise in the JS code and run the function asynchronously.
8
9Use **releaseJsCore** to release a JSVM.
10
11## Example
12
13Create multiple JS runtime environments and run JS code.
14
15  ```cpp
16#include <map>
17#include <mutex>
18#include <deque>
19using namespace std;
20// Define a map to manage each independent VM.
21static map<int, JSVM_VM *> g_vmMap;
22static map<int, JSVM_Env *> g_envMap;
23static map<int, JSVM_CallbackStruct *> g_callBackStructMap;
24static uint32_t ENVTAG_NUMBER = 0;
25static std::mutex envMapLock;
26
27class Task {
28public:
29    virtual ~Task() = default;
30    virtual void Run() = 0;
31};
32static map<int, deque<Task *>> g_taskQueueMap;
33
34// Customize Consoleinfo.
35static JSVM_Value Consoleinfo(JSVM_Env env, JSVM_CallbackInfo info) {
36    size_t argc = 1;
37    JSVM_Value args[1];
38    char log[256] = "";
39    size_t log_length;
40    JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
41
42    JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, args[0], log, 255, &log_length));
43    log[255] = 0;
44    OH_LOG_INFO(LOG_APP, "JSVM API TEST: %{public}s", log);
45    return nullptr;
46}
47
48// Create a promise method, which is used to create a promise in JS code.
49static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info) {
50    OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise start");
51    int envID = -1;
52    // Obtain envID of the current env.
53    for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
54        if (*it->second == env) {
55            envID = it->first;
56            break;
57        }
58    }
59    if (envID == -1) {
60        OH_LOG_ERROR(LOG_APP, "JSVM API TEST: CreatePromise envID faild");
61        return nullptr;
62    }
63    JSVM_Value promise;
64    JSVM_Deferred deferred;
65    JSVM_CALL(OH_JSVM_CreatePromise(env, &deferred, &promise));
66    // Define a ReadTask class to add deferred of the promise object to the execution queue.
67    class ReadTask : public Task {
68    public:
69        ReadTask(JSVM_Env env, JSVM_Deferred deferred, int envNum) : env_(env), envID_(envNum), deferred_(deferred) {}
70        void Run() override {
71            // string str = "TEST RUN OH_JSVM_ResolveDeferred";
72            int envID = 0;
73            for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
74                if (*it->second == env_) {
75                    envID = it->first;
76                    break;
77                }
78            }
79            OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise %{public}d", envID);
80            JSVM_Value result;
81            if (OH_JSVM_CreateInt32(env_, envID, &result) != JSVM_OK) {
82                return;
83            }
84            if (OH_JSVM_ResolveDeferred(env_, deferred_, result) != JSVM_OK) {
85                return;
86            }
87        }
88
89    private:
90        JSVM_Env env_;
91        int envID_;
92        JSVM_Deferred deferred_;
93    };
94    g_taskQueueMap[envID].push_back(new ReadTask(env, deferred, envID));
95    OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise end");
96    return promise;
97}
98
99// Customize the Add method.
100static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) {
101    size_t argc = 2;
102    JSVM_Value args[2];
103    JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
104    double num1, num2;
105    JSVM_CALL(OH_JSVM_GetValueDouble(env, args[0], &num1));
106    JSVM_CALL(OH_JSVM_GetValueDouble(env, args[1], &num2));
107    JSVM_Value sum = nullptr;
108    JSVM_CALL(OH_JSVM_CreateDouble(env, num1 + num2, &sum));
109    return sum;
110}
111
112// Customize the AssertEqual method.
113static JSVM_Value AssertEqual(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
118    bool isStrictEquals = false;
119    JSVM_CALL(OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals));
120
121    if (isStrictEquals) {
122        OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS");
123    } else {
124        OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: FAILED");
125    }
126    return nullptr;
127}
128
129static int fromOHStringValue(JSVM_Env &env, JSVM_Value &value, std::string &result) {
130    size_t size;
131    CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size));
132    char resultStr[size + 1];
133    CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size));
134    result = resultStr;
135    return 0;
136}
137
138// Provide an external interface for creating the JSVM and return the unique ID.
139static int CreateJsCore(uint32_t *result) {
140    OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore START");
141    g_taskQueueMap[ENVTAG_NUMBER] = deque<Task *>{};
142
143    if (g_aa == 0) {
144        JSVM_InitOptions init_options;
145        memset(&init_options, 0, sizeof(init_options));
146        CHECK(OH_JSVM_Init(&init_options) == JSVM_OK);
147        g_aa++;
148    }
149    std::lock_guard<std::mutex> lock_guard(envMapLock);
150
151    // VM instance.
152    g_vmMap[ENVTAG_NUMBER] = new JSVM_VM;
153    JSVM_CreateVMOptions options;
154    JSVM_VMScope vmScope;
155    memset(&options, 0, sizeof(options));
156    CHECK(OH_JSVM_CreateVM(&options, g_vmMap[ENVTAG_NUMBER]) == JSVM_OK);
157    CHECK(OH_JSVM_OpenVMScope(*g_vmMap[ENVTAG_NUMBER], &vmScope) == JSVM_OK);
158
159    // New environment.
160    g_envMap[ENVTAG_NUMBER] = new JSVM_Env;
161    g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[4];
162
163    // Register the pointers to the native callbacks and data provided by the user and expose them to JS code through JSVM-API.
164    for (int i = 0; i < 4; i++) {
165        g_callBackStructMap[ENVTAG_NUMBER][i].data = nullptr;
166    }
167    g_callBackStructMap[ENVTAG_NUMBER][0].callback = Consoleinfo;
168    g_callBackStructMap[ENVTAG_NUMBER][1].callback = Add;
169    g_callBackStructMap[ENVTAG_NUMBER][2].callback = AssertEqual;
170    g_callBackStructMap[ENVTAG_NUMBER][3].callback = CreatePromise;
171    JSVM_PropertyDescriptor descriptors[] = {
172        {"consoleinfo", NULL, &g_callBackStructMap[ENVTAG_NUMBER][0], NULL, NULL, NULL, JSVM_DEFAULT},
173        {"add", NULL, &g_callBackStructMap[ENVTAG_NUMBER][1], NULL, NULL, NULL, JSVM_DEFAULT},
174        {"assertEqual", NULL, &g_callBackStructMap[ENVTAG_NUMBER][2], NULL, NULL, NULL, JSVM_DEFAULT},
175        {"createPromise", NULL, &g_callBackStructMap[ENVTAG_NUMBER][3], NULL, NULL, NULL, JSVM_DEFAULT},
176    };
177    CHECK(OH_JSVM_CreateEnv(*g_vmMap[ENVTAG_NUMBER], sizeof(descriptors) / sizeof(descriptors[0]), descriptors,
178                            g_envMap[ENVTAG_NUMBER]) == JSVM_OK);
179    CHECK(OH_JSVM_CloseVMScope(*g_vmMap[ENVTAG_NUMBER], vmScope) == JSVM_OK);
180
181    OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore END");
182    *result = ENVTAG_NUMBER;
183    ENVTAG_NUMBER++;
184    return 0;
185}
186
187// Provide an external interface for releasing the JSVM based on envId.
188static int ReleaseJsCore(uint32_t coreEnvId) {
189    OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore START");
190    CHECK(g_envMap.count(coreEnvId) != 0 && g_envMap[coreEnvId] != nullptr);
191
192    std::lock_guard<std::mutex> lock_guard(envMapLock);
193
194    CHECK(OH_JSVM_DestroyEnv(*g_envMap[coreEnvId]) == JSVM_OK);
195    g_envMap[coreEnvId] = nullptr;
196    g_envMap.erase(coreEnvId);
197    CHECK(OH_JSVM_DestroyVM(*g_vmMap[coreEnvId]) == JSVM_OK);
198    g_vmMap[coreEnvId] = nullptr;
199    g_vmMap.erase(coreEnvId);
200    delete[] g_callBackStructMap[coreEnvId];
201    g_callBackStructMap[coreEnvId] = nullptr;
202    g_callBackStructMap.erase(coreEnvId);
203    g_taskQueueMap.erase(coreEnvId);
204
205    OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore END");
206    return 0;
207}
208
209static std::mutex mutexLock;
210// Provide an external interface for running the JS code in the JSVM identified by a core ID.
211static int EvaluateJS(uint32_t envId, const char *source, std::string &res) {
212    OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS START");
213
214    CHECK(g_envMap.count(envId) != 0 && g_envMap[envId] != nullptr);
215
216    JSVM_Env env = *g_envMap[envId];
217    JSVM_VM vm = *g_vmMap[envId];
218    JSVM_VMScope vmScope;
219    JSVM_EnvScope envScope;
220    JSVM_HandleScope handleScope;
221    JSVM_Value result;
222
223    std::lock_guard<std::mutex> lock_guard(mutexLock);
224    {
225        // Create a JSVM environment.
226        CHECK_RET(OH_JSVM_OpenVMScope(vm, &vmScope));
227        CHECK_RET(OH_JSVM_OpenEnvScope(*g_envMap[envId], &envScope));
228        CHECK_RET(OH_JSVM_OpenHandleScope(*g_envMap[envId], &handleScope));
229
230        // Call the test function through the script.
231        JSVM_Script script;
232        JSVM_Value jsSrc;
233        CHECK_RET(OH_JSVM_CreateStringUtf8(env, source, JSVM_AUTO_LENGTH, &jsSrc));
234        CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
235        CHECK_RET(OH_JSVM_RunScript(env, script, &result));
236
237        JSVM_ValueType type;
238        CHECK_RET(OH_JSVM_Typeof(env, result, &type));
239        OH_LOG_INFO(LOG_APP, "JSVM API TEST type: %{public}d", type);
240        // Execute tasks in the current env event queue.
241        while (!g_taskQueueMap[envId].empty()) {
242            auto task = g_taskQueueMap[envId].front();
243            g_taskQueueMap[envId].pop_front();
244            task->Run();
245            delete task;
246        }
247
248        if (type == JSVM_STRING) {
249            CHECK(fromOHStringValue(env, result, res) != -1);
250        } else if (type == JSVM_BOOLEAN) {
251            bool ret = false;
252            CHECK_RET(OH_JSVM_GetValueBool(env, result, &ret));
253            ret ? res = "true" : res = "false";
254        } else if (type == JSVM_NUMBER) {
255            int32_t num;
256            CHECK_RET(OH_JSVM_GetValueInt32(env, result, &num));
257            res = std::to_string(num);
258        } else if (type == JSVM_OBJECT) {
259            JSVM_Value objResult;
260            CHECK_RET(OH_JSVM_JsonStringify(env, result, &objResult));
261            CHECK(fromOHStringValue(env, objResult, res) != -1);
262        }
263    }
264    {
265        bool aal = false;
266        CHECK_RET(OH_JSVM_PumpMessageLoop(*g_vmMap[envId], &aal));
267        CHECK_RET(OH_JSVM_PerformMicrotaskCheckpoint(*g_vmMap[envId]));
268        CHECK_RET(OH_JSVM_CloseHandleScope(*g_envMap[envId], handleScope));
269        CHECK_RET(OH_JSVM_CloseEnvScope(*g_envMap[envId], envScope));
270        CHECK_RET(OH_JSVM_CloseVMScope(*g_vmMap[envId], vmScope));
271    }
272    OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS END");
273    return 0;
274}
275
276static int32_t TestJSVM() {
277    const char source1[] = "{\
278        let a = \"hello World\";\
279        consoleinfo(a);\
280        const mPromise = createPromise();\
281        mPromise.then((result) => {\
282          assertEqual(result, 0);\
283        });\
284        a;\
285    };";
286
287    const char source2[] = "{\
288        let a = \"second hello\";\
289        consoleinfo(a);\
290        let b = add(99, 1);\
291        assertEqual(100, b);\
292        assertEqual(add(99, 1), 100);\
293        createPromise().then((result) => {\
294            assertEqual(result, 1);\
295        });\
296        a;\
297    };";
298
299    // Create the first VM and bind the TS callback.
300    uint32_t coreId1;
301    CHECK(CreateJsCore(&coreId1) == 0);
302    OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId1);
303    // Run JS code in the first VM.
304    std::string result1;
305    CHECK(EvaluateJS(coreId1, source1, result1) == 0);
306    OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result1.c_str());
307
308    // Create the second VM and bind it with the TS callback.
309    uint32_t coreId2;
310    CHECK(CreateJsCore(&coreId2) == 0);
311    OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId2);
312    // Run JS code in the second VM.
313    std::string result2;
314    CHECK(EvaluateJS(coreId2, source2, result2) == 0);
315    OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result2.c_str());
316
317    // Release the first VM.
318    CHECK(ReleaseJsCore(coreId1) == 0);
319    // Release the second VM.
320    CHECK(ReleaseJsCore(coreId2) == 0);
321    OH_LOG_INFO(LOG_APP, "Test NAPI end");
322
323    return 0;
324}
325  ```
326