Implement method dispatch using TurboModuleSchema

Summary:
This is the final diff in the JS TurboModule codegen stack for Android. It implements method dispatch using the TurboModuleSchema object.

Changelog: [Internal]

Reviewed By: fkgozali

Differential Revision: D22837486

fbshipit-source-id: f91b03f064941457d4b8c5e37e011468559dee71
This commit is contained in:
Ramanpreet Nara
2020-11-08 14:02:02 -08:00
committed by Facebook GitHub Bot
parent 165dcccc58
commit 610dcf488b
5 changed files with 104 additions and 6 deletions
@@ -14,6 +14,7 @@
#include <ReactCommon/TurboCxxModule.h>
#include <ReactCommon/TurboModuleBinding.h>
#include <ReactCommon/TurboModulePerfLogger.h>
#include <ReactCommon/TurboModuleSchema.h>
#include <react/jni/JMessageQueueThread.h>
#include "TurboModuleManager.h"
@@ -76,7 +77,8 @@ void TurboModuleManager::installJSIBindings() {
jsCallInvoker_ = std::weak_ptr<CallInvoker>(jsCallInvoker_),
nativeCallInvoker_ = std::weak_ptr<CallInvoker>(nativeCallInvoker_),
delegate_ = jni::make_weak(delegate_),
javaPart_ = jni::make_weak(javaPart_)](
javaPart_ = jni::make_weak(javaPart_),
runtime_ = runtime_](
const std::string &name,
const jsi::Value *schema) -> std::shared_ptr<TurboModule> {
auto turboModuleCache = turboModuleCache_.lock();
@@ -139,6 +141,13 @@ void TurboModuleManager::installJSIBindings() {
.jsInvoker = jsCallInvoker,
.nativeInvoker = nativeCallInvoker};
if (schema->isObject() && !schema->isNull()) {
auto turboModule = std::make_shared<JavaTurboModule>(
params, TurboModuleSchema::parse(*runtime_, name, *schema));
TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
return turboModule;
}
auto turboModule = delegate->cthis()->getTurboModule(name, params);
turboModuleCache->insert({name, turboModule});
TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
@@ -56,6 +56,68 @@ void JavaTurboModule::enablePromiseAsyncDispatch(bool enable) {
isPromiseAsyncDispatchEnabled_ = enable;
}
JavaTurboModule::JavaTurboModule(
const InitParams &params,
TurboModuleSchema &&schema)
: TurboModule(params.moduleName, params.jsInvoker),
instance_(jni::make_global(params.instance)),
nativeInvoker_(params.nativeInvoker),
turboModuleSchema_(std::move(schema)) {}
jsi::Value JavaTurboModule::get(
jsi::Runtime &runtime,
const jsi::PropNameID &propName) {
if (!turboModuleSchema_) {
return TurboModule::get(runtime, propName);
}
std::string methodName = propName.utf8(runtime);
if (!turboModuleSchema_->hasMethod(methodName)) {
return jsi::Value::undefined();
}
using MethodImplStatus = TurboModuleSchema::Method::ImplStatus;
TurboModuleSchema::Method &method = turboModuleSchema_->getMethod(methodName);
if (method.isOptional) {
if (method.implStatus == MethodImplStatus::Unknown) {
auto instance = instance_.get();
JNIEnv *env = jni::Environment::current();
jclass cls = env->GetObjectClass(instance);
jmethodID methodID = env->GetMethodID(
cls, methodName.c_str(), method.jniSignature.c_str());
// If the method signature doesn't match, show a redbox here instead of
// crashing later.
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
method.implStatus = methodID != nullptr ? MethodImplStatus::Implemented
: MethodImplStatus::Unimplemented;
}
if (method.implStatus == MethodImplStatus::Unimplemented) {
return jsi::Value::undefined();
}
}
return jsi::Function::createFromHostFunction(
runtime,
propName,
method.jsParamCount,
[this, method](
facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisVal,
const facebook::jsi::Value *args,
size_t count) {
return invokeJavaMethod(
runtime,
method.jsReturnType,
method.name,
method.jniSignature,
args,
count);
});
}
namespace {
jni::local_ref<JCxxCallbackImpl::JavaPart> createJavaCallbackFromJSIFunction(
jsi::Function &&function,
@@ -13,9 +13,12 @@
#include <ReactCommon/TurboModule.h>
#include <ReactCommon/TurboModuleUtils.h>
#include <fbjni/fbjni.h>
#include <folly/Optional.h>
#include <jsi/jsi.h>
#include <react/jni/JCallback.h>
#include "TurboModuleSchema.h"
namespace facebook {
namespace react {
@@ -41,7 +44,9 @@ class JSI_EXPORT JavaTurboModule : public TurboModule {
};
JavaTurboModule(const InitParams &params);
JavaTurboModule(const InitParams &params, TurboModuleSchema &&schema);
virtual ~JavaTurboModule();
jsi::Value invokeJavaMethod(
jsi::Runtime &runtime,
TurboModuleMethodValueKind valueKind,
@@ -51,10 +56,13 @@ class JSI_EXPORT JavaTurboModule : public TurboModule {
size_t argCount);
static void enablePromiseAsyncDispatch(bool enable);
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &propName)
override;
private:
jni::global_ref<JTurboModule> instance_;
std::shared_ptr<CallInvoker> nativeInvoker_;
folly::Optional<TurboModuleSchema> turboModuleSchema_;
/**
* Experiments
@@ -44,6 +44,7 @@ std::ostream &operator<<(std::ostream &os, const TurboModuleSchema &schema) {
<< std::endl;
os << " .isOptional = " << (method.isOptional ? "true" : "false")
<< std::endl;
os << " .jsParamCount = " << method.jsParamCount << "," << std::endl;
os << " }," << std::endl;
}
@@ -96,9 +97,9 @@ bool TurboModuleSchema::hasMethod(const std::string &methodName) const {
return false;
}
const TurboModuleSchema::Method &TurboModuleSchema::getMethod(
const std::string &methodName) const {
for (const Method &method : methods_) {
TurboModuleSchema::Method &TurboModuleSchema::getMethod(
const std::string &methodName) {
for (Method &method : methods_) {
if (method.name == methodName) {
return method;
}
@@ -377,6 +378,8 @@ TurboModuleSchema::Method parseMethod(
.name = methodName,
.jniSignature = "()Ljava/util/Map;",
.isOptional = !isMethodRequired,
.jsParamCount = 0,
.implStatus = TurboModuleSchema::Method::ImplStatus::Unknown,
};
}
@@ -460,6 +463,8 @@ TurboModuleSchema::Method parseMethod(
.name = methodName,
.jniSignature = jniSignature,
.isOptional = !isMethodRequired,
.jsParamCount = numFunctionTypeAnnotationParams,
.implStatus = TurboModuleSchema::Method::ImplStatus::Unknown,
};
}
@@ -18,10 +18,24 @@ namespace react {
class TurboModuleSchema {
public:
struct Method {
/**
* Optional methods might not be implemented on the Java NativeModule class.
* - Unknown: We must check if the method exists using JNI
* - Implemented: Using JNI, we verified that the method exists
* - Unimplemented: Using JNI, we verified that the method doesn't exist
*/
enum class ImplStatus {
Unknown,
Implemented,
Unimplemented,
};
const TurboModuleMethodValueKind jsReturnType;
const std::string name;
const std::string jniSignature;
const bool isOptional;
const size_t jsParamCount;
ImplStatus implStatus;
};
class ParseException : public jsi::JSIException {
@@ -31,7 +45,7 @@ class TurboModuleSchema {
private:
const std::string moduleName_;
const std::vector<Method> methods_;
std::vector<Method> methods_;
TurboModuleSchema(
const std::string &moduleName,
@@ -40,7 +54,7 @@ class TurboModuleSchema {
public:
TurboModuleSchema() = delete;
bool hasMethod(const std::string &methodName) const;
const Method &getMethod(const std::string &methodName) const;
Method &getMethod(const std::string &methodName);
static TurboModuleSchema parse(
jsi::Runtime &runtime,