使用JSVM-API接口进行生命周期相关开发
======================

#### 简介

在JSVM-API中，JSVM\_Value是一个表示JavaScript值的抽象类型，它可以表示任何JavaScript值，包括基本类型（如数字、字符串、布尔值）和对象类型（如数组、函数、对象等）。

JSVM\_Value的生命周期与JavaScript值的生命周期相关。JavaScript值被垃圾回收后，JSVM\_Value将不再有效。避免在JavaScript值不存在时使用JSVM\_Value。

框架层的scope通常用于管理JSVM\_Value的生命周期。在JSVM-API中，可以使用OH\_JSVM\_OpenHandleScope和OH\_JSVM\_CloseHandleScope函数来创建和销毁scope。通过在scope内创建JSVM\_Value，可以确保在scope结束时自动释放JSVM\_Value，避免内存泄漏。

JSVM\_Ref是一个JSVM-API类型，用于管理JSVM\_Value的生命周期。JSVM\_Ref允许您在JSVM\_Value的生命周期内保持对其的引用，即使它已经超出了其原始上下文的范围。这使得您可以在不同的上下文中共享JSVM\_Value，并确保在不再需要时正确释放其内存。

合理使用OH\_JSVM\_OpenHandleScope和OH\_JSVM\_CloseHandleScope管理JSVM\_Value的生命周期，避免发生内存泄漏问题。

每个JSVM\_Value属于特定的HandleScope，HandleScope通过OH\_JSVM\_OpenHandleScope和OH\_JSVM\_CloseHandleScope来建立和关闭，HandleScope关闭后，所属的JSVM\_Value就会自动释放。

#### 基本概念

JSVM-API提供了一组功能，使开发人员能够在JSVM-API模块中创建和操作JavaScript对象，管理引用和生命周期，并注册垃圾回收回调函数等。下面是一些基本概念：

*   **作用域**：用于创建一个范围，在范围内声明的引用在范围外部将不再生效。JSVM-API提供了创建、关闭普通和可逃逸的作用域的函数。
*   **引用管理**：JSVM-API提供函数来创建、删除和管理对象的引用，以延长对象的生命周期，并避免在使用对象时发生内存泄漏。
*   **可逃逸的作用域**：允许在创建的作用域中声明的对象返回到父作用域，通过OH\_JSVM\_OpenEscapableHandleScope和OH\_JSVM\_CloseEscapableHandleScope进行管理。
*   **垃圾回收回调**：允许注册回调函数，以便在JavaScript对象被垃圾回收时执行特定的清理操作。

这些基本概念使开发人员安全且有效地操作JavaScript对象，并确保正确管理对象的生命周期。

#### 接口说明

| 接口 | 功能说明 |
| :-- | :-- |
| OH\_JSVM\_OpenHandleScope | 打开一个Handle scope，确保scope范围内的JSVM\_Value不被GC回收。 |
| OH\_JSVM\_CloseHandleScope | 关闭Handle scope。 |
| OH\_JSVM\_OpenEscapableHandleScope | 打开一个新的scope逃逸Handle scope，在关闭该scope之前创建的对象与父作用域有相同的生命周期。 |
| OH\_JSVM\_CloseEscapableHandleScope | 关闭一个scope，在此scope范围外创建的对象不受父作用域保护。 |
| OH\_JSVM\_EscapeHandle | 将JavaScript对象的句柄提升到外部作用域，确保在外部作用域中可以持续地使用该对象。 |
| OH\_JSVM\_CreateReference | 以指定的引用计数为JavaScript对象创建一个新的引用，该引用将指向传入的对象，引用允许在不同的上下文中使用和共享对象，并且可以有效地跟踪对象的生命周期。 |
| OH\_JSVM\_DeleteReference | 释放由OH\_JSVM\_CreateReference创建的引用，确保对象在不再被使用时能够被正确地释放和回收，避免内存泄漏。 |
| OH\_JSVM\_ReferenceRef | 增加由OH\_JSVM\_CreateReference创建的引用的引用计数，以确保对象在有引用时不会被提前释放。 |
| OH\_JSVM\_ReferenceUnref | 减少引用计数，用于管理引用计数。 |
| OH\_JSVM\_GetReferenceValue | 减少由OH\_JSVM\_CreateReference创建的引用的引用计数，以确保没有任何引用指向该对象时能正确地释放和回收。 |
| OH\_JSVM\_AddFinalizer | 为对象添加JSVM\_Finalize回调，以便在JavaScript对象被垃圾回收时调用来释放原生对象。 |

#### 使用示例

JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-jsvm-process)，本文仅展示接口对应的C++代码。

#### \[h2\]OH\_JSVM\_OpenHandleScope、OH\_JSVM\_CloseHandleScope

通过接口OH\_JSVM\_OpenHandleScope创建上下文环境，并使用OH\_JSVM\_CloseHandleScope关闭。这用于管理JavaScript对象的生命周期，确保在JSVM-API模块中正确处理JavaScript对象句柄，避免垃圾回收问题。

cpp 部分代码：

// OH\_JSVM\_OpenHandleScope、OH\_JSVM\_CloseHandleScope的三种样例方法
static JSVM\_Value HandleScopeFor(JSVM\_Env env, JSVM\_CallbackInfo info) {
    // 在for循环中频繁调用JSVM接口创建js对象时，要加handle\_scope及时释放不再使用的资源。
    // 下面例子中，每次循环结束局部变量res的生命周期已结束，因此加scope及时释放其持有的js对象，防止内存泄漏
    constexpr uint32\_t DIFF\_VALUE\_TEN\_THOUSAND = 10000;
    JSVM\_Value checked = nullptr;
    for (int i = 0; i < DIFF\_VALUE\_TEN\_THOUSAND; i++) {
        JSVM\_HandleScope scope = nullptr;
        JSVM\_Status status = OH\_JSVM\_OpenHandleScope(env, &scope);
        if (status != JSVM\_OK || scope == nullptr) {
            OH\_JSVM\_GetBoolean(env, false, &checked);
            OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_OpenHandleScope: failed");
            return checked;
        }
        JSVM\_Value res = nullptr;
        OH\_JSVM\_CreateObject(env, &res);
        status = OH\_JSVM\_CloseHandleScope(env, scope);
        if (status != JSVM\_OK) {
            OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_CloseHandleScope: failed");
        }
    }
    OH\_JSVM\_GetBoolean(env, true, &checked);
    OH\_LOG\_INFO(LOG\_APP, "JSVM HandleScopeFor: success");
    return checked;
}

// HandleScopeFor注册回调
static JSVM\_CallbackStruct param\[\] = {
    {.callback = HandleScopeFor, .data = nullptr},
};

static JSVM\_CallbackStruct \*method = param;
// HandleScopeFor方法别名，供JS调用
static JSVM\_PropertyDescriptor descriptor\[\] = {
    {"HandleScopeFor", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
};

const char \*srcCallNative = "HandleScopeFor()";

预期输出

JSVM HandleScopeFor: success

#### \[h2\]OH\_JSVM\_OpenEscapableHandleScope、OH\_JSVM\_CloseEscapableHandleScope、OH\_JSVM\_EscapeHandle

通过接口 OH\_JSVM\_OpenEscapableHandleScope 创建出一个可逃逸的 handle scope，可将 1 个范围内声明的值返回到父作用域。创建的 scope 需使用 OH\_JSVM\_CloseEscapableHandleScope 进行关闭。OH\_JSVM\_EscapeHandle 将传入的 JavaScript 对象的生命周期提升到其父作用域。

通过上述接口可以更灵活的使用管理传入的 JavaScript 对象，特别是在处理跨作用域的值传递时非常有用。

cpp 部分代码：

// OH\_JSVM\_OpenEscapableHandleScope、OH\_JSVM\_CloseEscapableHandleScope、OH\_JSVM\_EscapeHandle的样例方法
static JSVM\_Value EscapableHandleScopeTest(JSVM\_Env env, JSVM\_CallbackInfo info)
{
    // 创建一个可逃逸的句柄作用域
    JSVM\_EscapableHandleScope scope = nullptr;
    JSVM\_Status status = OH\_JSVM\_OpenEscapableHandleScope(env, &scope);
    if (status != JSVM\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_OpenEscapableHandleScope: failed");
        return nullptr;
    }
    // 在可逃逸的句柄作用域内创建一个obj
    JSVM\_Value obj = nullptr;
    OH\_JSVM\_CreateObject(env, &obj);
    // 在对象中添加属性
    JSVM\_Value value = nullptr;
    OH\_JSVM\_CreateStringUtf8(env, "Test jsvm\_escapable\_handle\_scope", JSVM\_AUTO\_LENGTH, &value);
    OH\_JSVM\_SetNamedProperty(env, obj, "name", value);
    // 调用OH\_JSVM\_EscapeHandle将对象逃逸到作用域之外
    JSVM\_Value escapedObj = nullptr;
    OH\_JSVM\_EscapeHandle(env, scope, obj, &escapedObj);
    // 关闭可逃逸的句柄作用域，清理资源
    status = OH\_JSVM\_CloseEscapableHandleScope(env, scope);
    if (status != JSVM\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_CloseEscapableHandleScope: failed");
        return nullptr;
    }
    // 此时的escapedObj已逃逸，可以在作用域外继续使用escapedObj
    bool result = false;
    OH\_JSVM\_CreateStringUtf8(env, "name", JSVM\_AUTO\_LENGTH, &value);
    OH\_JSVM\_HasProperty(env, escapedObj, value, &result);
    if (result) {
        OH\_LOG\_INFO(LOG\_APP, "JSVM EscapableHandleScopeTest: success");
    }
    return escapedObj;
}

// EscapableHandleScopeTest注册回调
static JSVM\_CallbackStruct param\[\] = {
    {.callback = EscapableHandleScopeTest, .data = nullptr},
};
static JSVM\_CallbackStruct \*method = param;
// EscapableHandleScopeTest方法别名，供JS调用
static JSVM\_PropertyDescriptor descriptor\[\] = {
    {"escapableHandleScopeTest", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
};

const char \*srcCallNative = "escapableHandleScopeTest()";

预期输出

JSVM EscapableHandleScopeTest: success

#### \[h2\]OH\_JSVM\_CreateReference、OH\_JSVM\_DeleteReference、OH\_JSVM\_GetReferenceValue

1.  调用 OH\_JSVM\_CreateReference 为 JavaScript 变量创建一个引用，以延长其生命周期。
2.  调用 OH\_JSVM\_GetReferenceValue 获取与引用关联的 JavaScript 变量。
3.  调用 OH\_JSVM\_DeleteReference 删除传入的引用。

调用者需要自己管理引用生命周期，引用有效期间 JavaScript 变量不会被垃圾回收处理。

#### \[h2\]OH\_JSVM\_ReferenceRef、OH\_JSVM\_ReferenceUnref

增加/减少传入的引用的引用计数，并获取新的计数。当引用计数被置为 0 后，对于可以被设置为弱引用的 JavaScript 类型（对象、函数、外部变量），引用将被置为弱引用，在垃圾回收机制认为必要的时候该变量会被回收，当变量被回收后，调用 OH\_JSVM\_GetReferenceValue 会获得 C NULL；对于不可被置为弱引用的 JavaScript 类型，该引用会被清除，调用 OH\_JSVM\_GetReferenceValue 会获得 C NULL。

cpp 部分代码：

static JSVM\_Value UseReference(JSVM\_Env env, JSVM\_CallbackInfo info)
{
    // 创建 JavaScript 对象
    JSVM\_Value obj = nullptr;
    OH\_JSVM\_CreateObject(env, &obj);
    JSVM\_Value value = nullptr;
    OH\_JSVM\_CreateStringUtf8(env, "UseReference", JSVM\_AUTO\_LENGTH, &value);
    OH\_JSVM\_SetNamedProperty(env, obj, "name", value);
    
    JSVM\_Ref g\_ref = nullptr;
    // 创建对JavaScript对象的引用
    JSVM\_Status status = OH\_JSVM\_CreateReference(env, obj, 1, &g\_ref);
    if (status != JSVM\_OK) {
        return nullptr;
    }

    // 增加传入引用的引用计数并返回生成的引用计数
    uint32\_t result = 0u;
    OH\_JSVM\_ReferenceRef(env, g\_ref, &result);
    OH\_LOG\_INFO(LOG\_APP, "JSVM OH\_JSVM\_ReferenceRef, count = %{public}d.", result);
    if (result != 2) {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_ReferenceRef: failed");
        return nullptr;
    }

    // 减少传入引用的引用计数并返回生成的引用计数
    uint32\_t num = 0u;
    OH\_JSVM\_ReferenceUnref(env, g\_ref, &num);
    OH\_LOG\_INFO(LOG\_APP, "JSVM OH\_JSVM\_ReferenceUnref, count = %{public}d.", num);
    if (num != 1) {
        return nullptr;
    }

    JSVM\_Value object = nullptr;
    // 通过调用OH\_JSVM\_GetReferenceValue获取引用的JavaScript对象
    status = OH\_JSVM\_GetReferenceValue(env, g\_ref, &object);
    if (status != JSVM\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_GetReferenceValue: failed");
        return nullptr;
    }

    // 不再使用引用，通过调用OH\_JSVM\_DeleteReference删除对JavaScript对象的引用
    status = OH\_JSVM\_DeleteReference(env, g\_ref);
    if (status != JSVM\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM OH\_JSVM\_DeleteReference: failed");
        return nullptr;
    }

    // 将获取到的对象返回
    OH\_LOG\_INFO(LOG\_APP, "JSVM UseReference success");
    return object;
}

// CreateReference、UseReference、DeleteReference注册回调
static JSVM\_CallbackStruct param\[\] = {
    {.callback = UseReference, .data = nullptr},
};
static JSVM\_CallbackStruct \*method = param;
// CreateReference、UseReference、DeleteReference方法别名，供JS调用
static JSVM\_PropertyDescriptor descriptor\[\] = {
    {"useReference", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
};

const char \*srcCallNative = "useReference()";

预期结果：

JSVM OH\_JSVM\_ReferenceRef, count = 2.
JSVM OH\_JSVM\_ReferenceUnref, count = 1.
JSVM UseReference success

#### \[h2\]OH\_JSVM\_AddFinalizer

为 JavaScript 对象添加 JSVM\_Finalize 回调，当 JavaScript 对象被垃圾回收时执行函数回调，该接口通常被用于释放与 JavaScript 对象相关的原生对象。如果传入的参数类型不是 JavaScript 对象，该接口调用失败并返回错误码。

Finalizer 方法被注册后无法取消，如果在调用 OH\_JSVM\_DestroyEnv 前均未被执行，则在 OH\_JSVM\_DestroyEnv 时执行。

cpp 部分代码：

static int AddFinalizer(JSVM\_VM vm, JSVM\_Env env) {
    // 打开 handlescope
    JSVM\_HandleScope handleScope;
    CHECK\_RET(OH\_JSVM\_OpenHandleScope(env, &handleScope));
    // 创建 object 并设置回调
    JSVM\_Value obj = nullptr;
    CHECK\_RET(OH\_JSVM\_CreateObject(env, &obj));
    CHECK\_RET(OH\_JSVM\_AddFinalizer(
        env, obj, nullptr,
        \[\](JSVM\_Env env, void \*data, void \*hint) -> void {
            // Finalizer 方法，可在该方法中清理 Native 对象
            OH\_LOG\_INFO(LOG\_APP, "JSVM: finalizer called.");
        },
        nullptr, nullptr));
    OH\_LOG\_INFO(LOG\_APP, "JSVM: finalizer added.");
    // 关闭 handlescope，触发 GC，GC 时 Finalizer 会被调用
    CHECK\_RET(OH\_JSVM\_CloseHandleScope(env, handleScope));
    OH\_LOG\_INFO(LOG\_APP, "JSVM: before call gc.");
    CHECK\_RET(OH\_JSVM\_MemoryPressureNotification(env, JSVM\_MemoryPressureLevel::JSVM\_MEMORY\_PRESSURE\_LEVEL\_CRITICAL));
    OH\_LOG\_INFO(LOG\_APP, "JSVM: after call gc.");

    return 0;
}

static JSVM\_Value RunDemo(JSVM\_Env env, JSVM\_CallbackInfo info) {
    JSVM\_VM vm;
    OH\_JSVM\_GetVM(env, &vm);
    if (AddFinalizer(vm, env) != 0) {
        OH\_LOG\_INFO(LOG\_APP, "Run PromiseRegisterHandler failed");
    }

    return nullptr;
}

// RunDemo注册回调
static JSVM\_CallbackStruct param\[\] = {
    {.data = nullptr, .callback = RunDemo},
};
static JSVM\_CallbackStruct \*method = param;
// RunDemo方法别名，供JS调用
static JSVM\_PropertyDescriptor descriptor\[\] = {
    {"RunDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM\_DEFAULT},
};

// 样例测试js
const char \*srcCallNative = R"JS(RunDemo();)JS";

预期结果：

JSVM: finalizer added.
JSVM: before call gc.
JSVM: finalizer called.
JSVM: after call gc.