使用JSVM-API接口创建多个引擎执行JS代码并销毁
===========================

#### 场景介绍

开发者通过createJsCore方法来创建一个新的JS运行时环境，并通过该方法获得一个CoreID。然后，通过evaluateJS方法使用CoreID对应的运行环境来运行JS代码，在JS代码中创建promise并异步执行函数。最后，使用releaseJsCore方法来销毁CoreID对应的运行环境。

#### 使用示例

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

创建多个JS运行时环境并运行JS代码

#include <map>
#include <mutex>
#include <deque>
using namespace std;
// 定义map管理每个独立vm环境
static map<int, JSVM\_VM \*> g\_vmMap;
static map<int, JSVM\_Env \*> g\_envMap;
static map<int, JSVM\_CallbackStruct \*> g\_callBackStructMap;
static uint32\_t ENVTAG\_NUMBER = 0;
static std::mutex envMapLock;
static int g\_aa = 0;

#define CHECK\_COND(cond)                                                                                               \\
    do {                                                                                                               \\
        if (!(cond)) {                                                                                                 \\
            OH\_LOG\_ERROR(LOG\_APP, "jsvm fail file: %{public}s line: %{public}d ret = false", \_\_FILE\_\_, \_\_LINE\_\_);      \\
            return -1;                                                                                                 \\
        }                                                                                                              \\
    } while (0)

class Task {
public:
    virtual ~Task() = default;
    virtual void Run() = 0;
};
static map<int, deque<Task \*>> g\_taskQueueMap;

// 自定义ConsoleInfo方法
static JSVM\_Value ConsoleInfo(JSVM\_Env env, JSVM\_CallbackInfo info) {
    size\_t argc = 1;
    JSVM\_Value args\[1\];
    char log\[256\] = "";
    size\_t log\_length = 0;
    JSVM\_CALL(OH\_JSVM\_GetCbInfo(env, info, &argc, args, NULL, NULL));

    JSVM\_CALL(OH\_JSVM\_GetValueStringUtf8(env, args\[0\], log, 255, &log\_length));
    log\[255\] = 0;
    OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST: %{public}s", log);
    return nullptr;
}

// 自定义创建Promise方法用以在JS代码中创建Promise
static JSVM\_Value CreatePromise(JSVM\_Env env, JSVM\_CallbackInfo info) {
    OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST: CreatePromise start");
    int envID = -1;
    // 通过当前env获取envID
    for (auto it = g\_envMap.begin(); it != g\_envMap.end(); ++it) {
        if (\*it->second == env) {
            envID = it->first;
            break;
        }
    }
    if (envID == -1) {
        OH\_LOG\_ERROR(LOG\_APP, "JSVM API TEST: CreatePromise envID failed");
        return nullptr;
    }
    JSVM\_Value promise;
    JSVM\_Deferred deferred;
    JSVM\_CALL(OH\_JSVM\_CreatePromise(env, &deferred, &promise));
    // 设计ReadTask类用以将promise对象的deferred加入执行队列
    class ReadTask : public Task {
    public:
        ReadTask(JSVM\_Env env, JSVM\_Deferred deferred, int envNum) : env\_(env), envID\_(envNum), deferred\_(deferred) {}
        void Run() override {
            // string str = "TEST RUN OH\_JSVM\_ResolveDeferred";
            int envID = 0;
            for (auto it = g\_envMap.begin(); it != g\_envMap.end(); ++it) {
                if (\*it->second == env\_) {
                    envID = it->first;
                    break;
                }
            }
            OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST: CreatePromise %{public}d", envID);
            JSVM\_Value result;
            if (OH\_JSVM\_CreateInt32(env\_, envID, &result) != JSVM\_OK) {
                return;
            }
            if (OH\_JSVM\_ResolveDeferred(env\_, deferred\_, result) != JSVM\_OK) {
                return;
            }
        }

    private:
        JSVM\_Env env\_;
        int envID\_;
        JSVM\_Deferred deferred\_;
    };
    g\_taskQueueMap\[envID\].push\_back(new ReadTask(env, deferred, envID));
    OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST: CreatePromise end");
    return promise;
}

// 自定义Add方法
static JSVM\_Value Add(JSVM\_Env env, JSVM\_CallbackInfo info) {
    size\_t argc = 2;
    JSVM\_Value args\[2\];
    JSVM\_CALL(OH\_JSVM\_GetCbInfo(env, info, &argc, args, NULL, NULL));
    double num1 = 0, num2 = 0;
    JSVM\_CALL(OH\_JSVM\_GetValueDouble(env, args\[0\], &num1));
    JSVM\_CALL(OH\_JSVM\_GetValueDouble(env, args\[1\], &num2));
    JSVM\_Value sum = nullptr;
    JSVM\_CALL(OH\_JSVM\_CreateDouble(env, num1 + num2, &sum));
    return sum;
}

// 自定义AssertEqual方法
static JSVM\_Value AssertEqual(JSVM\_Env env, JSVM\_CallbackInfo info) {
    size\_t argc = 2;
    JSVM\_Value args\[2\];
    JSVM\_CALL(OH\_JSVM\_GetCbInfo(env, info, &argc, args, NULL, NULL));

    bool isStrictEquals = false;
    JSVM\_CALL(OH\_JSVM\_StrictEquals(env, args\[0\], args\[1\], &isStrictEquals));

    if (isStrictEquals) {
        OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST RESULT: PASS");
    } else {
        OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST RESULT: FAILED");
    }
    return nullptr;
}

static int fromOHStringValue(JSVM\_Env &env, JSVM\_Value &value, std::string &result) {
    size\_t size = 0;
    CHECK\_RET(OH\_JSVM\_GetValueStringUtf8(env, value, nullptr, 0, &size));
    char \*resultStr = new char\[size + 1\];
    CHECK\_RET(OH\_JSVM\_GetValueStringUtf8(env, value, resultStr, size + 1, &size));
    result = resultStr;
    delete\[\] resultStr;
    return 0;
}

// 提供创建JSVM运行环境的对外接口并返回对应唯一ID
static int CreateJsCore(uint32\_t \*result) {
    OH\_LOG\_INFO(LOG\_APP, "JSVM CreateJsCore START");
    g\_taskQueueMap\[ENVTAG\_NUMBER\] = deque<Task \*>{};

    if (g\_aa == 0) {
        JSVM\_InitOptions init\_options;
        memset(&init\_options, 0, sizeof(init\_options));
        CHECK(OH\_JSVM\_Init(&init\_options));
        g\_aa++;
    }
    std::lock\_guard<std::mutex> lock\_guard(envMapLock);

    // 虚拟机实例
    g\_vmMap\[ENVTAG\_NUMBER\] = new JSVM\_VM;
    JSVM\_CreateVMOptions options;
    JSVM\_VMScope vmScope;
    memset(&options, 0, sizeof(options));
    CHECK(OH\_JSVM\_CreateVM(&options, g\_vmMap\[ENVTAG\_NUMBER\]));
    CHECK(OH\_JSVM\_OpenVMScope(\*g\_vmMap\[ENVTAG\_NUMBER\], &vmScope));

    // 新环境
    g\_envMap\[ENVTAG\_NUMBER\] = new JSVM\_Env;
    g\_callBackStructMap\[ENVTAG\_NUMBER\] = new JSVM\_CallbackStruct\[4\];

    // 注册用户提供的本地函数的回调函数指针和数据，通过JSVM-API暴露给js
    for (int i = 0; i < 4; i++) {
        g\_callBackStructMap\[ENVTAG\_NUMBER\]\[i\].data = nullptr;
    }
    g\_callBackStructMap\[ENVTAG\_NUMBER\]\[0\].callback = ConsoleInfo;
    g\_callBackStructMap\[ENVTAG\_NUMBER\]\[1\].callback = Add;
    g\_callBackStructMap\[ENVTAG\_NUMBER\]\[2\].callback = AssertEqual;
    g\_callBackStructMap\[ENVTAG\_NUMBER\]\[3\].callback = CreatePromise;
    JSVM\_PropertyDescriptor descriptors\[\] = {
        {"consoleinfo", NULL, &g\_callBackStructMap\[ENVTAG\_NUMBER\]\[0\], NULL, NULL, NULL, JSVM\_DEFAULT},
        {"add", NULL, &g\_callBackStructMap\[ENVTAG\_NUMBER\]\[1\], NULL, NULL, NULL, JSVM\_DEFAULT},
        {"assertEqual", NULL, &g\_callBackStructMap\[ENVTAG\_NUMBER\]\[2\], NULL, NULL, NULL, JSVM\_DEFAULT},
        {"createPromise", NULL, &g\_callBackStructMap\[ENVTAG\_NUMBER\]\[3\], NULL, NULL, NULL, JSVM\_DEFAULT},
    };
    CHECK(OH\_JSVM\_CreateEnv(\*g\_vmMap\[ENVTAG\_NUMBER\], sizeof(descriptors) / sizeof(descriptors\[0\]), descriptors,
                            g\_envMap\[ENVTAG\_NUMBER\]));
    CHECK(OH\_JSVM\_CloseVMScope(\*g\_vmMap\[ENVTAG\_NUMBER\], vmScope));

    OH\_LOG\_INFO(LOG\_APP, "JSVM CreateJsCore END");
    \*result = ENVTAG\_NUMBER;
    ENVTAG\_NUMBER++;
    return 0;
}

// 对外提供释放JSVM环境接口，通过envId释放对应环境
static int ReleaseJsCore(uint32\_t coreEnvId) {
    std::lock\_guard<std::mutex> lock\_guard(envMapLock);
    
    OH\_LOG\_INFO(LOG\_APP, "JSVM ReleaseJsCore START");
    CHECK\_COND(g\_envMap.count(coreEnvId) != 0 && g\_envMap\[coreEnvId\] != nullptr);

    CHECK(OH\_JSVM\_DestroyEnv(\*g\_envMap\[coreEnvId\]));
    g\_envMap\[coreEnvId\] = nullptr;
    g\_envMap.erase(coreEnvId);
    CHECK(OH\_JSVM\_DestroyVM(\*g\_vmMap\[coreEnvId\]));
    g\_vmMap\[coreEnvId\] = nullptr;
    g\_vmMap.erase(coreEnvId);
    delete\[\] g\_callBackStructMap\[coreEnvId\];
    g\_callBackStructMap\[coreEnvId\] = nullptr;
    g\_callBackStructMap.erase(coreEnvId);
    g\_taskQueueMap.erase(coreEnvId);

    OH\_LOG\_INFO(LOG\_APP, "JSVM ReleaseJsCore END");
    return 0;
}

static std::mutex mutexLock;
// 对外提供执行JS代码接口，通过coreID在对应的JSVM环境中执行JS代码
static int EvaluateJS(uint32\_t envId, const char \*source, std::string &res) {
    OH\_LOG\_INFO(LOG\_APP, "JSVM EvaluateJS START");

    CHECK\_COND(g\_envMap.count(envId) != 0 && g\_envMap\[envId\] != nullptr);

    JSVM\_Env env = \*g\_envMap\[envId\];
    JSVM\_VM vm = \*g\_vmMap\[envId\];
    JSVM\_VMScope vmScope;
    JSVM\_EnvScope envScope;
    JSVM\_HandleScope handleScope;
    JSVM\_Value result;

    std::lock\_guard<std::mutex> lock\_guard(mutexLock);
    {
        // 创建JSVM环境
        CHECK\_RET(OH\_JSVM\_OpenVMScope(vm, &vmScope));
        CHECK\_RET(OH\_JSVM\_OpenEnvScope(\*g\_envMap\[envId\], &envScope));
        CHECK\_RET(OH\_JSVM\_OpenHandleScope(\*g\_envMap\[envId\], &handleScope));

        // 通过script调用测试函数
        JSVM\_Script script;
        JSVM\_Value jsSrc;
        CHECK\_RET(OH\_JSVM\_CreateStringUtf8(env, source, JSVM\_AUTO\_LENGTH, &jsSrc));
        CHECK\_RET(OH\_JSVM\_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
        CHECK\_RET(OH\_JSVM\_RunScript(env, script, &result));

        JSVM\_ValueType type;
        CHECK\_RET(OH\_JSVM\_Typeof(env, result, &type));
        OH\_LOG\_INFO(LOG\_APP, "JSVM API TEST type: %{public}d", type);
        // Execute tasks in the current env event queue
        while (!g\_taskQueueMap\[envId\].empty()) {
            auto task = g\_taskQueueMap\[envId\].front();
            g\_taskQueueMap\[envId\].pop\_front();
            task->Run();
            delete task;
        }

        if (type == JSVM\_STRING) {
            CHECK\_COND(fromOHStringValue(env, result, res) != -1);
        } else if (type == JSVM\_BOOLEAN) {
            bool ret = false;
            CHECK\_RET(OH\_JSVM\_GetValueBool(env, result, &ret));
            ret ? res = "true" : res = "false";
        } else if (type == JSVM\_NUMBER) {
            int32\_t num = 0;
            CHECK\_RET(OH\_JSVM\_GetValueInt32(env, result, &num));
            res = std::to\_string(num);
        } else if (type == JSVM\_OBJECT) {
            JSVM\_Value objResult;
            CHECK\_RET(OH\_JSVM\_JsonStringify(env, result, &objResult));
            CHECK\_COND(fromOHStringValue(env, objResult, res) != -1);
        }
    }
    {
        bool aal = false;
        CHECK\_RET(OH\_JSVM\_PumpMessageLoop(\*g\_vmMap\[envId\], &aal));
        CHECK\_RET(OH\_JSVM\_PerformMicrotaskCheckpoint(\*g\_vmMap\[envId\]));
        CHECK\_RET(OH\_JSVM\_CloseHandleScope(\*g\_envMap\[envId\], handleScope));
        CHECK\_RET(OH\_JSVM\_CloseEnvScope(\*g\_envMap\[envId\], envScope));
        CHECK\_RET(OH\_JSVM\_CloseVMScope(\*g\_vmMap\[envId\], vmScope));
    }
    OH\_LOG\_INFO(LOG\_APP, "JSVM EvaluateJS END");
    return 0;
}

static int32\_t TestJSVM() {
    const char source1\[\] = "{\\
        let a = \\"hello World\\";\\
        consoleinfo(a);\\
        const mPromise = createPromise();\\
        mPromise.then((result) => {\\
          assertEqual(result, 0);\\
        });\\
        a;\\
    };";

    const char source2\[\] = "{\\
        let a = \\"second hello\\";\\
        consoleinfo(a);\\
        let b = add(99, 1);\\
        assertEqual(100, b);\\
        assertEqual(add(99, 1), 100);\\
        createPromise().then((result) => {\\
            assertEqual(result, 1);\\
        });\\
        a;\\
    };";

    // 创建首个运行环境，并绑定TS回调
    uint32\_t coreId1 = 0;
    CHECK\_COND(CreateJsCore(&coreId1) == 0);
    OH\_LOG\_INFO(LOG\_APP, "TEST coreId: %{public}d", coreId1);
    // 在首个运行环境中执行JS代码
    std::string result1;
    CHECK\_COND(EvaluateJS(coreId1, source1, result1) == 0);
    OH\_LOG\_INFO(LOG\_APP, "TEST evaluateJS: %{public}s", result1.c\_str());

    // 创建第二个运行环境，并绑定TS回调
    uint32\_t coreId2 = 0;
    CHECK\_COND(CreateJsCore(&coreId2) == 0);
    OH\_LOG\_INFO(LOG\_APP, "TEST coreId: %{public}d", coreId2);
    // 在第二个运行环境中执行JS代码
    std::string result2;
    CHECK\_COND(EvaluateJS(coreId2, source2, result2) == 0);
    OH\_LOG\_INFO(LOG\_APP, "TEST evaluateJS: %{public}s", result2.c\_str());

    // 释放首个运行环境
    CHECK\_COND(ReleaseJsCore(coreId1) == 0);
    // 释放第二个运行环境
    CHECK\_COND(ReleaseJsCore(coreId2) == 0);
    OH\_LOG\_INFO(LOG\_APP, "Test NAPI end");

    return 0;
}

预计的输出结果：

JSVM CreateJsCore START
JSVM CreateJsCore END
TEST coreId: 0
JSVM EvaluateJS START
JSVM API TEST: hello World
JSVM API TEST: CreatePromise start
JSVM API TEST: CreatePromise end
JSVM API TEST type: 4
JSVM API TEST: CreatePromise 0
JSVM API TEST RESULT: PASS
JSVM EvaluateJS END
TEST evaluateJS: hello World
JSVM CreateJsCore START
JSVM CreateJsCore END
TEST coreId: 1
JSVM EvaluateJS START
JSVM API TEST: second hello
JSVM API TEST RESULT: PASS
JSVM API TEST RESULT: PASS
JSVM API TEST: CreatePromise start
JSVM API TEST: CreatePromise end
JSVM API TEST type: 4
JSVM API TEST: CreatePromise 1
JSVM API TEST RESULT: PASS
JSVM EvaluateJS END
TEST evaluateJS: second hello
JSVM ReleaseJsCore START
JSVM ReleaseJsCore END
JSVM ReleaseJsCore START
JSVM ReleaseJsCore END
Test NAPI end