场景描述

通过JSVM,可以在应用运行期间直接执行一段动态加载的JS代码。也可以选择将一些对性能、底层系统调用有较高要求的核心功能用C/C++实现并将C++方法注册到JS侧,在JS代码中直接调用,提高应用的执行效率。

功能描述

通过createJsCore方法来创建一个新的JS基础运行时环境,并通过该方法获得一个虚拟机ID,通过evalUateJS方法使用虚拟机ID对应的运行环境来运行JS代码,在JS代码中创建promise并异步调取TS侧设定的callback函数,最后使用releaseJsCore方法来释放虚拟机ID对应的运行环境。

JSVM主要交互流程

接口介绍

创建所需接口:

// 初始化虚拟机
JSVM_EXTERN JSVM_Status OH_JSVM_Init(const JSVM_InitOptions* options);
​
// 创建虚拟机实例
JSVM_EXTERN JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options,JSVM_VM* result);
​
// 创建虚拟机作用域
JSVM_EXTERN JSVM_Status OH_JSVM_OpenVMScope(JSVM_VM vm,JSVM_VMScope* result);
​
// 创建上下文环境
JSVM_EXTERN JSVM_Status OH_JSVM_CreateEnv(JSVM_VM vm,size_t propertyCount,const JSVM_PropertyDescriptor* properties,JSVM_Env* result);
​
// 创建上下文环境作用域
JSVM_EXTERN JSVM_Status OH_JSVM_OpenEnvScope(JSVM_Env env,JSVM_EnvScope* result);

执行所需接口:

// 开启新作用域
JSVM_EXTERN JSVM_Status OH_JSVM_OpenHandleScope(JSVM_Env env,JSVM_HandleScope* result);
​
// 编译一串JS代码,并返回编译后脚本
JSVM_EXTERN JSVM_Status OH_JSVM_CompileScript(JSVM_Env env,JSVM_Value script,const uint8_t* cachedData,size_t cacheDataLength,bool eagerCompile,bool* cacheRejected,JSVM_Script* result);
​
// 执行一段JS代码并返回结果
JSVM_EXTERN JSVM_Status OH_JSVM_RunScript(JSVM_Env env,JSVM_Script script,JSVM_Value* result);
​
// 关闭传入的作用域
JSVM_EXTERN JSVM_Status OH_JSVM_CloseHandleScope(JSVM_Env env,JSVM_HandleScope scope);

关闭所需接口:

// 关闭上下文环境作用域
JSVM_EXTERN JSVM_Status OH_JSVM_CloseEnvScope(JSVM_Env env,JSVM_EnvScope scope);
​
// 销毁上下文环境
JSVM_EXTERN JSVM_Status OH_JSVM_DestroyEnv(JSVM_Env env);
​
// 关闭虚拟机作用域
JSVM_EXTERN JSVM_Status OH_JSVM_CloseVMScope(JSVM_VM vm,JSVM_VMScope scope);
​
// 销毁虚拟机实例
JSVM_EXTERN JSVM_Status OH_JSVM_DestroyVM(JSVM_VM vm);

场景代码

场景一:JSVM执行JS代码并回调C++代码

创建虚拟机所需环境,编译并执行JS代码回调C++代码,销毁之前创建的虚拟机环境。

配置

cmakelists:

target_link_libraries(entry PUBLIC libjsvm.so)

ArkTS代码:

示例中包含了提供给JSVM的JS里的consoleinfo同步方法,createPromise创建异步方法,执行assertEqual方法,onJSResultCallback异步方法回调。

  1. 调用Native的创建虚拟机及环境的接口,绑定MyCallback回调(用于JS代码里onJSResultCallback方法的回调),接收native返回的虚拟机id。
  2. 调用Native的执行JS代码的接口,传入虚拟机id和JS代码字符串,在native侧的虚拟机里编译并执行。
  3. 调用Native的销毁虚拟机及环境的接口,传入虚拟机id,指定销毁对应的虚拟机和环境。

可以同时运行多个JSVM虚拟机执行多个JS代码。

import testNapi from 'libentry.so';
​
// 用于JSVM执行的JS代码字符串
let jsCodeStr = `{
let a = "hello World";
consoleinfo(a);
let b = add(99, 1);
const mPromise = createPromise();
mPromise.then((result) => {
assertEqual(result, 0);
onJSResultCallback(result, "abc", "v");
});
a;
};`;
​
function MyCallback(a:string, b:string):string {
  console.log("TEST MyCallback run: " + a);
  b = "callback done";
  console.log("TEST MyCallback run: " + b);
  return "callback pass";
}
​
// 创建首个运行环境,并绑定TS回调
const coreId = testNapi.createJsCore(MyCallback);
// 在首个运行环境中执行JS代码
testNapi.evalUateJS(coreId, jsCodeStr);
// 释放首个运行环境
testNapi.releaseJsCore(coreId);

Native代码:

JS运行环境创建,创建让JS代码运行的虚拟机环境:

  1. 通过OH\_JSVM\_Init初始化虚拟机。
  2. 通过OH\_JSVM\_CreateVM创建虚拟机实例。
  3. 通过OH\_JSVM\_OpenVMScope创建虚拟机作用域。
  4. 将本地函数的回调函数放到JSVM\_PropertyDescriptor集合中(用于JS调用的C++函数)。
  5. 通过OH\_JSVM\_CreateEnv创建上下文环境并注册JSVM\_PropertyDescriptor。
  6. 通过OH\_JSVM\_OpenEnvScope创建上下文环境作用域。
static void CreateArkJSContext() {
  JSVM_Status status;
  JSVM_InitOptions init_options;
  memset(&init_options, 0, sizeof(init_options));
  if (aa == 0) {

    //****1****初始化JSVM
    OH_JSVM_Init(&init_options);
    aa++;
  }
  ​
  g_vmMap[ENVTAG_NUMBER] = new JSVM_VM;
  JSVM_VMScope vmScope;
  g_vmScopeMap[ENVTAG_NUMBER] = vmScope;
  JSVM_CreateVMOptions options;
  memset(&options, 0, sizeof(options));

  //****2****创建JSVM实例
  status = OH_JSVM_CreateVM(&options, g_vmMap[ENVTAG_NUMBER]);

  //****3****创建JSVM作用域
  status = OH_JSVM_OpenVMScope(*g_vmMap[ENVTAG_NUMBER], &g_vmScopeMap[ENVTAG_NUMBER]);
  ​
  g_envMap[ENVTAG_NUMBER] = new JSVM_Env;
  g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[5];
  // 注册用户提供的本地函数的回调函数指针和数据,通过JSVM-API暴露给JS
  for (int i = 0; i < 5; 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 = OnJSResultCallback;
  g_callBackStructMap[ENVTAG_NUMBER][4].callback = CreatePromise;

  //****4****将本地函数的回调函数放到JSVM_PropertyDescriptor集合中
  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},
    {"onJSResultCallback", NULL, &g_callBackStructMap[ENVTAG_NUMBER][3], NULL, NULL, NULL, JSVM_DEFAULT},
    {"createPromise", NULL, &g_callBackStructMap[ENVTAG_NUMBER][4], NULL, NULL, NULL, JSVM_DEFAULT},
  };
//****5****创建JSVM环境
status = OH_JSVM_CreateEnv(*g_vmMap[ENVTAG_NUMBER], sizeof(descriptors) / sizeof(descriptors[0]), descriptors, g_envMap[ENVTAG_NUMBER]);
JSVM_EnvScope envScope;
g_envScopeMap[ENVTAG_NUMBER] = envScope;
//****6****创建JSVM环境作用域
status = OH_JSVM_OpenEnvScope(*g_envMap[ENVTAG_NUMBER], &g_envScopeMap[ENVTAG_NUMBER]);
}
​
// 提供创建JSVM运行环境的对外接口并返回对应唯一ID
static napi_value CreateJsCore(napi_env env1, napi_callback_info info) {
  OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore START");
  size_t argc = 1;
  napi_value argv[1];
  napi_get_cb_info(env1, info, &argc, argv, nullptr, nullptr);
  if (argc < 1) {
    OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore the number of params must be one");
    return nullptr;
  }
  g_napiEnvMap[ENVTAG_NUMBER] = env1;
  g_taskQueueMap[ENVTAG_NUMBER] = deque<Task *>{};
  // 将TS侧传入的回调函数与env对应存储方便后续调用
  //napi_ref生命周期由开发者自己管理,需要手动delete,引用 napi_value 的抽象。这允许用户管理 JavaScript 值的生命周期,包括明确定义它们的最小生命周期。
  napi_ref callFun;
  //为Object创建一个reference,以延长其生命周期。调用者需要自己管理reference生命周期。
  napi_create_reference(env1, argv[0], 1, &callFun);
  g_callBackMap[ENVTAG_NUMBER] = callFun;
  napi_value coreID = 0;
  {
    //互斥锁
    std::lock_guard<std::mutex> lock_guard(envMapLock);
    CreateArkJSContext();
    //累加
    napi_create_uint32(env1, ENVTAG_NUMBER, &coreID);
    ENVTAG_NUMBER++;
  }
  OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore END");
  return coreID;
}

JS代码执行,在虚拟机环境中编译并运行JS代码:

  1. 通过OH\_JSVM\_OpenHandleScope开启新的作用域。
  2. 通过OH\_JSVM\_CompileScript对传入的JS代码进行编译。
  3. 通过OH\_JSVM\_RunScript运行JS代码。
  4. 通过OH\_JSVM\_CloseHandleScope关闭作用域。
static std::mutex mutexLock;
// 对外提供执行JS代码接口,通过coreID在对应的JSVN环境中执行JS代码
static napi_value EvalUateJS(napi_env env, napi_callback_info info) {
  OH_LOG_ERROR(LOG_APP, "JSVM EvalUateJS START");
  size_t argc = 2;
  napi_value args[2] = {nullptr};
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  uint32_t envId;
  napi_status status = napi_get_value_uint32(env, args[0], &envId);
  if (status != napi_ok) {
    OH_LOG_ERROR(LOG_APP, "EvalUateJS first param should be number");
    return nullptr;
  }

  if (g_envMap.count(envId) == 0 || g_envMap[envId] == nullptr) {
    OH_LOG_ERROR(LOG_APP, "EvalUateJS env is null");
    return nullptr;
  }

  //获取传入的JS代码,转char[]类型
  std::string dataStr = napiValueToString(env, args[1]);
  napi_value res = nullptr;

  std::lock_guard<std::mutex> lock_guard(mutexLock);
  {
    // open handle scope
    JSVM_HandleScope handlescope;

    //****1**** 开启新作用域
    OH_JSVM_OpenHandleScope(*g_envMap[envId], &handlescope);
    // compile js
    JSVM_Value sourcecodevalue;
    // 创建一个由utf-8转的JS字符串
    OH_JSVM_CreateStringUtf8(*g_envMap[envId], dataStr.c_str(), dataStr.size(), &sourcecodevalue);
    JSVM_Script script;

    //****2****编译一串JS代码,并返回编译后脚本
    OH_JSVM_CompileScript(*g_envMap[envId], sourcecodevalue, nullptr, 0, true, nullptr, &script);
    // run JS
    JSVM_Value result;

    //****3****执行一段JS代码并返回结果
    OH_JSVM_RunScript(*g_envMap[envId], script, &result);
    JSVM_ValueType type;
    //调用typeof运算符的行为,得到结果类型
    OH_JSVM_Typeof(*g_envMap[envId], 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) {
      //JSVM_Value转字符串
      std::string stdResult = fromOHStringValue(*g_envMap[envId], result);
      napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
    } else if (type == JSVM_BOOLEAN) {
      bool ret = false;
      std::string stdResult;
      OH_JSVM_GetValueBool(*g_envMap[envId], result, &ret);
      ret ? stdResult = "true" : stdResult = "false";
      napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
    } else if (type == JSVM_NUMBER) {
      int32_t num;
      OH_JSVM_GetValueInt32(*g_envMap[envId], result, &num);
      std::string stdResult = std::to_string(num);
      napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
    } else if (type == JSVM_OBJECT) {
      JSVM_Value objResult;
      OH_JSVM_JsonStringify(*g_envMap[envId], result, &objResult);
      std::string stdResult = fromOHStringValue(*g_envMap[envId], objResult);
      napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
    }

    //****4****关闭传入的作用域
    OH_JSVM_CloseHandleScope(*g_envMap[envId], handlescope);
  }
  OH_LOG_ERROR(LOG_APP, "JSVM EvalUateJS END");
  return res;
}

JS运行环境销毁

关闭和销毁虚拟机和上下文环境的作用域与实例。

  1. 使用OH\_JSVM\_CloseEnvScope关闭上下文环境作用域。
  2. 使用OH\_JSVM\_DestroyEnv销毁上下文环境。
  3. 使用OH\_JSVM\_CloseVMScope关闭虚拟机作用域。
  4. 使用OH\_JSVM\_DestroyVM销毁虚拟机实例。
// 对外提供释放JSVM环境接口,通过envId释放对应环境
static napi_value ReleaseJsCore(napi_env env1, napi_callback_info info) {
  OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore START");
  size_t argc = 1;
  napi_value argv[1];
  napi_get_cb_info(env1, info, &argc, argv, nullptr, nullptr);
  if (argc < 1) {
    OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore the number of params must be one");
    return nullptr;
  }

  uint32_t coreEnvId;
  napi_status status = napi_get_value_uint32(env1, argv[0], &coreEnvId);
  if (status != napi_ok) {
    OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore napi_get_value_uint32 faild");
    return nullptr;
  }
  //是否存在创建的env环境
  if (g_envMap.count(coreEnvId) == 0 ) {
    OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore not has env ");
    return nullptr;
  }

  if (g_envMap[coreEnvId] != nullptr) {
    // 互斥锁
    std::lock_guard<std::mutex> lock_guard(envMapLock);

    //****1****关闭指定的Env的环境作用域
    OH_JSVM_CloseEnvScope(*g_envMap[coreEnvId], g_envScopeMap[coreEnvId]);
    //map中删除这个环境作用域
    g_envScopeMap.erase(coreEnvId);

    //****2****销毁指定Env环境
    OH_JSVM_DestroyEnv(*g_envMap[coreEnvId]);
    //map中删除这个环境
    g_envMap[coreEnvId] = nullptr;
    g_envMap.erase(coreEnvId);

    //****3**** 关闭指定的VM的作用域
    OH_JSVM_CloseVMScope(*g_vmMap[coreEnvId], g_vmScopeMap[coreEnvId]);
    g_vmScopeMap.erase(coreEnvId);

    //****4****销毁指定VM实例
    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);

    //删除引用计数
    napi_delete_reference(env1, g_callBackMap[coreEnvId]);
    g_callBackMap.erase(coreEnvId);
    g_taskQueueMap.erase(coreEnvId);
  }
  OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore END");
  return nullptr;
}

JS中调用的C++代码实现

调用的C++方法类似于NAPI的写法,通过OH\_JSVM\_GetCbInfo获取JS传入方法的参数,将获取到的JSVM\_Value的类型转换成c的类型。

自定义C++代码中的JSVM的常用接口与NAPI的接口类似,可参考文档底部的官方文档。

同步方法

用于打印日志的简单同步方法:

// 自定义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;
  OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL);
  ​
  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;
}

用于相加的简单同步方法:

// 自定义Add方法
static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) {
  size_t argc = 2;
  JSVM_Value args[2];
  OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL);
  double num1, num2;
  OH_JSVM_GetValueDouble(env, args[0], &num1);
  OH_JSVM_GetValueDouble(env, args[1], &num2);
  JSVM_Value sum = nullptr;
  OH_JSVM_CreateDouble(env, num1 + num2, &sum);
  return sum;
}

异步方法

Promise方式的异步调用,主要调用OH\_JSVM\_CreatePromise创建一个延迟对象和一个JavaScript promise,OH\_JSVM\_ResolveDeferred通过与之关联的延迟对象来解析JavaScript promise,用于解析对应的可用的延迟对象的JavaScript Promise

可参考NAPI的异步任务:使用Node-API接口进行异步任务开发

// 自定义创建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 = 0;
  // 通过当前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_INFO(LOG_APP, "JSVM API TEST: CreatePromise envID faild");
    return nullptr;
  }
  JSVM_Value promise;
  JSVM_Deferred deferred;
  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;
    OH_JSVM_CreateInt32(env_, envID, &result);
    OH_JSVM_ResolveDeferred(env_, deferred_, result);
  }
  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;
}

callback方式的异步调用,需要调用NAPI的异步调用方法。

// 用以在native层中调用TS侧传入的Callback函数
static JSVM_Value OnJSResultCallback(JSVM_Env env, JSVM_CallbackInfo info) {
  size_t argc = 3;
  JSVM_Value args[3];
  JSVM_CALL(env, OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
  int callId = 0;
  OH_JSVM_GetValueInt32(env, args[0], &callId);
  napi_value callArgs[2] = {nullptr, nullptr};
  size_t size;
  size_t size1;
  ​
  OH_JSVM_GetValueStringUtf8(env, args[1], nullptr, 0, &size);
  char Str1[size + 1];
  OH_JSVM_GetValueStringUtf8(env, args[1], Str1, size + 1, &size);

  OH_JSVM_GetValueStringUtf8(env, args[2], nullptr, 0, &size1);
  char Str2[size1 + 1];
  OH_JSVM_GetValueStringUtf8(env, args[2], Str2, size1 + 1, &size1);

  napi_create_string_utf8(g_napiEnvMap[callId], Str1, size + 1, &callArgs[0]);
  napi_create_string_utf8(g_napiEnvMap[callId], Str2, size1 + 1, &callArgs[1]);
  napi_value callback = nullptr;
  // 通过callId获取在创建当前JSVM环境时传入的TS回调方法
  napi_get_reference_value(g_napiEnvMap[callId], g_callBackMap[callId], &callback);
  napi_value ret;
  // 执行TS回调方法
  napi_call_function(g_napiEnvMap[callId], nullptr, callback, 2, callArgs, &ret);
  char retStr[256];
  napi_get_value_string_utf8(g_napiEnvMap[callId], ret, retStr, 256, &size);

  JSVM_Value returnVal;
  OH_JSVM_CreateStringUtf8(env, retStr, JSVM_AUTO_LENGTH, &returnVal);
  return returnVal;
}

OH\_JSVM\_StrictEquals提供类似调用严格相等算法的行为。

// 自定义AssertEqual方法
static JSVM_Value AssertEqual(JSVM_Env env, JSVM_CallbackInfo info) {
  size_t argc = 2;
  JSVM_Value args[2];
  JSVM_CALL(env, OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
  ​
  bool isStrictEquals = false;
  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;
}

代码中使用的全局变量和工具方法,包含自定义的全局变量和工具方法。

#include "napi/native_api.h"
#include "ark_runtime/jsvm.h"
#include "common.h"
​
#include <bits/alltypes.h>
#include <deque>
#include <map>
#include <unistd.h>
#include <hilog/log.h>
​
#include <cstring>
#include <string>
#include <vector>
#include <sstream>
​
#define LOG_TAG "TEST_TAG"
using namespace std;
// 定义map管理每个独立vm环境
static map<int, JSVM_VM*> g_vmMap;
static map<int, JSVM_VMScope> g_vmScopeMap;
static map<int, JSVM_Env*> g_envMap;
static map<int, napi_env> g_napiEnvMap;
static map<int, JSVM_EnvScope> g_envScopeMap;
static map<int, napi_ref> g_callBackMap;
static map<int, JSVM_CallbackStruct*> g_callBackStructMap;
static uint32_t ENVTAG_NUMBER = 0;
static std::mutex envMapLock;
static int aa = 0;
​
class Task {
  public:
    //virtual虚析构函数在销毁时会调用对象的析构函数,这样就不会出现像有的数据成员没有销毁导致内存泄露的问题或者程序直接崩溃
    virtual ~Task() = default;
  // virtual纯虚函数的类是一个抽象类
  virtual void Run() = 0;
};
//deque双端存储队列
static map<int, deque<Task *>> g_taskQueueMap;
​
std::string napiValueToString(napi_env env, napi_value nValue) {
  size_t buffLen = 0;
  napi_get_value_string_utf8(env, nValue, nullptr, 0, &buffLen);
  char buffer[buffLen + 1];
  napi_get_value_string_utf8(env, nValue, buffer, buffLen + 1, &buffLen);
  ​
  return buffer;
}
​
static std::string fromOHStringValue(JSVM_Env &env, JSVM_Value &value) {
  size_t size;
  JSVM_Status status;
  status = OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size);
  char resultStr[size + 1];
  status = OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size);
  return resultStr;
}
​
// create_jsvm_runtime.cpp
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
  napi_property_descriptor desc[] = {
    {"createJsCore", nullptr, CreateJsCore, nullptr, nullptr, nullptr, napi_default, nullptr},
  {"releaseJsCore", nullptr, ReleaseJsCore, nullptr, nullptr, nullptr, napi_default, nullptr},
  {"evalUateJS", nullptr, EvalUateJS, nullptr, nullptr, nullptr, napi_default, nullptr}
};
​
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
​
static napi_module demoModule = {
  .nm_version = 1,
  .nm_flags = 0,
  .nm_filename = nullptr,
  .nm_register_func = Init,
  .nm_modname = "entry",
  .nm_priv = ((void *)0),
  .reserved = {0},
};
​
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }

场景二:JSVM读取并执行文件的JS代码

一些场景需要去读取文件中的JS代码,可将JS代码放到/resources/rawfile/目录下,使用读取资源文件相关功能,将读取到的字符串,使用场景一的方式在JSVM执行JS代码,并回调C++代码。

从资源文件中读取JS代码

xxx.js文件放在/resources/rawfile/目录下

xxx.js

{
  let a = "hello World";
  consoleinfo(a);
  const mPromise = createPromise();
  mPromise.then((result) => {
    assertEqual(result, 0);
    onJSResultCallback(result, "abc", "v");
  });
  a;
};

配置cmakelists:

target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so librawfile.z.so)

ArkTS代码:

获取资源对象,调用native的方法传入资源对象和文件名称。

let resmgr = getContext().resourceManager
let a = testNapi.getRawFileContent(resmgr,"xxx.js")

Native代码:

获取ArkTS传入的资源对象转成native对象,获取rawfile指针对象,获取rawfile大小并申请内存,然后读取文件内容,最后关闭指针,将获取到的内容去除\r。

#include "napi/native_api.h"
#include <hilog/log.h>
#include <memory>
#include <rawfile/raw_file_manager.h>
​
static std::string str_txt;
​
static napi_value GetRawFileContent(napi_env env, napi_callback_info info)
{
  OH_LOG_INFO(LOG_APP, "GetFileContent Begin");
  size_t requireArgc = 3;
  size_t argc = 2;
  napi_value argv[2] = { nullptr };
  // 获取参数信息
  napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
  ​
  // argv[0]即为函数第一个参数Js资源对象,OH_ResourceManager_InitNativeResourceManager转为Native对象。
  NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
  size_t strSize;
  char strBuf[256];
  napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
  std::string filename(strBuf, strSize);
  ​
  // 获取rawfile指针对象
  RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, filename.c_str());
  if (rawFile != nullptr) {
    OH_LOG_INFO(LOG_APP, "OH_ResourceManager_OpenRawFile success");
  }
  // 获取rawfile大小并申请内存
  long len = OH_ResourceManager_GetRawFileSize(rawFile);
  std::unique_ptr<uint8_t[]> data= std::make_unique<uint8_t[]>(len);
  ​
  // 一次性读取rawfile全部内容
  int res = OH_ResourceManager_ReadRawFile(rawFile, data.get(), len);
  ​
  // 多次部分读取rawfile, 每次读取100 Byte。获取全部内容
  // long offset = 0;
  // while (OH_ResourceManager_GetRawFileRemainingLength(rawFile) > 0) {
  //     OH_ResourceManager_ReadRawFile(rawFile, data.get() + offset, 100);
  //     offset += 100;
  // }
  ​
  // 关闭打开的指针对象
  OH_ResourceManager_CloseRawFile(rawFile);
  OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
  // 转为JS对象
  ​
  std::string str(reinterpret_cast<char *>(data.get()), len);
  size_t pos = 0;
  while ((pos = str.find('\r')) != std::string::npos) {
  str.replace(pos, 1, "");
}
  str_txt = str;
  ​
  return nullptr;
}

JSVM执行JS代码

总体与场景一相同,只需修改JS代码,执行这段代码里获取ArkTS传入的JS字符串相关代码,将其替换为上面从JS文件中获取到的JS字符串。

官方文档

包含各种常用接口及介绍:JSVM

资源文件读取可参考文档:Rawfile开发指导


HarmonyOS码上奇行
7k 声望2.8k 粉丝