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