• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Creating and Destroying JS VMs Using JSVM-API
2
3## When to Use
4
5Use **createJsCore** to create a JavaScript virtual machine (JS VM), 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 to asynchronously invoke the callback set in TS.
8
9Use **releaseJsCore** to release a JS VM.
10
11## Example
12
131. Declare the APIs, configure compile settings, and register the module.
14
15   **Declare the APIs.**
16
17     ```ts
18     // index.d.ts
19     export const createJsCore: (fun: Function) => number;
20     export const releaseJsCore: (a: number) => void;
21     export const evalUateJS: (a: number, str: string) => string;
22     ```
23
24   **Configure compile settings.**
25
26     ```
27     // CMakeLists.txt
28     # the minimum version of CMake.
29     cmake_minimum_required(VERSION 3.4.1)
30     project(MyApplication)
31
32     set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
33
34     if(DEFINED PACKAGE_FIND_FILE)
35     include(${PACKAGE_FIND_FILE})
36     endif()
37
38     include_directories(${NATIVERENDER_ROOT_PATH}
39                         ${NATIVERENDER_ROOT_PATH}/include)
40
41     add_library(entry SHARED create_jsvm_runtime.cpp)
42     target_link_libraries(entry PUBLIC libace_napi.z.so libjsvm.so libhilog_ndk.z.so)
43     ```
44
45   **Register the module.**
46
47     ```cpp
48     // create_jsvm_runtime.cpp
49     EXTERN_C_START
50     static napi_value Init(napi_env env, napi_value exports) {
51         napi_property_descriptor desc[] = {
52             {"createJsCore", nullptr, CreateJsCore, nullptr, nullptr, nullptr, napi_default, nullptr},
53             {"releaseJsCore", nullptr, ReleaseJsCore, nullptr, nullptr, nullptr, napi_default, nullptr},
54             {"evalUateJS", nullptr, EvalUateJS, nullptr, nullptr, nullptr, napi_default, nullptr}
55         };
56
57         napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
58         return exports;
59     }
60     EXTERN_C_END
61
62     static napi_module demoModule = {
63         .nm_version = 1,
64         .nm_flags = 0,
65         .nm_filename = nullptr,
66         .nm_register_func = Init,
67         .nm_modname = "entry",
68         .nm_priv = ((void *)0),
69         .reserved = {0},
70     };
71
72     extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
73     ```
74
752. Create multiple JS VMs and run JS code.
76
77  ```cpp
78  // create_jsvm_runtime.cpp
79  #include "napi/native_api.h"
80  #include "ark_runtime/jsvm.h"
81  #include "common.h"
82
83  #include <bits/alltypes.h>
84  #include <deque>
85  #include <map>
86  #include <unistd.h>
87  #include <hilog/log.h>
88  #include "myobject.h"
89
90  #include <cstring>
91  #include <string>
92  #include <vector>
93  #include <sstream>
94
95  #define LOG_TAG "TEST_TAG"
96  using namespace std;
97  // Define a map to manage each independent VM.
98  static map<int, JSVM_VM*> g_vmMap;
99  static map<int, JSVM_VMScope> g_vmScopeMap;
100  static map<int, JSVM_Env*> g_envMap;
101  static map<int, napi_env> g_napiEnvMap;
102  static map<int, JSVM_EnvScope> g_envScopeMap;
103  static map<int, napi_ref> g_callBackMap;
104  static map<int, JSVM_CallbackStruct*> g_callBackStructMap;
105  static uint32_t ENVTAG_NUMBER = 0;
106  static std::mutex envMapLock;
107  static int aa = 0;
108
109  class Task {
110  public:
111      virtual ~Task() = default;
112      virtual void Run() = 0;
113  };
114  static map<int, deque<Task *>> g_taskQueueMap;
115
116  // Customize Consoleinfo.
117  static JSVM_Value Consoleinfo(JSVM_Env env, JSVM_CallbackInfo info) {
118      size_t argc = 1;
119      JSVM_Value args[1];
120      char log[256] = "";
121      size_t log_length;
122      OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL);
123
124      OH_JSVM_GetValueStringUtf8(env, args[0], log, 255, &log_length);
125      log[255] = 0;
126      OH_LOG_INFO(LOG_APP, "JSVM API TEST: %{public}s", log);
127      return nullptr;
128  }
129
130  // Create a promise method, which is used to create a promise in JS code.
131  static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info) {
132      OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise start");
133      int envID = 0;
134      // Obtain envID of the current env.
135      for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
136          if (*it->second == env) {
137              envID = it->first;
138              break;
139          }
140      }
141      if (envID == -1) {
142          OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise envID faild");
143          return nullptr;
144      }
145      JSVM_Value promise;
146      JSVM_Deferred deferred;
147      OH_JSVM_CreatePromise(env, &deferred, &promise);
148      // Define a ReadTask class to add deferred of the promise object to the execution queue.
149      class ReadTask : public Task {
150      public:
151          ReadTask(JSVM_Env env, JSVM_Deferred deferred, int envNum) : env_(env), envID_(envNum), deferred_(deferred) {}
152          void Run() override {
153              //string str = "TEST RUN OH_JSVM_ResolveDeferred";
154              int envID = 0;
155              for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
156                  if (*it->second == env_) {
157                      envID = it->first;
158                      break;
159                  }
160              }
161              OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise %{public}d", envID);
162              JSVM_Value result;
163              OH_JSVM_CreateInt32(env_, envID, &result);
164              OH_JSVM_ResolveDeferred(env_, deferred_, result);
165          }
166      private:
167          JSVM_Env env_;
168          int envID_;
169          JSVM_Deferred deferred_;
170      };
171      g_taskQueueMap[envID].push_back(new ReadTask(env, deferred, envID));
172      OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise end");
173      return promise;
174  }
175
176  // Customize the Add method.
177  static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) {
178      size_t argc = 2;
179      JSVM_Value args[2];
180      OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL);
181      double num1, num2;
182      OH_JSVM_GetValueDouble(env, args[0], &num1);
183      OH_JSVM_GetValueDouble(env, args[1], &num2);
184      JSVM_Value sum = nullptr;
185      OH_JSVM_CreateDouble(env, num1 + num2, &sum);
186      return sum;
187  }
188
189  // Customize the AssertEqual method.
190  static JSVM_Value AssertEqual(JSVM_Env env, JSVM_CallbackInfo info) {
191      size_t argc = 2;
192      JSVM_Value args[2];
193      JSVM_CALL(env, OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
194
195      bool isStrictEquals = false;
196      OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals);
197
198      if (isStrictEquals) {
199          OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS");
200      } else {
201          OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: FAILED");
202      }
203      return nullptr;
204  }
205
206  // Invoke the native callback passed by TS.
207  static JSVM_Value OnJSResultCallback(JSVM_Env env, JSVM_CallbackInfo info) {
208      size_t argc = 3;
209      JSVM_Value args[3];
210      JSVM_CALL(env, OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
211      int callId = 0;
212      OH_JSVM_GetValueInt32(env, args[0], &callId);
213      napi_value callArgs[2] = {nullptr, nullptr};
214      size_t size;
215      size_t size1;
216
217      OH_JSVM_GetValueStringUtf8(env, args[1], nullptr, 0, &size);
218      char Str1[size + 1];
219      OH_JSVM_GetValueStringUtf8(env, args[1], Str1, size + 1, &size);
220
221      OH_JSVM_GetValueStringUtf8(env, args[2], nullptr, 0, &size1);
222      char Str2[size1 + 1];
223      OH_JSVM_GetValueStringUtf8(env, args[2], Str2, size1 + 1, &size1);
224
225      napi_create_string_utf8(g_napiEnvMap[callId], Str1, size + 1, &callArgs[0]);
226      napi_create_string_utf8(g_napiEnvMap[callId], Str2, size1 + 1, &callArgs[1]);
227      napi_value callback = nullptr;
228      // Obtain the TS callback, which is passed in when the JS VM is created, based on the call ID.
229      napi_get_reference_value(g_napiEnvMap[callId], g_callBackMap[callId], &callback);
230      napi_value ret;
231      // Execute the TS callback.
232      napi_call_function(g_napiEnvMap[callId], nullptr, callback, 2, callArgs, &ret);
233      char retStr[256];
234      napi_get_value_string_utf8(g_napiEnvMap[callId], ret, retStr, 256, &size);
235
236      JSVM_Value returnVal;
237      OH_JSVM_CreateStringUtf8(env, retStr, JSVM_AUTO_LENGTH, &returnVal);
238      return returnVal;
239  }
240
241  std::string napiValueToString(napi_env env, napi_value nValue) {
242      size_t buffLen = 0;
243      napi_get_value_string_utf8(env, nValue, nullptr, 0, &buffLen);
244      char buffer[buffLen + 1];
245      napi_get_value_string_utf8(env, nValue, buffer, buffLen + 1, &buffLen);
246
247      return buffer;
248  }
249
250  static std::string fromOHStringValue(JSVM_Env &env, JSVM_Value &value) {
251      size_t size;
252      JSVM_Status status;
253      status = OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size);
254      char resultStr[size + 1];
255      status = OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size);
256      return resultStr;
257  }
258
259  static void CreateArkJSContext() {
260      JSVM_Status status;
261      JSVM_InitOptions init_options;
262      memset(&init_options, 0, sizeof(init_options));
263      if (aa == 0) {
264          OH_JSVM_Init(&init_options);
265          aa++;
266      }
267
268      // VM instance.
269      g_vmMap[ENVTAG_NUMBER] = new JSVM_VM;
270      JSVM_VMScope vmScope;
271      g_vmScopeMap[ENVTAG_NUMBER] = vmScope;
272      JSVM_CreateVMOptions options;
273      memset(&options, 0, sizeof(options));
274      status = OH_JSVM_CreateVM(&options, g_vmMap[ENVTAG_NUMBER]);
275      status = OH_JSVM_OpenVMScope(*g_vmMap[ENVTAG_NUMBER], &g_vmScopeMap[ENVTAG_NUMBER]);
276
277      // New environment.
278      g_envMap[ENVTAG_NUMBER] = new JSVM_Env;
279      g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[5];
280
281      // Register the pointers to the native callbacks and data provided by the user and expose them to JS code through JSVM-API.
282      for (int i = 0; i < 5; i++) {
283          g_callBackStructMap[ENVTAG_NUMBER][i].data = nullptr;
284      }
285      g_callBackStructMap[ENVTAG_NUMBER][0].callback = Consoleinfo;
286      g_callBackStructMap[ENVTAG_NUMBER][1].callback = Add;
287      g_callBackStructMap[ENVTAG_NUMBER][2].callback = AssertEqual;
288      g_callBackStructMap[ENVTAG_NUMBER][3].callback = OnJSResultCallback;
289      g_callBackStructMap[ENVTAG_NUMBER][4].callback = CreatePromise;
290      JSVM_PropertyDescriptor descriptors[] = {
291          {"consoleinfo", NULL, &g_callBackStructMap[ENVTAG_NUMBER][0], NULL, NULL, NULL, JSVM_DEFAULT},
292          {"add", NULL, &g_callBackStructMap[ENVTAG_NUMBER][1], NULL, NULL, NULL, JSVM_DEFAULT},
293          {"assertEqual", NULL, &g_callBackStructMap[ENVTAG_NUMBER][2], NULL, NULL, NULL, JSVM_DEFAULT},
294          {"onJSResultCallback", NULL, &g_callBackStructMap[ENVTAG_NUMBER][3], NULL, NULL, NULL, JSVM_DEFAULT},
295          {"createPromise", NULL, &g_callBackStructMap[ENVTAG_NUMBER][4], NULL, NULL, NULL, JSVM_DEFAULT},
296      };
297      status = OH_JSVM_CreateEnv(*g_vmMap[ENVTAG_NUMBER], sizeof(descriptors) / sizeof(descriptors[0]), descriptors, g_envMap[ENVTAG_NUMBER]);
298      JSVM_EnvScope envScope;
299      g_envScopeMap[ENVTAG_NUMBER] = envScope;
300      status = OH_JSVM_OpenEnvScope(*g_envMap[ENVTAG_NUMBER], &g_envScopeMap[ENVTAG_NUMBER]);
301  }
302
303  // Provide an external interface for creating the JS VM and return the unique ID.
304  static napi_value CreateJsCore(napi_env env1, napi_callback_info info) {
305      OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore START");
306      size_t argc = 1;
307      napi_value argv[1];
308      napi_get_cb_info(env1, info, &argc, argv, nullptr, nullptr);
309      if (argc < 1) {
310          OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore the number of params must be one");
311          return nullptr;
312      }
313      g_napiEnvMap[ENVTAG_NUMBER] = env1;
314      g_taskQueueMap[ENVTAG_NUMBER] = deque<Task *>{};
315      // Store the mapping between the callback passed in by TS and env for subsequent calling.
316      napi_ref callFun;
317      napi_create_reference(env1, argv[0], 1, &callFun);
318      g_callBackMap[ENVTAG_NUMBER] = callFun;
319      napi_value coreID = 0;
320      {
321          std::lock_guard<std::mutex> lock_guard(envMapLock);
322          CreateArkJSContext();
323          napi_create_uint32(env1, ENVTAG_NUMBER, &coreID);
324          ENVTAG_NUMBER++;
325      }
326      OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore END");
327      return coreID;
328  }
329
330  // Provide an external interface for releasing the JS VM based on envId.
331  static napi_value ReleaseJsCore(napi_env env1, napi_callback_info info) {
332      OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore START");
333      size_t argc = 1;
334      napi_value argv[1];
335      napi_get_cb_info(env1, info, &argc, argv, nullptr, nullptr);
336      if (argc < 1) {
337          OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore the number of params must be one");
338          return nullptr;
339      }
340
341      uint32_t coreEnvId;
342      napi_status status = napi_get_value_uint32(env1, argv[0], &coreEnvId);
343      if (status != napi_ok) {
344          OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore napi_get_value_uint32 faild");
345          return nullptr;
346      }
347      if (g_envMap.count(coreEnvId) == 0 ) {
348          OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore not has env ");
349          return nullptr;
350      }
351      if (g_envMap[coreEnvId] != nullptr) {
352          std::lock_guard<std::mutex> lock_guard(envMapLock);
353          OH_JSVM_CloseEnvScope(*g_envMap[coreEnvId], g_envScopeMap[coreEnvId]);
354          g_envScopeMap.erase(coreEnvId);
355          OH_JSVM_DestroyEnv(*g_envMap[coreEnvId]);
356          g_envMap[coreEnvId] = nullptr;
357          g_envMap.erase(coreEnvId);
358          OH_JSVM_CloseVMScope(*g_vmMap[coreEnvId], g_vmScopeMap[coreEnvId]);
359          g_vmScopeMap.erase(coreEnvId);
360          OH_JSVM_DestroyVM(*g_vmMap[coreEnvId]);
361          g_vmMap[coreEnvId] = nullptr;
362          g_vmMap.erase(coreEnvId);
363          delete [] g_callBackStructMap[coreEnvId];
364          g_callBackStructMap[coreEnvId] = nullptr;
365          g_callBackStructMap.erase(coreEnvId);
366          napi_delete_reference(env1, g_callBackMap[coreEnvId]);
367          g_callBackMap.erase(coreEnvId);
368          g_taskQueueMap.erase(coreEnvId);
369      }
370      OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore END");
371      return nullptr;
372  }
373
374  static std::mutex mutexLock;
375  // Provide an external interface for running the JS code in the JS VM identified by a core ID.
376  static napi_value EvalUateJS(napi_env env, napi_callback_info info) {
377      OH_LOG_ERROR(LOG_APP, "JSVM EvalUateJS START");
378      size_t argc = 2;
379      napi_value args[2] = {nullptr};
380      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
381      uint32_t envId;
382      napi_status status = napi_get_value_uint32(env, args[0], &envId);
383      if (status != napi_ok) {
384          OH_LOG_ERROR(LOG_APP, "EvalUateJS first param should be number");
385          return nullptr;
386      }
387
388      if (g_envMap.count(envId) == 0 || g_envMap[envId] == nullptr) {
389          OH_LOG_ERROR(LOG_APP, "EvalUateJS env is null");
390          return nullptr;
391      }
392      std::string dataStr = napiValueToString(env, args[1]);
393      napi_value res = nullptr;
394      std::lock_guard<std::mutex> lock_guard(mutexLock);
395      {
396          // open handle scope
397          JSVM_HandleScope handlescope;
398          OH_JSVM_OpenHandleScope(*g_envMap[envId], &handlescope);
399          // Compile the JS script.
400          JSVM_Value sourcecodevalue;
401          OH_JSVM_CreateStringUtf8(*g_envMap[envId], dataStr.c_str(), dataStr.size(), &sourcecodevalue);
402          JSVM_Script script;
403          OH_JSVM_CompileScript(*g_envMap[envId], sourcecodevalue, nullptr, 0, true, nullptr, &script);
404          // Run the JS script.
405          JSVM_Value result;
406          OH_JSVM_RunScript(*g_envMap[envId], script, &result);
407          JSVM_ValueType type;
408          OH_JSVM_Typeof(*g_envMap[envId], result, &type);
409          OH_LOG_INFO(LOG_APP, "JSVM API TEST type: %{public}d", type);
410          // Execute tasks in the current env event queue.
411          while (!g_taskQueueMap[envId].empty()) {
412              auto task = g_taskQueueMap[envId].front();
413              g_taskQueueMap[envId].pop_front();
414              task->Run();
415              delete task;
416          }
417
418          if (type == JSVM_STRING) {
419              std::string stdResult = fromOHStringValue(*g_envMap[envId], result);
420              napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
421          } else if (type == JSVM_BOOLEAN) {
422              bool ret = false;
423              std::string stdResult;
424              OH_JSVM_GetValueBool(*g_envMap[envId], result, &ret);
425              ret ? stdResult = "true" : stdResult = "false";
426              napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
427          } else if (type == JSVM_NUMBER) {
428              int32_t num;
429              OH_JSVM_GetValueInt32(*g_envMap[envId], result, &num);
430              std::string stdResult = std::to_string(num);
431              napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
432          } else if (type == JSVM_OBJECT) {
433              JSVM_Value objResult;
434              OH_JSVM_JsonStringify(*g_envMap[envId], result, &objResult);
435              std::string stdResult = fromOHStringValue(*g_envMap[envId], objResult);
436              napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
437      }
438          bool aal = false;
439          PumpMessageLoop(*g_vmMap[envId], &aal);
440          PerformMicrotaskCheckpoint(*g_vmMap[envId]);
441          OH_JSVM_CloseHandleScope(*g_envMap[envId], handlescope);
442      }
443      OH_LOG_ERROR(LOG_APP, "JSVM EvalUateJS END");
444      return res;
445  }
446  ```
447
448  3. ArkTS code
449
450       ```ts
451       import { hilog } from '@kit.PerformanceAnalysisKit';
452       import testNapi from 'libentry.so';
453
454       function MyCallback(a:string, b:string):string {
455           console.log("TEST MyCallback run: " + a);
456           b = "callback done";
457           console.log("TEST MyCallback run: " + b);
458           return "callback pass";
459       }
460
461       function MyCallback2(a:string, b:string):string {
462         console.log("TEST MyCallback2 start: a =  " + a);
463         console.log("TEST MyCallback2 start: b =  " + b);
464         return "MyCallback2 pass";
465       }
466
467       @Entry
468       @Component
469       struct Index {
470         @State message: string = 'Hello World';
471
472         build() {
473           Row() {
474             Column() {
475               Text(this.message)
476                 .fontSize(50)
477                 .fontWeight(FontWeight.Bold)
478                 .onClick(() => {
479                   let sourceCodeStr = `{
480                 let a = "hello World";
481                 consoleinfo(a);
482                 const mPromise = createPromise();
483                 mPromise.then((result) => {
484                 assertEqual(result, 0);
485                 onJSResultCallback(result, "abc", "v");
486                 });
487                 a;
488                 };`;
489
490                   let sourcecodestr1 = `{
491                 let a = "second hello";
492                 consoleinfo(a);
493                 let b = add(99, 1);
494                 assertEqual(100, b);"
495                 "assertEqual(add(99, 1), 100);
496                 createPromise().then((result) => {
497                 assertEqual(result, 1);
498                 consoleinfo(onJSResultCallback(result, '999','666'));});"
499                 "a
500                 };`;
501
502                   // Create the first VM and bind the TS callback.
503                   const coreId = testNapi.createJsCore(MyCallback);
504                   console.log("TEST coreId: " + coreId);
505                   // Run JS code in the first VM.
506                   console.log("TEST evalUateJS :   " + testNapi.evalUateJS(coreId, sourcecodestr));
507
508                   // Create the second VM and bind the TS callback.
509                   const coreId1 = testNapi.createJsCore(MyCallback2);
510                   console.log("TEST coreId: " + coreId1);
511                   // Run JS code in the second VM.
512                   console.log("TEST evalUateJS :   " + testNapi.evalUateJS(coreId1, sourcecodestr1));
513
514                   // Release the first VM.
515                   testNapi.releaseJsCore(coreId);
516                   // Release the second VM.
517                   testNapi.releaseJsCore(coreId1);
518                   hilog.info(0x0000, 'testTag', 'Test NAPI end');
519                 })
520             }
521             .width('100%')
522           }
523           .height('100%')
524         }
525       }
526       ```