JSI

react-native v0.67.3

node v14.21.2

JSI全称JavaScript Interface,JavaScript接口层, 旨在抹平JS 虚拟机/引擎(JSCore/V8/quick js)等APi差异,封装统一的JS 虚拟机API,https://github.com/react-native-community/discussions-and-proposals/issues/91

N-API

类似JSI的概念在JSI中也有体现,叫做Node-API。官方的解释如下

Node-API(以前的N-API)是一个用于构建本地插件的API。它独立于底层的JavaScript运行时(例如,V8),并作为Node.js本身的一部分进行维护。该API将在Node.js的各个版本中保持应用二进制接口(ABI)的稳定性。它的目的是使附加组件不受底层JavaScript引擎变化的影响,并允许为一个主要版本编译的模块在后来的Node.js主要版本上运行而无需重新编译。

JSIRuntime

JSI定一个runtime protocol,以实现不同的runtime,JSI runtime 目前实现引擎是JSCRuntime

JSI Runtime提供的公开方法

class JSI_EXPORT Runtime {
 public:
  virtual ~Runtime();

  virtual Value evaluateJavaScript(
      const std::shared_ptr<const Buffer>& buffer,
      const std::string& sourceURL) = 0;

  virtual std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
      const std::shared_ptr<const Buffer>& buffer,
      std::string sourceURL) = 0;

  virtual Value evaluatePreparedJavaScript(
      const std::shared_ptr<const PreparedJavaScript>& js) = 0;

  /// \return the global object
  virtual Object global() = 0;

};

JSI runtime提供基本的方法如evaluateJavaScript、获取global对象

prepare

另外JSI runtime 提供prepare的功能,把js data 存在内存中

prepare的使用

TEST_P(JSITest, PreparedJavaScriptSourceTest) {
  rt.evaluateJavaScript(std::make_unique<StringBuffer>("var q = 0;"), "");
  auto prep = rt.prepareJavaScript(std::make_unique<StringBuffer>("q++;"), "");
  EXPECT_EQ(rt.global().getProperty(rt, "q").getNumber(), 0);
  rt.evaluatePreparedJavaScript(prep);
  EXPECT_EQ(rt.global().getProperty(rt, "q").getNumber(), 1);
  rt.evaluatePreparedJavaScript(prep);
  EXPECT_EQ(rt.global().getProperty(rt, "q").getNumber(), 2);
}

调用prepare 是返回一个SourceJavaScriptPreparation对象,里面有buffer 和 sourceURL的成员变量

evaluatePreparedJavaScript则是把SourceJavaScriptPreparation读取出来并且执行

JSIExecutor

JSIExecutor 是对JSI的封装,JSIExecutor继承自JSExecutor,原RCTObjcExecutor也实现了JSExecutor

class RN_EXPORT JSExecutor {
 public:
  /**
   * Prepares the JS runtime for React Native by installing global variables.
   * Called once before any JS is evaluated.
   */
  virtual void initializeRuntime() = 0;
  /**
   * Execute an application script bundle in the JS context.
   */
  virtual void loadBundle(
      std::unique_ptr<const JSBigString> script,
      std::string sourceURL) = 0;

  /**
   * Add an application "RAM" bundle registry
   */
  virtual void setBundleRegistry(
      std::unique_ptr<RAMBundleRegistry> bundleRegistry) = 0;

  /**
   * Register a file path for an additional "RAM" bundle
   */
  virtual void registerBundle(
      uint32_t bundleId,
      const std::string &bundlePath) = 0;

  /**
   * Executes BatchedBridge.callFunctionReturnFlushedQueue with the module ID,
   * method ID and optional additional arguments in JS. The executor is
   * responsible for using Bridge->callNativeModules to invoke any necessary
   * native modules methods.
   */
  virtual void callFunction(
      const std::string &moduleId,
      const std::string &methodId,
      const folly::dynamic &arguments) = 0;

  /**
   * Executes BatchedBridge.invokeCallbackAndReturnFlushedQueue with the cbID,
   * and optional additional arguments in JS and returns the next queue. The
   * executor is responsible for using Bridge->callNativeModules to invoke any
   * necessary native modules methods.
   */
  virtual void invokeCallback(
      const double callbackId,
      const folly::dynamic &arguments) = 0;

  virtual void setGlobalVariable(
      std::string propName,
      std::unique_ptr<const JSBigString> jsonValue) = 0;

  virtual void *getJavaScriptContext() {
    return nullptr;
  }

  /**
   * Returns whether or not the underlying executor supports debugging via the
   * Chrome remote debugging protocol.
   */
  virtual bool isInspectable() {
    return false;
  }

  /**
   * The description is displayed in the dev menu, if there is one in
   * this build.  There is a default, but if this method returns a
   * non-empty string, it will be used instead.
   */
  virtual std::string getDescription() = 0;

  virtual void handleMemoryPressure(__unused int pressureLevel) {}

  virtual void destroy() {}
  virtual ~JSExecutor() {}

  virtual void flush() {}

  static std::string getSyntheticBundlePath(
      uint32_t bundleId,
      const std::string &bundlePath);
};

} // namespace react
} // namespace facebook

initializeRuntime

与js 的交互协议在JSIExecutor内的initializeRuntime函数注入的

比如关于invoke的注入是

runtime_->global().setProperty(
      *runtime_,
      "nativeFlushQueueImmediate",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) {
            if (count != 1) {
              throw std::invalid_argument(
                  "nativeFlushQueueImmediate arg count must be 1");
            }
            callNativeModules(args[0], false);
            return Value::undefined();
          }));

可以支持set 函数

其函数在JSCRuntime内的实现是一个JSObject

jsi::Function JSCRuntime::createFunctionFromHostFunction(
    const jsi::PropNameID &name,
    unsigned int paramCount,
    jsi::HostFunctionType func) {

  JSObjectRef funcRef = JSObjectMake(
      ctx_,
      hostFunctionClass,
      new HostFunctionMetadata(this, func, paramCount, stringRef(name)));
  return createObject(funcRef).getFunction(*this);
}

using HostFunctionType = std::function<
    Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>;

void JSCRuntime::setPropertyValue(
    jsi::Object &object,
    const jsi::PropNameID &name,
    const jsi::Value &value) {
  JSValueRef exc = nullptr;
  JSObjectSetProperty(
      ctx_,
      objectRef(object),
      stringRef(name),
      valueRef(value),
      kJSPropertyAttributeNone,
      &exc);
  checkException(exc);
}

loadBundle

loadBundle的顺序是

  1. evaluateJavaScript
  2. flush

setBundleRegistry

RAM Bundle才支持

注入nativeRequire方法,并且保存RAMBundleRegistry参数到内存

nativeRequire方法的实现

  • 调用RAMBundleRegistry::getModule的方法
  • evaluateJavaScript得到的module的代码

registerBundle

如果存在bundleRegistry_(RAMBundleRegistry),则调用其registerBundle。没有则evaluateJavaScript

其中RAMBundleRegistry有一个map保存着bundleid和bundlepath的映射关系

JSIDynamic

valueFromDynamic

cpp的对象(folly::dynamic)转换为JS 对象(Value)

Value

整个JSI通过Value来表示各种数据类型,Value能够转换为其他的类型 Object String 等

Object的常见方法

Value getProperty(Runtime& runtime, const char* name) const;
template <typename T>
  void setProperty(Runtime& runtime, const char* name, T&& value);

JSValueRef

在JSCRuntime中的valueRef方法,会最终把jsi的Value转换为JSValueRef

JSValueRef JSCRuntime::valueRef(const jsi::Value &value) {
  // I would rather switch on value.kind_
  if (value.isUndefined()) {
    return JSValueMakeUndefined(ctx_);
  } else if (value.isNull()) {
    return JSValueMakeNull(ctx_);
  } else if (value.isBool()) {
    return JSValueMakeBoolean(ctx_, value.getBool());
  } else if (value.isNumber()) {
    return JSValueMakeNumber(ctx_, value.getNumber());
  } else if (value.isSymbol()) {
    return symbolRef(value.getSymbol(*this));
  } else if (value.isString()) {
    return JSValueMakeString(ctx_, stringRef(value.getString(*this)));
  } else if (value.isObject()) {
    return objectRef(value.getObject(*this));
  } else {
    // What are you?
    abort();
  }
}

JSINativeModules

createModule方法

首先从js侧获取到__fbGenNativeModule,详细见NativeModules.js

function genModule(
  config: ?ModuleConfig,
  moduleID: number,
): ?{
  name: string,
  module?: {...},
  ...
} {

  const [moduleName, constants, methods, promiseMethods, syncMethods] = config;

  if (!constants && !methods) {
    // Module contents will be filled in lazily later
    return {name: moduleName};
  }

  const module = {};
  methods &&
    methods.forEach((methodName, methodID) => {
      const isPromise =
        (promiseMethods && arrayContains(promiseMethods, methodID)) || false;
      const isSync =
        (syncMethods && arrayContains(syncMethods, methodID)) || false;

      const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
      module[methodName] = genMethod(moduleID, methodID, methodType);
    });

  Object.assign(module, constants);

  if (module.getConstants == null) {
    module.getConstants = () => constants || Object.freeze({});
  }

  return {name: moduleName, module};
}

// export this method as a global so we can call it from native
global.__fbGenNativeModule = genModule;

然后调用__fbGenNativeModule函数,传入ModuleConfig和moduleId,这两个的来源自m_moduleRegistry

__fbGenNativeModule函数会返回module到native侧,完成createModule的调用

m_moduleRegistry的数据源在RCTCxxBridge的_buildModuleRegistryUnlocked进行构建,这里会把_moduleDataByID(RCTModuleData)转换为RCTNativeModule

getModule方法,从m_objects中查找module

HostObject

实现该接口就可以在JS Runtime注册一个对象

class JSI_EXPORT HostObject {
  virtual Value get(Runtime&, const PropNameID& name);

  virtual void set(Runtime&, const PropNameID& name, const Value& value);

  virtual std::vector<PropNameID> getPropertyNames(Runtime& rt);

};

这几个方法需要子类实现,可以看mmkv的例子

Object提供HostObject 转 Object的方法

class JSI_EXPORT Object : public Pointer {

  static Object createFromHostObject(
      Runtime& runtime,
      std::shared_ptr<HostObject> ho) {
    return runtime.createObject(ho);
  }

  template <typename T = HostObject>
  std::shared_ptr<T> getHostObject(Runtime& runtime) const;

  template <typename T = HostObject>
  std::shared_ptr<T> asHostObject(Runtime& runtime) const;
};

Runtime也有相关方法

  virtual Object createObject(std::shared_ptr<HostObject> ho) = 0;

  virtual std::shared_ptr<HostObject> getHostObject(const jsi::Object&) = 0;
  virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0;

  virtual bool isHostObject(const jsi::Object&) const = 0;
  virtual bool isHostFunction(const jsi::Function&) const = 0;

  virtual Function createFunctionFromHostFunction(
      const PropNameID& name,
      unsigned int paramCount,
      HostFunctionType func) = 0;

results matching ""

    No results matching ""