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