1# 使用JSVM-API接口进行虚拟机快照相关开发 2 3## 简介 4 5JavaScript虚拟机(JSVM)的快照创建功能,将当前运行时的JavaScript程序状态保存为一个快照文件,这个快照文件包含了当前的堆内存、执行上下文、函数闭包等信息。 6 7## 基本概念 8 9- **虚拟机启动快照**:虚拟机在某个特定时间点的状态快照,包含了当前虚拟机的所有内部状态和数据。通过创建一个启动快照,可以在之后的时间点恢复虚拟机到相同的状态。 10 11创建虚拟机启动快照可以简化一些复杂的编程任务,使得在JSVM中管理和维护虚拟机更加便捷,使程序更加灵活与稳定。 12 13## 接口说明 14 15| 接口 | 功能说明 | 16|----------------------------|-------------------------------| 17| OH_JSVM_CreateSnapshot | 用于创建虚拟机的启动快照 | 18|OH_JSVM_CreateEnvFromSnapshot| 基于虚拟机的起始快照,创建一个新的环境 | 19## 使用示例 20 21### OH_JSVM_CreateSnapshot & OH_JSVM_CreateEnvFromSnapshot 22 23用于创建和使用虚拟机的启动快照。 24 25cpp部分代码 26 27**注意事项**: 需要在OH_JSVM_Init的时候,将JSVM对外部的依赖注册到initOptions.externalReferences中。 28```cpp 29// hello.cpp 30#include "napi/native_api.h" 31#include "ark_runtime/jsvm.h" 32#include <hilog/log.h> 33#include <fstream> 34 35#define LOG_DEMAIN 0x0202 36#define LOG_TAG "TEST_TAG" 37 38static int g_aa = 0; 39 40#define CHECK_RET(theCall) \ 41 do { \ 42 JSVM_Status cond = theCall; \ 43 if ((cond) != JSVM_OK) { \ 44 const JSVM_ExtendedErrorInfo *info; \ 45 OH_JSVM_GetLastErrorInfo(env, &info); \ 46 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \ 47 __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \ 48 return -1; \ 49 } \ 50 } while (0) 51 52#define CHECK(theCall) \ 53 do { \ 54 JSVM_Status cond = theCall; \ 55 if ((cond) != JSVM_OK) { \ 56 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d", __FILE__, __LINE__, \ 57 cond); \ 58 return -1; \ 59 } \ 60 } while (0) 61 62// 用于调用theCall并检查其返回值是否为JSVM_OK。 63// 如果不是,则调用GET_AND_THROW_LAST_ERROR处理错误并返回retVal。 64#define JSVM_CALL_BASE(env, theCall, retVal) \ 65 do { \ 66 JSVM_Status cond = theCall; \ 67 if (cond != JSVM_OK) { \ 68 const JSVM_ExtendedErrorInfo *info; \ 69 OH_JSVM_GetLastErrorInfo(env, &info); \ 70 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \ 71 __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : ""); \ 72 return retVal; \ 73 } \ 74 } while (0) 75 76// JSVM_CALL_BASE的简化版本,返回nullptr 77#define JSVM_CALL(theCall) JSVM_CALL_BASE(env, theCall, nullptr) 78 79static const int MAX_BUFFER_SIZE = 128; 80// CreateHelloString()函数需绑定到JSVM虚拟机, 用于OH_JSVM_CreateSnapshot虚拟机快照的正常创建 81static JSVM_Value CreateHelloString(JSVM_Env env, JSVM_CallbackInfo info) { 82 JSVM_Value outPut; 83 OH_JSVM_CreateStringUtf8(env, "Hello world!", JSVM_AUTO_LENGTH, &outPut); 84 return outPut; 85} 86// 提供外部引用的方式以便JavaScript环境可以调用绑定的函数 87static JSVM_CallbackStruct helloCb = {CreateHelloString, nullptr}; 88 89static intptr_t externals[] = { 90 (intptr_t)&helloCb, 91 0, 92}; 93 94static JSVM_Value RunVMScript(JSVM_Env env, std::string &src) { 95 // 打开handleScope作用域 96 JSVM_HandleScope handleScope; 97 OH_JSVM_OpenHandleScope(env, &handleScope); 98 JSVM_Value jsStr = nullptr; 99 OH_JSVM_CreateStringUtf8(env, src.c_str(), src.size(), &jsStr); 100 // 编译JavaScript代码 101 JSVM_Script script; 102 OH_JSVM_CompileScript(env, jsStr, nullptr, 0, true, nullptr, &script); 103 // 执行JavaScript代码 104 JSVM_Value result = nullptr; 105 OH_JSVM_RunScript(env, script, &result); 106 // 关闭handleScope作用域 107 OH_JSVM_CloseHandleScope(env, handleScope); 108 return result; 109} 110// OH_JSVM_CreateSnapshot的样例方法 111static void CreateVMSnapshot() { 112 // 创建JavaScript虚拟机实例,打开虚拟机作用域 113 JSVM_VM vm; 114 JSVM_CreateVMOptions vmOptions; 115 memset(&vmOptions, 0, sizeof(vmOptions)); 116 // isForSnapshotting设置该虚拟机是否用于创建快照 117 vmOptions.isForSnapshotting = true; 118 OH_JSVM_CreateVM(&vmOptions, &vm); 119 JSVM_VMScope vmScope; 120 OH_JSVM_OpenVMScope(vm, &vmScope); 121 // 创建JavaScript环境,打开环境作用域 122 JSVM_Env env; 123 // 将native函数注册成JavaScript可调用的方法 124 JSVM_PropertyDescriptor descriptor[] = { 125 {"createHelloString", nullptr, &helloCb, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 126 }; 127 OH_JSVM_CreateEnv(vm, 1, descriptor, &env); 128 JSVM_EnvScope envScope; 129 OH_JSVM_OpenEnvScope(env, &envScope); 130 // 使用OH_JSVM_CreateSnapshot创建虚拟机的启动快照 131 const char *blobData = nullptr; 132 size_t blobSize = 0; 133 JSVM_Env envs[1] = {env}; 134 OH_JSVM_CreateSnapshot(vm, 1, envs, &blobData, &blobSize); 135 // 将snapshot保存到文件中 136 // 保存快照数据,/data/storage/el2/base/files/test_blob.bin为沙箱路径 137 // 以包名为com.example.jsvm为例,实际文件会保存到/data/app/el2/100/base/com.example.jsvm/files/test_blob.bin 138 std::ofstream file("/data/storage/el2/base/files/test_blob.bin", 139 std::ios::out | std::ios::binary | std::ios::trunc); 140 file.write(blobData, blobSize); 141 file.close(); 142 // 关闭并销毁环境和虚拟机 143 OH_JSVM_CloseEnvScope(env, envScope); 144 OH_JSVM_DestroyEnv(env); 145 OH_JSVM_CloseVMScope(vm, vmScope); 146 OH_JSVM_DestroyVM(vm); 147} 148 149static void RunVMSnapshot() { 150 // blobData的生命周期不能短于vm的生命周期 151 // 从文件中读取snapshot 152 std::vector<char> blobData; 153 std::ifstream file("/data/storage/el2/base/files/test_blob.bin", std::ios::in | std::ios::binary | std::ios::ate); 154 size_t blobSize = file.tellg(); 155 blobData.resize(blobSize); 156 file.seekg(0, std::ios::beg); 157 file.read(blobData.data(), blobSize); 158 file.close(); 159 OH_LOG_INFO(LOG_APP, "Test JSVM RunVMSnapshot read file blobSize = : %{public}ld", blobSize); 160 // 使用快照数据创建虚拟机实例 161 JSVM_VM vm; 162 JSVM_CreateVMOptions vmOptions; 163 memset(&vmOptions, 0, sizeof(vmOptions)); 164 vmOptions.snapshotBlobData = blobData.data(); 165 vmOptions.snapshotBlobSize = blobSize; 166 OH_JSVM_CreateVM(&vmOptions, &vm); 167 JSVM_VMScope vmScope; 168 OH_JSVM_OpenVMScope(vm, &vmScope); 169 // 从快照中创建环境env 170 JSVM_Env env; 171 OH_JSVM_CreateEnvFromSnapshot(vm, 0, &env); 172 JSVM_EnvScope envScope; 173 OH_JSVM_OpenEnvScope(env, &envScope); 174 // 执行js脚本,快照记录的env中定义了createHelloString() 175 std::string src = "createHelloString()"; 176 JSVM_Value result = RunVMScript(env, src); 177 // 环境关闭前检查脚本运行结果 178 char str[MAX_BUFFER_SIZE]; 179 OH_JSVM_GetValueStringUtf8(env, result, str, MAX_BUFFER_SIZE, nullptr); 180 if (strcmp(str, "Hello world!") !=0) { 181 OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d", __FILE__, __LINE__); 182 } 183 // 关闭并销毁环境和虚拟机 184 OH_JSVM_CloseEnvScope(env, envScope); 185 OH_JSVM_DestroyEnv(env); 186 OH_JSVM_CloseVMScope(vm, vmScope); 187 OH_JSVM_DestroyVM(vm); 188 return; 189} 190 191static JSVM_Value AdjustExternalMemory(JSVM_Env env, JSVM_CallbackInfo info) { 192 // 在创建虚拟机快照时,如果存在对外部的依赖,需要在OH_JSVM_Init时,将外部依赖注册到initOptions.externalReferences中 193 // 创建虚拟机快照并将快照保存到文件中 194 CreateVMSnapshot(); 195 // snapshot可以记录下特定的js执行环境,可以跨进程通过snapshot快速还原出js执行上下文环境 196 RunVMSnapshot(); 197 JSVM_Value result = nullptr; 198 OH_JSVM_CreateInt32(env, 0, &result); 199 return result; 200} 201 202static JSVM_CallbackStruct param[] = { 203 {.data = nullptr, .callback = AdjustExternalMemory}, 204}; 205static JSVM_CallbackStruct *method = param; 206// AdjustExternalMemory方法别名,供JS调用 207static JSVM_PropertyDescriptor descriptor[] = { 208 {"adjustExternalMemory", nullptr, method, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 209}; 210 211// 样例测试JS 212const char *srcCallNative = R"JS(adjustExternalMemory();)JS"; 213 214static int32_t TestJSVM() { 215 JSVM_InitOptions initOptions = {0}; 216 JSVM_VM vm; 217 JSVM_Env env = nullptr; 218 JSVM_VMScope vmScope; 219 JSVM_EnvScope envScope; 220 JSVM_HandleScope handleScope; 221 JSVM_Value result; 222 // 初始化JavaScript引擎实例 223 if (g_aa == 0) { 224 g_aa++; 225 initOptions.externalReferences = externals; 226 int argc = 0; 227 char **argv = nullptr; 228 initOptions.argc = &argc; 229 initOptions.argv = argv; 230 CHECK(OH_JSVM_Init(&initOptions)); 231 } 232 // 创建JSVM环境 233 CHECK(OH_JSVM_CreateVM(nullptr, &vm)); 234 CHECK(OH_JSVM_CreateEnv(vm, sizeof(descriptor) / sizeof(descriptor[0]), descriptor, &env)); 235 CHECK(OH_JSVM_OpenVMScope(vm, &vmScope)); 236 CHECK_RET(OH_JSVM_OpenEnvScope(env, &envScope)); 237 CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope)); 238 239 // 通过script调用测试函数 240 JSVM_Script script; 241 JSVM_Value jsSrc; 242 CHECK_RET(OH_JSVM_CreateStringUtf8(env, srcCallNative, JSVM_AUTO_LENGTH, &jsSrc)); 243 CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script)); 244 CHECK_RET(OH_JSVM_RunScript(env, script, &result)); 245 246 // 销毁JSVM环境 247 CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope)); 248 CHECK_RET(OH_JSVM_CloseEnvScope(env, envScope)); 249 CHECK(OH_JSVM_CloseVMScope(vm, vmScope)); 250 CHECK(OH_JSVM_DestroyEnv(env)); 251 CHECK(OH_JSVM_DestroyVM(vm)); 252 return 0; 253} 254 255static napi_value RunTest(napi_env env, napi_callback_info info) 256{ 257 TestJSVM(); 258 return nullptr; 259} 260 261EXTERN_C_START 262static napi_value Init(napi_env env, napi_value exports) { 263 OH_LOG_INFO(LOG_APP, "JSVM Init"); 264 napi_property_descriptor desc[] = {{"runTest", nullptr, RunTest, nullptr, nullptr, nullptr, napi_default, nullptr}, 265 }; 266 267 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 268 return exports; 269} 270EXTERN_C_END 271 272static napi_module demoModule = { 273 .nm_version = 1, 274 .nm_flags = 0, 275 .nm_filename = nullptr, 276 .nm_register_func = Init, 277 .nm_modname = "entry", 278 .nm_priv = ((void *)0), 279 .reserved = {0}, 280}; 281 282extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } 283``` 284 285ArkTS侧示例代码 286 287```ts 288@Entry 289@Component 290struct Index { 291 @State message: string = 'Hello World'; 292 293 build() { 294 Row() { 295 Column() { 296 Text(this.message) 297 .fontSize(50) 298 .fontWeight(FontWeight.Bold) 299 .onClick(() => { 300 // runtest 301 napitest.runTest(); 302 }) 303 } 304 .width('100%') 305 } 306 .height('100%') 307 } 308} 309``` 310执行结果 311在LOG中输出:Test JSVM RunVMSnapshot read file blobSize = : 300064 312