ReactNative JSI
简述
React Native使用JavaScript来实现UI,新架构使用的jsi来实现C/C++和JavaScript之间的互相调用,而jsi是使用JS引擎提供的api能力来实现的,如果看过前面TurboModule的介绍,应该对JSI有一些概念了,这一节我们会更加进一步去介绍JSI的实现,其实JSI的逻辑是很简单的,只不过因为有非常多的数据类型,所以有许多的业务逻辑。
Runtime
JSI定义了一个Runtime作为接口,不同的JS引擎都需要实现这个Runtime,JavaScriptCore的实现是JSCRuntime,而Hermes的实现是HermesRuntime,HermesRuntime的源码没有放在RN里,我们只要知道这个Runtime的实现类里就是通过JS引擎提供的api来实现Runtime定义的接口功能,这里就可以解决JSI接口固定,但是底层使用不同的JS引擎的情况。
我们来看看Runtime定义的一些接口。
这里提供了大量接口,执行JavaScript代码,创建JS对象,获得或者配置JS对象的属性,数据类型的转换等等,根据不同的JS引擎提供的api来实现这些方法,然后JSI就是基于这些接口来做封装。
class JSI_EXPORT Runtime {
public:
virtual ~Runtime();
// 执行JavaScript代码,这个我们之前启动流程中看到有使用过
virtual Value evaluateJavaScript(
const std::shared_ptr<const Buffer>& buffer,
const std::string& sourceURL) = 0;
// 检查,预处理,JavaScript代码
virtual std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
const std::shared_ptr<const Buffer>& buffer,
std::string sourceURL) = 0;
// 执行已经预处理的JavaScript代码
virtual Value evaluatePreparedJavaScript(
const std::shared_ptr<const PreparedJavaScript>& js) = 0;
// 将微任务挂到队列上
virtual void queueMicrotask(const jsi::Function& callback) = 0;
// 清理微任务
virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;
// 获取global对象
virtual Object global() = 0;
// ...
protected:
// 这些类映射JavaScript上的一些内容
friend class Pointer;
friend class PropNameID;
friend class Symbol;
friend class BigInt;
friend class String;
friend class Object;
friend class WeakObject;
friend class Array;
friend class ArrayBuffer;
friend class Function;
friend class Value;
friend class Scope;
friend class JSError;
struct PointerValue {
virtual void invalidate() = 0;
protected:
virtual ~PointerValue() = default;
};
// 一些clone函数
virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* cloneBigInt(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0;
// 构建PropNameID,JavaScript对象里的变量在这里是以键值对的形式,PropNameID去关联属性。
virtual PropNameID createPropNameIDFromAscii(
const char* str,
size_t length) = 0;
virtual PropNameID createPropNameIDFromUtf8(
const uint8_t* utf8,
size_t length) = 0;
virtual PropNameID createPropNameIDFromString(const String& str) = 0;
virtual PropNameID createPropNameIDFromSymbol(const Symbol& sym) = 0;
virtual std::string utf8(const PropNameID&) = 0;
virtual bool compare(const PropNameID&, const PropNameID&) = 0;
// 限免是一些数据类型的相互转换
virtual std::string symbolToString(const Symbol&) = 0;
virtual BigInt createBigIntFromInt64(int64_t) = 0;
virtual BigInt createBigIntFromUint64(uint64_t) = 0;
virtual bool bigintIsInt64(const BigInt&) = 0;
virtual bool bigintIsUint64(const BigInt&) = 0;
virtual uint64_t truncate(const BigInt&) = 0;
virtual String bigintToString(const BigInt&, int) = 0;
virtual String createStringFromAscii(const char* str, size_t length) = 0;
virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0;
virtual std::string utf8(const String&) = 0;
virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length);
// 构建JS对象的方法
virtual Object createObject() = 0;
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 hasNativeState(const jsi::Object&) = 0;
virtual std::shared_ptr<NativeState> getNativeState(const jsi::Object&) = 0;
virtual void setNativeState(
const jsi::Object&,
std::shared_ptr<NativeState> state) = 0;
// 获取或者设置属性
virtual Value getProperty(const Object&, const PropNameID& name) = 0;
virtual Value getProperty(const Object&, const String& name) = 0;
virtual bool hasProperty(const Object&, const PropNameID& name) = 0;
virtual bool hasProperty(const Object&, const String& name) = 0;
virtual void setPropertyValue(
const Object&,
const PropNameID& name,
const Value& value) = 0;
virtual void
setPropertyValue(const Object&, const String& name, const Value& value) = 0;
// 判断数据类型
virtual bool isArray(const Object&) const = 0;
virtual bool isArrayBuffer(const Object&) const = 0;
virtual bool isFunction(const Object&) const = 0;
virtual bool isHostObject(const jsi::Object&) const = 0;
virtual bool isHostFunction(const jsi::Function&) const = 0;
virtual Array getPropertyNames(const Object&) = 0;
virtual WeakObject createWeakObject(const Object&) = 0;
virtual Value lockWeakObject(const WeakObject&) = 0;
virtual Array createArray(size_t length) = 0;
virtual ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) = 0;
virtual size_t size(const Array&) = 0;
virtual size_t size(const ArrayBuffer&) = 0;
virtual uint8_t* data(const ArrayBuffer&) = 0;
virtual Value getValueAtIndex(const Array&, size_t i) = 0;
virtual void
setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0;
// 创建方法
virtual Function createFunctionFromHostFunction(
const PropNameID& name,
unsigned int paramCount,
HostFunctionType func) = 0;
virtual Value call(
const Function&,
const Value& jsThis,
const Value* args,
size_t count) = 0;
virtual Value
callAsConstructor(const Function&, const Value* args, size_t count) = 0;
// Private data for managing scopes.
struct ScopeState;
virtual ScopeState* pushScope();
virtual void popScope(ScopeState*);
virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0;
virtual bool strictEquals(const BigInt& a, const BigInt& b) const = 0;
virtual bool strictEquals(const String& a, const String& b) const = 0;
virtual bool strictEquals(const Object& a, const Object& b) const = 0;
virtual bool instanceOf(const Object& o, const Function& f) = 0;
virtual void setExternalMemoryPressure(
const jsi::Object& obj,
size_t amount) = 0;
template <typename T>
static T make(PointerValue* pv);
static PointerValue* getPointerValue(Pointer& pointer);
static const PointerValue* getPointerValue(const Pointer& pointer);
static const PointerValue* getPointerValue(const Value& value);
friend class ::FBJSRuntime;
template <typename Plain, typename Base>
friend class RuntimeDecorator;
};
JSI使用
我们以上一章提到过的TurboModule来作为例子,来介绍怎么JSI是怎么使用的。
我们之前介绍过TurboModule是继承于jni::HostObject,HostObject是可以在JS运行时添加到JS引擎中的对象,我们先看看HostObject的定义。
class JSI_EXPORT HostObject {
public:
virtual ~HostObject();
// 根据PropNameID获取对应的Value,JS调用对象的对应属性就会通过这里来获取
virtual Value get(Runtime&, const PropNameID& name);
// 配置PropNameID对应的Value
virtual void set(Runtime&, const PropNameID& name, const Value& value);
// 当JS层需要获取一个属性列表,会调用该方法。
virtual std::vector<PropNameID> getPropertyNames(Runtime& rt);
};
再来看看TurboModule是怎么实现这几个方法。
先看看get方法:
facebook::jsi::Value get(
facebook::jsi::Runtime& runtime,
const facebook::jsi::PropNameID& propName) override {
{
// 调用create来获取对应propName的Vaule
auto prop = create(runtime, propName);
if (jsRepresentation_ && !prop.isUndefined()) {
jsRepresentation_->lock(runtime).asObject(runtime).setProperty(
runtime, propName, prop);
}
return prop;
}
}
create方法通过methodMap_来查找是否存在对应的属性,TurboModule里面全部都是方法,我们在上一节看过,脚手架生成的代码会把定义的方法都存储在这个methodMap_里面。
这里获取到方法后需要将它封装成jsi::Function才能返回给JSI。JSI只能接收这些JSI自己定义的类型。
virtual jsi::Value create(
jsi::Runtime& runtime,
const jsi::PropNameID& propName) {
std::string propNameUtf8 = propName.utf8(runtime);
// 在methodMap_中根据propName找对应的方法
if (auto methodIter = methodMap_.find(propNameUtf8);
methodIter != methodMap_.end()) {
const MethodMetadata& meta = methodIter->second;
// 找到的方法封装成jsi::Function返回
return jsi::Function::createFromHostFunction(
runtime,
propName,
static_cast<unsigned int>(meta.argCount),
[this, meta](
jsi::Runtime& rt,
[[maybe_unused]] const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) { return meta.invoker(rt, *this, args, count); });
} else if (auto eventEmitterIter = eventEmitterMap_.find(propNameUtf8);
eventEmitterIter != eventEmitterMap_.end()) {
return eventEmitterIter->second->get(runtime, jsInvoker_);
}
// Neither Method nor EventEmitter were not found, let JS decide what to do.
return facebook::jsi::Value::undefined();
}
再来看看getPropertyNames,这个很简单,只要把methodMap_的所有key都收集起来返回就可以了。
virtual std::vector<facebook::jsi::PropNameID> getPropertyNames(
facebook::jsi::Runtime& runtime) override {
std::vector<jsi::PropNameID> result;
result.reserve(methodMap_.size());
for (auto it = methodMap_.cbegin(); it != methodMap_.cend(); ++it) {
result.push_back(jsi::PropNameID::forUtf8(runtime, it->first));
}
return result;
}
TurboModule并没有实现set方法,因为这些方法不应该在JS可以修改,所以这里就没有实现。
小结
本节介绍了jsi::Runtime的定义的一些接口,然后以TurboModule为例介绍了JSI应该如何使用,总体来说还是比较简单的,下一节我们再来看一下Fabric渲染器和Fabric组件。