1# 使用JSVM-API接口创建多个引擎执行JS代码并销毁 2<!--Kit: NDK Development--> 3<!--Subsystem: arkcompiler--> 4<!--Owner: @yuanxiaogou; @string_sz--> 5<!--Designer: @knightaoko--> 6<!--Tester: @test_lzz--> 7<!--Adviser: @fang-jinxu--> 8 9## 场景介绍 10 11开发者通过createJsCore方法来创建一个新的JS运行时环境,并通过该方法获得一个CoreID。然后,通过evaluateJS方法使用CoreID对应的运行环境来运行JS代码,在JS代码中创建promise并异步执行函数。最后,使用releaseJsCore方法来销毁CoreID对应的运行环境。 12 13## 使用示例 14 15JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应C++相关代码进行展示。 16 17创建多个JS运行时环境并运行JS代码 18 19 ```cpp 20#include <map> 21#include <mutex> 22#include <deque> 23using namespace std; 24// 定义map管理每个独立vm环境 25static map<int, JSVM_VM *> g_vmMap; 26static map<int, JSVM_Env *> g_envMap; 27static map<int, JSVM_CallbackStruct *> g_callBackStructMap; 28static uint32_t ENVTAG_NUMBER = 0; 29static std::mutex envMapLock; 30static int g_aa = 0; 31 32#define CHECK_COND(cond) \ 33 do { \ 34 if (!(cond)) { \ 35 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = false", __FILE__, __LINE__); \ 36 return -1; \ 37 } \ 38 } while (0) 39 40class Task { 41public: 42 virtual ~Task() = default; 43 virtual void Run() = 0; 44}; 45static map<int, deque<Task *>> g_taskQueueMap; 46 47// 自定义Consoleinfo方法 48static JSVM_Value Consoleinfo(JSVM_Env env, JSVM_CallbackInfo info) { 49 size_t argc = 1; 50 JSVM_Value args[1]; 51 char log[256] = ""; 52 size_t log_length = 0; 53 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL)); 54 55 JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, args[0], log, 255, &log_length)); 56 log[255] = 0; 57 OH_LOG_INFO(LOG_APP, "JSVM API TEST: %{public}s", log); 58 return nullptr; 59} 60 61// 自定义创建Promise方法用以在JS代码中创建Promise 62static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info) { 63 OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise start"); 64 int envID = -1; 65 // 通过当前env获取envID 66 for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) { 67 if (*it->second == env) { 68 envID = it->first; 69 break; 70 } 71 } 72 if (envID == -1) { 73 OH_LOG_ERROR(LOG_APP, "JSVM API TEST: CreatePromise envID failed"); 74 return nullptr; 75 } 76 JSVM_Value promise; 77 JSVM_Deferred deferred; 78 JSVM_CALL(OH_JSVM_CreatePromise(env, &deferred, &promise)); 79 // 设计ReadTask类用以将promise对象的deferred加入执行队列 80 class ReadTask : public Task { 81 public: 82 ReadTask(JSVM_Env env, JSVM_Deferred deferred, int envNum) : env_(env), envID_(envNum), deferred_(deferred) {} 83 void Run() override { 84 // string str = "TEST RUN OH_JSVM_ResolveDeferred"; 85 int envID = 0; 86 for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) { 87 if (*it->second == env_) { 88 envID = it->first; 89 break; 90 } 91 } 92 OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise %{public}d", envID); 93 JSVM_Value result; 94 if (OH_JSVM_CreateInt32(env_, envID, &result) != JSVM_OK) { 95 return; 96 } 97 if (OH_JSVM_ResolveDeferred(env_, deferred_, result) != JSVM_OK) { 98 return; 99 } 100 } 101 102 private: 103 JSVM_Env env_; 104 int envID_; 105 JSVM_Deferred deferred_; 106 }; 107 g_taskQueueMap[envID].push_back(new ReadTask(env, deferred, envID)); 108 OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise end"); 109 return promise; 110} 111 112// 自定义Add方法 113static JSVM_Value Add(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 double num1 = 0, num2 = 0; 118 JSVM_CALL(OH_JSVM_GetValueDouble(env, args[0], &num1)); 119 JSVM_CALL(OH_JSVM_GetValueDouble(env, args[1], &num2)); 120 JSVM_Value sum = nullptr; 121 JSVM_CALL(OH_JSVM_CreateDouble(env, num1 + num2, &sum)); 122 return sum; 123} 124 125// 自定义AssertEqual方法 126static JSVM_Value AssertEqual(JSVM_Env env, JSVM_CallbackInfo info) { 127 size_t argc = 2; 128 JSVM_Value args[2]; 129 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL)); 130 131 bool isStrictEquals = false; 132 JSVM_CALL(OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals)); 133 134 if (isStrictEquals) { 135 OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS"); 136 } else { 137 OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: FAILED"); 138 } 139 return nullptr; 140} 141 142static int fromOHStringValue(JSVM_Env &env, JSVM_Value &value, std::string &result) { 143 size_t size = 0; 144 CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size)); 145 char *resultStr = new char[size + 1]; 146 CHECK_RET(OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size)); 147 result = resultStr; 148 delete[] resultStr; 149 return 0; 150} 151 152// 提供创建JSVM运行环境的对外接口并返回对应唯一ID 153static int CreateJsCore(uint32_t *result) { 154 OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore START"); 155 g_taskQueueMap[ENVTAG_NUMBER] = deque<Task *>{}; 156 157 if (g_aa == 0) { 158 JSVM_InitOptions init_options; 159 memset(&init_options, 0, sizeof(init_options)); 160 CHECK(OH_JSVM_Init(&init_options)); 161 g_aa++; 162 } 163 std::lock_guard<std::mutex> lock_guard(envMapLock); 164 165 // 虚拟机实例 166 g_vmMap[ENVTAG_NUMBER] = new JSVM_VM; 167 JSVM_CreateVMOptions options; 168 JSVM_VMScope vmScope; 169 memset(&options, 0, sizeof(options)); 170 CHECK(OH_JSVM_CreateVM(&options, g_vmMap[ENVTAG_NUMBER])); 171 CHECK(OH_JSVM_OpenVMScope(*g_vmMap[ENVTAG_NUMBER], &vmScope)); 172 173 // 新环境 174 g_envMap[ENVTAG_NUMBER] = new JSVM_Env; 175 g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[4]; 176 177 // 注册用户提供的本地函数的回调函数指针和数据,通过JSVM-API暴露给js 178 for (int i = 0; i < 4; i++) { 179 g_callBackStructMap[ENVTAG_NUMBER][i].data = nullptr; 180 } 181 g_callBackStructMap[ENVTAG_NUMBER][0].callback = Consoleinfo; 182 g_callBackStructMap[ENVTAG_NUMBER][1].callback = Add; 183 g_callBackStructMap[ENVTAG_NUMBER][2].callback = AssertEqual; 184 g_callBackStructMap[ENVTAG_NUMBER][3].callback = CreatePromise; 185 JSVM_PropertyDescriptor descriptors[] = { 186 {"consoleinfo", NULL, &g_callBackStructMap[ENVTAG_NUMBER][0], NULL, NULL, NULL, JSVM_DEFAULT}, 187 {"add", NULL, &g_callBackStructMap[ENVTAG_NUMBER][1], NULL, NULL, NULL, JSVM_DEFAULT}, 188 {"assertEqual", NULL, &g_callBackStructMap[ENVTAG_NUMBER][2], NULL, NULL, NULL, JSVM_DEFAULT}, 189 {"createPromise", NULL, &g_callBackStructMap[ENVTAG_NUMBER][3], NULL, NULL, NULL, JSVM_DEFAULT}, 190 }; 191 CHECK(OH_JSVM_CreateEnv(*g_vmMap[ENVTAG_NUMBER], sizeof(descriptors) / sizeof(descriptors[0]), descriptors, 192 g_envMap[ENVTAG_NUMBER])); 193 CHECK(OH_JSVM_CloseVMScope(*g_vmMap[ENVTAG_NUMBER], vmScope)); 194 195 OH_LOG_INFO(LOG_APP, "JSVM CreateJsCore END"); 196 *result = ENVTAG_NUMBER; 197 ENVTAG_NUMBER++; 198 return 0; 199} 200 201// 对外提供释放JSVM环境接口,通过envId释放对应环境 202static int ReleaseJsCore(uint32_t coreEnvId) { 203 std::lock_guard<std::mutex> lock_guard(envMapLock); 204 205 OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore START"); 206 CHECK_COND(g_envMap.count(coreEnvId) != 0 && g_envMap[coreEnvId] != nullptr); 207 208 CHECK(OH_JSVM_DestroyEnv(*g_envMap[coreEnvId])); 209 g_envMap[coreEnvId] = nullptr; 210 g_envMap.erase(coreEnvId); 211 CHECK(OH_JSVM_DestroyVM(*g_vmMap[coreEnvId])); 212 g_vmMap[coreEnvId] = nullptr; 213 g_vmMap.erase(coreEnvId); 214 delete[] g_callBackStructMap[coreEnvId]; 215 g_callBackStructMap[coreEnvId] = nullptr; 216 g_callBackStructMap.erase(coreEnvId); 217 g_taskQueueMap.erase(coreEnvId); 218 219 OH_LOG_INFO(LOG_APP, "JSVM ReleaseJsCore END"); 220 return 0; 221} 222 223static std::mutex mutexLock; 224// 对外提供执行JS代码接口,通过coreID在对应的JSVN环境中执行JS代码 225static int EvaluateJS(uint32_t envId, const char *source, std::string &res) { 226 OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS START"); 227 228 CHECK_COND(g_envMap.count(envId) != 0 && g_envMap[envId] != nullptr); 229 230 JSVM_Env env = *g_envMap[envId]; 231 JSVM_VM vm = *g_vmMap[envId]; 232 JSVM_VMScope vmScope; 233 JSVM_EnvScope envScope; 234 JSVM_HandleScope handleScope; 235 JSVM_Value result; 236 237 std::lock_guard<std::mutex> lock_guard(mutexLock); 238 { 239 // 创建JSVM环境 240 CHECK_RET(OH_JSVM_OpenVMScope(vm, &vmScope)); 241 CHECK_RET(OH_JSVM_OpenEnvScope(*g_envMap[envId], &envScope)); 242 CHECK_RET(OH_JSVM_OpenHandleScope(*g_envMap[envId], &handleScope)); 243 244 // 通过script调用测试函数 245 JSVM_Script script; 246 JSVM_Value jsSrc; 247 CHECK_RET(OH_JSVM_CreateStringUtf8(env, source, JSVM_AUTO_LENGTH, &jsSrc)); 248 CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script)); 249 CHECK_RET(OH_JSVM_RunScript(env, script, &result)); 250 251 JSVM_ValueType type; 252 CHECK_RET(OH_JSVM_Typeof(env, result, &type)); 253 OH_LOG_INFO(LOG_APP, "JSVM API TEST type: %{public}d", type); 254 // Execute tasks in the current env event queue 255 while (!g_taskQueueMap[envId].empty()) { 256 auto task = g_taskQueueMap[envId].front(); 257 g_taskQueueMap[envId].pop_front(); 258 task->Run(); 259 delete task; 260 } 261 262 if (type == JSVM_STRING) { 263 CHECK_COND(fromOHStringValue(env, result, res) != -1); 264 } else if (type == JSVM_BOOLEAN) { 265 bool ret = false; 266 CHECK_RET(OH_JSVM_GetValueBool(env, result, &ret)); 267 ret ? res = "true" : res = "false"; 268 } else if (type == JSVM_NUMBER) { 269 int32_t num = 0; 270 CHECK_RET(OH_JSVM_GetValueInt32(env, result, &num)); 271 res = std::to_string(num); 272 } else if (type == JSVM_OBJECT) { 273 JSVM_Value objResult; 274 CHECK_RET(OH_JSVM_JsonStringify(env, result, &objResult)); 275 CHECK_COND(fromOHStringValue(env, objResult, res) != -1); 276 } 277 } 278 { 279 bool aal = false; 280 CHECK_RET(OH_JSVM_PumpMessageLoop(*g_vmMap[envId], &aal)); 281 CHECK_RET(OH_JSVM_PerformMicrotaskCheckpoint(*g_vmMap[envId])); 282 CHECK_RET(OH_JSVM_CloseHandleScope(*g_envMap[envId], handleScope)); 283 CHECK_RET(OH_JSVM_CloseEnvScope(*g_envMap[envId], envScope)); 284 CHECK_RET(OH_JSVM_CloseVMScope(*g_vmMap[envId], vmScope)); 285 } 286 OH_LOG_INFO(LOG_APP, "JSVM EvaluateJS END"); 287 return 0; 288} 289 290static int32_t TestJSVM() { 291 const char source1[] = "{\ 292 let a = \"hello World\";\ 293 consoleinfo(a);\ 294 const mPromise = createPromise();\ 295 mPromise.then((result) => {\ 296 assertEqual(result, 0);\ 297 });\ 298 a;\ 299 };"; 300 301 const char source2[] = "{\ 302 let a = \"second hello\";\ 303 consoleinfo(a);\ 304 let b = add(99, 1);\ 305 assertEqual(100, b);\ 306 assertEqual(add(99, 1), 100);\ 307 createPromise().then((result) => {\ 308 assertEqual(result, 1);\ 309 });\ 310 a;\ 311 };"; 312 313 // 创建首个运行环境,并绑定TS回调 314 uint32_t coreId1 = 0; 315 CHECK_COND(CreateJsCore(&coreId1) == 0); 316 OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId1); 317 // 在首个运行环境中执行JS代码 318 std::string result1; 319 CHECK_COND(EvaluateJS(coreId1, source1, result1) == 0); 320 OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result1.c_str()); 321 322 // 创建第二个运行环境,并绑定TS回调 323 uint32_t coreId2 = 0; 324 CHECK_COND(CreateJsCore(&coreId2) == 0); 325 OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId2); 326 // 在第二个运行环境中执行JS代码 327 std::string result2; 328 CHECK_COND(EvaluateJS(coreId2, source2, result2) == 0); 329 OH_LOG_INFO(LOG_APP, "TEST evaluateJS: %{public}s", result2.c_str()); 330 331 // 释放首个运行环境 332 CHECK_COND(ReleaseJsCore(coreId1) == 0); 333 // 释放第二个运行环境 334 CHECK_COND(ReleaseJsCore(coreId2) == 0); 335 OH_LOG_INFO(LOG_APP, "Test NAPI end"); 336 337 return 0; 338} 339``` 340<!-- @[runtime_task](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmDebug/runtimetask/src/main/cpp/hello.cpp) --> 341预计的输出结果: 342``` 343JSVM CreateJsCore START 344JSVM CreateJsCore END 345TEST coreId: 0 346JSVM EvaluateJS START 347JSVM API TEST: hello World 348JSVM API TEST: CreatePromise start 349JSVM API TEST: CreatePromise end 350JSVM API TEST type: 4 351JSVM API TEST: CreatePromise 0 352JSVM API TEST RESULT: PASS 353JSVM EvaluateJS END 354TEST evaluateJS: hello World 355JSVM CreateJsCore START 356JSVM CreateJsCore END 357TEST coreId: 1 358JSVM EvaluateJS START 359JSVM API TEST: second hello 360JSVM API TEST RESULT: PASS 361JSVM API TEST RESULT: PASS 362JSVM API TEST: CreatePromise start 363JSVM API TEST: CreatePromise end 364JSVM API TEST type: 4 365JSVM API TEST: CreatePromise 1 366JSVM API TEST RESULT: PASS 367JSVM EvaluateJS END 368TEST evaluateJS: second hello 369JSVM ReleaseJsCore START 370JSVM ReleaseJsCore END 371JSVM ReleaseJsCore START 372JSVM ReleaseJsCore END 373Test NAPI end 374``` 375