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