1# Working with VM Snapshots Using JSVM-API 2 3## Introduction 4 5A JavaScript virtual machine (JSVM) snapshot records the state of a JSVM at a particular time point. The snapshot file contains information, such as the current heap memory, execution context, and function closure. 6 7## Basic Concepts 8 9- VM startup snapshot: a snapshot of the VM status at a specific time, including all internal status and data of the VM. The snapshot can be used to quickly restore the VM to the state it was when the snapshot was created. 10 11It helps simplify complex programming tasks and shorten the creation time of a JS context, making the application more efficient and stable. 12 13## Available APIs 14 15| API | Description | 16|----------------------------|-------------------------------| 17| OH_JSVM_CreateSnapshot | Creates a VM startup snapshot. | 18|OH_JSVM_CreateEnvFromSnapshot| Creates an environment based on the start snapshot of a VM.| 19## Example 20 21### OH_JSVM_CreateSnapshot & OH_JSVM_CreateEnvFromSnapshot 22 23Use **OH_JSVM_CreateSnapshot** to create a VM startup snapshot. 24 25CPP code: 26 27**NOTE**<br>Register the external dependencies of the JSVM with **initOptions.externalReferences** in **OH_JSVM_Init**. 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// Call theCall and check whether the return value is JSVM_OK. 63// If no, call GET_AND_THROW_LAST_ERROR to process the error and return 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// Simplified version of JSVM_CALL_BASE, which returns nullptr. 77#define JSVM_CALL(theCall) JSVM_CALL_BASE(env, theCall, nullptr) 78 79static const int MAX_BUFFER_SIZE = 128; 80// Allow the JSVM to call the CreateHelloString() function when needs, using the callback struct and external references. 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// Enable the JSVM to call the bound function through external references. 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 // Open the handle scope. 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 // Compile the JS code. 101 JSVM_Script script; 102 OH_JSVM_CompileScript(env, jsStr, nullptr, 0, true, nullptr, &script); 103 // Execute the JS code. 104 JSVM_Value result = nullptr; 105 OH_JSVM_RunScript(env, script, &result); 106 // Close the handle scope. 107 OH_JSVM_CloseHandleScope(env, handleScope); 108 return result; 109} 110// Define OH_JSVM_CreateSnapshot. 111static void CreateVMSnapshot() { 112 // Create a JSVM instance and open the VM scope. 113 JSVM_VM vm; 114 JSVM_CreateVMOptions vmOptions; 115 memset(&vmOptions, 0, sizeof(vmOptions)); 116 // Use isForSnapshotting to set whether the VM is used for creating snapshots. 117 vmOptions.isForSnapshotting = true; 118 OH_JSVM_CreateVM(&vmOptions, &vm); 119 JSVM_VMScope vmScope; 120 OH_JSVM_OpenVMScope(vm, &vmScope); 121 // Create a JS environment and open the environment scope. 122 JSVM_Env env; 123 // Register the native function as a method that can be called from JS. 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 // Use OH_JSVM_CreateSnapshot to create a VM startup snapshot. 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 // Save the snapshot to a file. 136 // Save the snapshot data to the /data/storage/el2/base/files/test_blob.bin directory, which is a sandbox directory. 137 // For example, the bundle name is com.example.jsvm. The snapshot file is saved in /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 // Close and destroy the environment and the VM. 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 // The lifespan of blobData cannot be shorter than that of the VM. 151 // Read the snapshot from the file. 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 // Use the snapshot data to create a VM instance. 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 // Create an environment from the snapshot. 170 JSVM_Env env; 171 OH_JSVM_CreateEnvFromSnapshot(vm, 0, &env); 172 JSVM_EnvScope envScope; 173 OH_JSVM_OpenEnvScope(env, &envScope); 174 // Execute the JS script. createHelloString() is defined in env of the snapshot record. 175 std::string src = "createHelloString()"; 176 JSVM_Value result = RunVMScript(env, src); 177 // Check the script execution result before closing the environment. 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 // Close and destroy the environment and the VM. 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 // If external dependencies exist when creating a VM snapshot, register the external dependencies with initOptions.externalReferences in OH_JSVM_Init. 193 // Create a VM snapshot and save it to a file. 194 CreateVMSnapshot(); 195 // The snapshot records the specific JS execution environment and can be used to quickly restore the JS execution context environment across processes. 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// Set a property descriptor named adjustExternalMemory and associate it with a callback. This allows the AdjustExternalMemory callback to be called from JS. 207static JSVM_PropertyDescriptor descriptor[] = { 208 {"adjustExternalMemory", nullptr, method, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 209}; 210 211// Call the C++ code from 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 // Initialize the JSVM instance. 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 // Create a JSVM environment. 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 // Call the test function through the 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 // Destroy the JSVM environment. 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 code: 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**Expected output**: 311Output in the log:<br>Test JSVM RunVMSnapshot read file blobSize =: 300064 312