1# Performing Lifecycle Management Using JSVM-API 2 3## Introduction 4 5In JSVM-API, **JSVM_Value** is an abstract data type that represents a JavaScript (JS) value of any type, which includes the basic type (such as number, string, or Boolean) and the composite type (such as array, function, or object). 6The **JSVM_Value** lifecycle is closely related to the lifecycle of the JS value. When a JS value is garbage-collected, the **JSVM_Value** associated with it is no longer valid. Avoid using the **JSVM_Value** when the JS value no longer exists. 7 8Scope is used to manage the **JSVM_Value** lifecycle in the framework layer. You can use **OH_JSVM_OpenHandleScope** to create a scope and use **OH_JSVM_CloseHandleScope** to destroy a scope. By creating a **JSVM_Value** in a scope, you can ensure that the **JSVM_Value** is automatically released when the scope ends. This helps prevent memory leaks. 9 10**JSVM_Ref** is a JSVM-API data type used to manage the **JSVM_Value** lifecycle. It allows reference to a **JSVM_Value** during its lifecycle, even if the value is beyond its original context. The reference allows a **JSVM_Value** to be shared in different contexts and released in a timely manner. 11 12Properly using **OH_JSVM_OpenHandleScope** and **OH_JSVM_CloseHandleScope** can minimize the **JSVM_Value** lifecycle and prevent memory leaks. 13 14Each **JSVM_Value** belongs to a specific **HandleScope** instance, which is created by **OH_JSVM_OpenHandleScope** and closed by **OH_JSVM_CloseHandleScope**. After a **HandleScope** instance is closed, the corresponding **JSVM_Value** will be automatically released. 15 16## Basic Concepts 17 18JSVM-API provides APIs for creating and manipulating JS objects, managing references to and lifecycle of the JS objects, and registering garbage collection (GC) callbacks in C/C++. Before you get started, you need to understand the following concepts: 19 20- Scope: used to ensure that the objects created within a certain scope remain active and are properly cleared when no longer required. JSVM-API provides APIs for creating and closing normal and escapable scopes. 21- Reference management: JSVM-API provides APIs for creating, deleting, and managing object references to extend the object lifecycle and prevent memory leaks when objects are used. 22- Escapable scope: used to return the values created within the **escapable_handle_scope** to a parent scope. It is created by **OH_JSVM_OpenEscapableHandleScope** and closed by **OH_JSVM_CloseEscapableHandleScope**. 23- GC callback: You can register GC callbacks to perform specific cleanup operations when JS objects are garbage-collected. 24 25Understanding these concepts helps you securely and effectively manipulate JS objects in C/C++ and perform object lifecycle management. 26 27## Available APIs 28 29| API | Description | 30|----------------------------|--------------------------------| 31| OH_JSVM_OpenHandleScope | Opens a handle scope. **JSVM_Value** within the scope will not be garbage-collected.| 32| OH_JSVM_CloseHandleScope | Closes a handle scope.| 33| OH_JSVM_OpenEscapableHandleScope | Opens an escapable handle scope. Before this scope is closed, the object created within the scope has the same lifecycle as its parent scope.| 34| OH_JSVM_CloseEscapableHandleScope | Closes an escapable handle scope.| 35| OH_JSVM_EscapeHandle | Promotes a handle to a JS object so that it is valid for the lifetime of the outer scope.| 36| OH_JSVM_CreateReference | Creates a reference with the specified reference count to the value passed in. The reference allows objects to be used and shared in different contexts and helps effective track of the object lifecycle.| 37| OH_JSVM_DeleteReference | Deletes the reference created by **OH_JSVM_CreateReference**. This allows objects to be correctly released and reclaimed when they are no longer required, avoiding memory leaks.| 38| OH_JSVM_ReferenceRef | Increments the reference count of the reference created by **OH_JSVM_CreateReference** so that the object referenced will not be released.| 39| OH_JSVM_ReferenceUnref | Decrements the reference count of the reference created by **OH_JSVM_CreateReference** so that the object can be correctly released and reclaimed when it is not referenced.| 40| OH_JSVM_GetReferenceValue | Obtains the object referenced by **OH_JSVM_CreateReference**. | 41| OH_JSVM_AddFinalizer | Adds a **JSVM_Finalize** callback to a JS object. The callback will be invoked to release the native object when the JS object is garbage-collected.| 42 43## Example 44 45If 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 lifecycle management. 46 47### OH_JSVM_OpenHandleScope and OH_JSVM_CloseHandleScope 48 49Call **OH_JSVM_OpenHandleScope** to open a handle scope. Call **OH_JSVM_CloseHandleScope** to close a handle scope. Properly managing JS handle scopes can prevent GC problems. 50 51CPP code: 52 53```cpp 54// Define OH_JSVM_OpenHandleScope and OH_JSVM_CloseHandleScope. 55static JSVM_Value HandleScopeFor(JSVM_Env env, JSVM_CallbackInfo info) { 56 // When JSVM-API is frequently called to create JS objects in the for loop, use handle_scope to release resources in a timely manner when they are no longer required. 57 // In the following example, the lifecycle of the local variable res ends at the end of each loop. To prevent memory leaks, scope is used to release the JS object in a timely manner. 58 constexpr uint32_t DIFF_VALUE_HUNDRED_THOUSAND = 10000; 59 JSVM_Value checked = nullptr; 60 for (int i = 0; i < DIFF_VALUE_HUNDRED_THOUSAND; i++) { 61 JSVM_HandleScope scope = nullptr; 62 JSVM_Status status = OH_JSVM_OpenHandleScope(env, &scope); 63 if (status != JSVM_OK || scope == nullptr) { 64 OH_JSVM_GetBoolean(env, false, &checked); 65 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_OpenHandleScope: failed"); 66 return checked; 67 } 68 JSVM_Value res = nullptr; 69 OH_JSVM_CreateObject(env, &res); 70 status = OH_JSVM_CloseHandleScope(env, scope); 71 if (status != JSVM_OK) { 72 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_CloseHandleScope: failed"); 73 } 74 } 75 OH_JSVM_GetBoolean(env, true, &checked); 76 OH_LOG_INFO(LOG_APP, "JSVM HandleScopeFor: success"); 77 return checked; 78} 79 80// Register the HandleScopeFor callback. 81static JSVM_CallbackStruct param[] = { 82 {.callback = HandleScopeFor, .data = nullptr}, 83}; 84 85static JSVM_CallbackStruct *method = param; 86// Alias for the HandleScopeFor method, which can be called from JS. 87static JSVM_PropertyDescriptor descriptor[] = { 88 {"HandleScopeFor", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 89}; 90 91const char *srcCallNative = "HandleScopeFor()"; 92``` 93 94Expected result: 95``` 96JSVM HandleScopeFor: success 97``` 98 99### OH_JSVM_OpenEscapableHandleScope, OH_JSVM_CloseEscapableHandleScope, and OH_JSVM_EscapeHandle 100 101Call **OH_JSVM_OpenEscapableHandleScope** to create an escapable handle scope, which allows the declared values in a scope to be returned to its parent scope. <br>Call **OH_JSVM_CloseEscapableHandleScope** to close the created scope.<br>Call **OH_JSVM_EscapeHandle** to promote the lifecycle of the passed-in JS object to its parent scope. 102These APIs are helpful for managing JS objects more flexibly in C/C++, especially when passing cross-scope values. 103 104CPP code: 105 106```cpp 107// Define OH_JSVM_OpenEscapableHandleScope, OH_JSVM_CloseEscapableHandleScope, and OH_JSVM_EscapeHandle. 108static JSVM_Value EscapableHandleScopeTest(JSVM_Env env, JSVM_CallbackInfo info) 109{ 110 // Create an escapable handle scope. 111 JSVM_EscapableHandleScope scope = nullptr; 112 JSVM_Status status = OH_JSVM_OpenEscapableHandleScope(env, &scope); 113 if (status != JSVM_OK) { 114 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_OpenEscapableHandleScope: failed"); 115 return nullptr; 116 } 117 // Create an object within the scope of the escapable handle. 118 JSVM_Value obj; 119 OH_JSVM_CreateObject(env, &obj); 120 // Add properties to the object. 121 JSVM_Value value = nullptr; 122 OH_JSVM_CreateStringUtf8(env, "Test jsvm_escapable_handle_scope", JSVM_AUTO_LENGTH, &value); 123 OH_JSVM_SetNamedProperty(env, obj, "name", value); 124 // Call OH_JSVM_EscapeHandle to promote the JS object handle to make it valid with the lifetime of the outer scope. 125 JSVM_Value escapedObj = nullptr; 126 OH_JSVM_EscapeHandle(env, scope, obj, &escapedObj); 127 // Close the escapable handle scope to clear resources. 128 status = OH_JSVM_CloseEscapableHandleScope(env, scope); 129 if (status != JSVM_OK) { 130 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_CloseEscapableHandleScope: failed"); 131 return nullptr; 132 } 133 // Here, escapedObj can be used in the outer scope. 134 bool result = false; 135 OH_JSVM_CreateStringUtf8(env, "name", JSVM_AUTO_LENGTH, &value); 136 OH_JSVM_HasProperty(env, escapedObj, value, &result); 137 if (result) { 138 OH_LOG_INFO(LOG_APP, "JSVM EscapableHandleScopeTest: success"); 139 } 140 return escapedObj; 141} 142 143// Define the EscapableHandleScopeTest callback. 144static JSVM_CallbackStruct param[] = { 145 {.callback = EscapableHandleScopeTest, .data = nullptr}, 146}; 147static JSVM_CallbackStruct *method = param; 148// Alias for the escapableHandleScopeTest method, which can be called from JS. 149static JSVM_PropertyDescriptor descriptor[] = { 150 {"escapableHandleScopeTest", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 151}; 152 153const char *srcCallNative = "escapableHandleScopeTest()"; 154``` 155 156Expected result: 157 158``` 159JSVM EscapableHandleScopeTest: success 160``` 161 162### OH_JSVM_CreateReference, OH_JSVM_DeleteReference, and OH_JSVM_GetReferenceValue 163 164Call **OH_JSVM_CreateReference** to create a reference for a JS variable to extend its lifecycle. 165Call **OH_JSVM_GetReferenceValue** to obtain the JS variable associated with the reference. 166Call **OH_JSVM_DeleteReference** to delete the reference. 167 168The caller must manage the reference lifecycle. During the reference validity period, the JS variable will not be garbage-collected. 169 170### OH_JSVM_ReferenceRef and OH_JSVM_ReferenceUnref 171 172Call **OH_JSVM_ReferenceRef** to increment the reference count of a reference and call **OH_JSVM_ReferenceUnref** to decrement the reference count of a reference, and return the new count value. 173When the reference count is **0**: 174- For the JS types that can be set as weak references (objects, functions, and external variables), the reference will be set as a weak reference. The associated variable will be garbage-collected when the GC mechanism deems it necessary. After the variable is garbage-collected, calling **OH_JSVM_GetReferenceValue** will return JS **NULL**. 175- For the JS types that cannot be set as weak references, the reference will be cleared and calling **OH_JSVM_GetReferenceValue** will return JS **NULL**. 176 177CPP code: 178 179```cpp 180static JSVM_Value UseReference(JSVM_Env env, JSVM_CallbackInfo info) 181{ 182 // Create a JS object. 183 JSVM_Value obj = nullptr; 184 OH_JSVM_CreateObject(env, &obj); 185 JSVM_Value value = nullptr; 186 OH_JSVM_CreateStringUtf8(env, "UseReference", JSVM_AUTO_LENGTH, &value); 187 OH_JSVM_SetNamedProperty(env, obj, "name", value); 188 189 JSVM_Ref g_ref = nullptr; 190 // Create a reference to the JS object. 191 JSVM_Status status = OH_JSVM_CreateReference(env, obj, 1, &g_ref); 192 if (status != JSVM_OK) { 193 return nullptr; 194 } 195 196 // Increment the reference count and return the new reference count. 197 uint32_t result; 198 OH_JSVM_ReferenceRef(env, g_ref, &result); 199 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_ReferenceRef, count = %{public}d.", result); 200 if (result != 2) { 201 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_ReferenceRef: failed"); 202 return nullptr; 203 } 204 205 // Decrement the reference count and return the new reference count. 206 uint32_t num; 207 OH_JSVM_ReferenceUnref(env, g_ref, &num); 208 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_ReferenceUnref, count = %{public}d.", num); 209 if (num != 1) { 210 return nullptr; 211 } 212 213 JSVM_Value object = nullptr; 214 // Call OH_JSVM_GetReferenceValue to obtain the referenced JS object. 215 status = OH_JSVM_GetReferenceValue(env, g_ref, &object); 216 if (status != JSVM_OK) { 217 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_GetReferenceValue: failed"); 218 return nullptr; 219 } 220 221 // When the reference is no longer required, call OH_JSVM_DeleteReference to delete it. 222 status = OH_JSVM_DeleteReference(env, g_ref); 223 if (status != JSVM_OK) { 224 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_DeleteReference: failed"); 225 return nullptr; 226 } 227 228 // Return the obtained object. 229 OH_LOG_INFO(LOG_APP, "JSVM UseReference success"); 230 return object; 231} 232 233// Register the CreateReference, UseReference, and DeleteReference callbacks. 234static JSVM_CallbackStruct param[] = { 235 {.callback = UseReference, .data = nullptr}, 236}; 237static JSVM_CallbackStruct *method = param; 238// Aliases for the CreateReference, UseReference, and DeleteReference methods, which cal be called from JS. 239static JSVM_PropertyDescriptor descriptor[] = { 240 {"useReference", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 241}; 242 243const char *srcCallNative = "useReference()"; 244``` 245 246Expected result: 247 248``` 249JSVM OH_JSVM_ReferenceRef, count = 2. 250JSVM OH_JSVM_ReferenceUnref, count = 1. 251JSVM UseReference success 252``` 253 254### OH_JSVM_AddFinalizer 255Call **OH_JSVM_AddFinalizer** to add the **JSVM_Finalize** callback to a JS object. The callback will be invoked when the JS object is garbage-collected. **OH_JSVM_AddFinalizer** is usually used to release the native object associated with a JS object. If the input parameter is not a JS object, the call will fail and return an error code. 256The Finalizer method cannot be canceled after being registered. If it is not executed before **OH_JSVM_DestroyEnv** is called, it will be executed when **OH_JVSM_DestroyEnv** is called. 257 258CPP code: 259 260```cpp 261static int AddFinalizer(JSVM_VM vm, JSVM_Env env) { 262 // Open the handle scope. 263 JSVM_HandleScope handleScope; 264 CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope)); 265 // Create an object and set a callback. 266 JSVM_Value obj; 267 CHECK_RET(OH_JSVM_CreateObject(env, &obj)); 268 CHECK_RET(OH_JSVM_AddFinalizer( 269 env, obj, nullptr, 270 [](JSVM_Env env, void *data, void *hint) -> void { 271 // Finalizer method, which can be used to clear the native object. 272 OH_LOG_INFO(LOG_APP, "JSVM: finalizer called."); 273 }, 274 nullptr, nullptr)); 275 OH_LOG_INFO(LOG_APP, "JSVM: finalizer added."); 276 // Close the handle scope to trigger GC. The Finalizer callback will be called during GC. 277 CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope)); 278 OH_LOG_INFO(LOG_APP, "JSVM: before call gc."); 279 CHECK_RET(OH_JSVM_MemoryPressureNotification(env, JSVM_MemoryPressureLevel::JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL)); 280 OH_LOG_INFO(LOG_APP, "JSVM: after call gc."); 281 282 return 0; 283} 284 285static JSVM_Value RunDemo(JSVM_Env env, JSVM_CallbackInfo info) { 286 JSVM_VM vm; 287 OH_JSVM_GetVM(env, &vm); 288 if (AddFinalizer(vm, env) != 0) { 289 OH_LOG_INFO(LOG_APP, "Run PromiseRegisterHandler failed"); 290 } 291 292 return nullptr; 293} 294 295// Register the RunDemo callback. 296static JSVM_CallbackStruct param[] = { 297 {.data = nullptr, .callback = RunDemo}, 298}; 299static JSVM_CallbackStruct *method = param; 300// Alias for the RunDemo method to be called from JS. 301static JSVM_PropertyDescriptor descriptor[] = { 302 {"RunDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 303}; 304 305// Call C++ code from JS. 306const char *srcCallNative = R"JS(RunDemo();)JS"; 307``` 308 309Expected result: 310```ts 311JSVM: finalizer added. 312JSVM: before call gc. 313JSVM: finalizer called. 314JSVM: after call gc. 315``` 316