1# 使用JSVM-API接口创建多个引擎执行JS代码并销毁 2 3## 场景介绍 4 5开发者通过createJsCore方法来创建一个新的JS运行时环境,并通过该方法获得一个CoreID。然后,通过evaluateJS方法使用CoreID对应的运行环境来运行JS代码,在JS代码中创建promise并异步执行函数。最后,使用releaseJsCore方法来释放CoreID对应的运行环境。 6 7## 使用示例 8 9JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应C++相关代码进行展示。 10 11新建多个JS运行时环境并运行JS代码 12 13 ```cpp 14#include <map> 15#include <mutex> 16#include <deque> 17using namespace std; 18// 定义map管理每个独立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// 自定义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// 自定义创建Promise方法用以在JS代码中创建Promise 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 // 通过当前env获取envID 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 // 设计ReadTask类用以将promise对象的deferred加入执行队列 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// 自定义Add方法 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// 自定义AssertEqual方法 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// 提供创建JSVM运行环境的对外接口并返回对应唯一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 // 虚拟机实例 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 // 新环境 166 g_envMap[ENVTAG_NUMBER] = new JSVM_Env; 167 g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[4]; 168 169 // 注册用户提供的本地函数的回调函数指针和数据,通过JSVM-API暴露给js 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// 对外提供释放JSVM环境接口,通过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// 对外提供执行JS代码接口,通过coreID在对应的JSVN环境中执行JS代码 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 // 创建JSVM环境 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 // 通过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 // 创建首个运行环境,并绑定TS回调 306 uint32_t coreId1; 307 CHECK_COND(CreateJsCore(&coreId1) == 0); 308 OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId1); 309 // 在首个运行环境中执行JS代码 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 // 创建第二个运行环境,并绑定TS回调 315 uint32_t coreId2; 316 CHECK_COND(CreateJsCore(&coreId2) == 0); 317 OH_LOG_INFO(LOG_APP, "TEST coreId: %{public}d", coreId2); 318 // 在第二个运行环境中执行JS代码 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 // 释放首个运行环境 324 CHECK_COND(ReleaseJsCore(coreId1) == 0); 325 // 释放第二个运行环境 326 CHECK_COND(ReleaseJsCore(coreId2) == 0); 327 OH_LOG_INFO(LOG_APP, "Test NAPI end"); 328 329 return 0; 330} 331 ``` 332