Refactor CxxModuleWrapper to support different use cases

Reviewed By: javache

Differential Revision: D4807680

fbshipit-source-id: 48eccfb382814a0c4082f56617e0359e61345da7
This commit is contained in:
Marc Horowitz 2017-04-05 00:51:55 -07:00 committed by Facebook Github Bot
parent 0ec9c93eeb
commit a893d0bb23
11 changed files with 179 additions and 101 deletions

View File

@ -2,70 +2,25 @@
package com.facebook.react.cxxbridge;
import java.util.Map;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.ExecutorToken;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReadableNativeArray;
import com.facebook.soloader.SoLoader;
/**
* A Java Object which represents a cross-platform C++ module
*
* This module implements the NativeModule interface but will never be invoked from Java,
* instead the underlying Cxx module will be extracted by the bridge and called directly.
* This does nothing interesting, except avoid breaking existing code.
*/
@DoNotStrip
public class CxxModuleWrapper implements NativeModule
public class CxxModuleWrapper extends CxxModuleWrapperBase
{
static {
SoLoader.loadLibrary(CatalystInstanceImpl.REACT_NATIVE_LIB);
}
@DoNotStrip
private HybridData mHybridData;
public CxxModuleWrapper(String library, String factory) {
SoLoader.loadLibrary(library);
mHybridData =
initHybrid(SoLoader.unpackLibraryAndDependencies(library).getAbsolutePath(), factory);
}
@Override
public native String getName();
@Override
public Map<String, NativeMethod> getMethods() {
throw new UnsupportedOperationException();
}
@Override
public void initialize() {
// do nothing
}
@Override
public boolean canOverrideExistingModule() {
return false;
}
@Override
public boolean supportsWebWorkers() {
return false;
}
@Override
public void onCatalystInstanceDestroy() {
mHybridData.resetNative();
}
// For creating a wrapper from C++, or from a derived class.
protected CxxModuleWrapper(HybridData hd) {
mHybridData = hd;
super(hd);
}
private native HybridData initHybrid(String soPath, String factory);
private static native CxxModuleWrapper makeDsoNative(String soPath, String factory);
public static CxxModuleWrapper makeDso(String library, String factory) {
SoLoader.loadLibrary(library);
String soPath = SoLoader.unpackLibraryAndDependencies(library).getAbsolutePath();
return makeDsoNative(soPath, factory);
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.cxxbridge;
import java.util.Map;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.NativeModule;
import com.facebook.soloader.SoLoader;
/**
* A Java Object which represents a cross-platform C++ module
*
* This module implements the NativeModule interface but will never be invoked from Java,
* instead the underlying Cxx module will be extracted by the bridge and called directly.
*/
@DoNotStrip
public class CxxModuleWrapperBase implements NativeModule
{
static {
SoLoader.loadLibrary(CatalystInstanceImpl.REACT_NATIVE_LIB);
}
@DoNotStrip
private HybridData mHybridData;
@Override
public native String getName();
@Override
public Map<String, NativeMethod> getMethods() {
throw new UnsupportedOperationException();
}
@Override
public void initialize() {
// do nothing
}
@Override
public boolean canOverrideExistingModule() {
return false;
}
@Override
public boolean supportsWebWorkers() {
return false;
}
@Override
public void onCatalystInstanceDestroy() {
mHybridData.resetNative();
}
// For creating a wrapper from C++, or from a derived class.
protected CxxModuleWrapperBase(HybridData hd) {
mHybridData = hd;
}
}

View File

@ -46,7 +46,7 @@ public class NativeModuleRegistry {
ArrayList<JavaModuleWrapper> javaModules = new ArrayList<>();
for (Map.Entry<Class<? extends NativeModule>, ModuleHolder> entry : mModules.entrySet()) {
Class<?> type = entry.getKey();
if (!CxxModuleWrapper.class.isAssignableFrom(type)) {
if (!CxxModuleWrapperBase.class.isAssignableFrom(type)) {
javaModules.add(new JavaModuleWrapper(jsInstance, entry.getValue()));
}
}
@ -57,7 +57,7 @@ public class NativeModuleRegistry {
ArrayList<ModuleHolder> cxxModules = new ArrayList<>();
for (Map.Entry<Class<? extends NativeModule>, ModuleHolder> entry : mModules.entrySet()) {
Class<?> type = entry.getKey();
if (CxxModuleWrapper.class.isAssignableFrom(type)) {
if (CxxModuleWrapperBase.class.isAssignableFrom(type)) {
cxxModules.add(entry.getValue());
}
}

View File

@ -2,6 +2,8 @@ include_defs("//ReactAndroid/DEFS")
EXPORTED_HEADERS = [
"CxxModuleWrapper.h",
"CxxModuleWrapperBase.h",
"CxxSharedModuleWrapper.h",
"JavaModuleWrapper.h",
"JExecutorToken.h",
"JSLoader.h",

View File

@ -2,27 +2,18 @@
#include "CxxModuleWrapper.h"
#include <fb/fbjni.h>
#include <fb/Environment.h>
#include <folly/ScopeGuard.h>
#include <cxxreact/CxxModule.h>
#include <dlfcn.h>
using namespace facebook::jni;
using namespace facebook::xplat::module;
using namespace facebook::react;
void CxxModuleWrapper::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", CxxModuleWrapper::initHybrid),
makeNativeMethod("getName", CxxModuleWrapper::getName)
});
}
namespace facebook {
namespace react {
CxxModuleWrapper::CxxModuleWrapper(const std::string& soPath, const std::string& fname) {
jni::local_ref<CxxModuleWrapper::javaobject> CxxModuleWrapper::makeDsoNative(
jni::alias_ref<jclass>, const std::string& soPath, const std::string& fname) {
// soPath is the path of a library which has already been loaded by
// java SoLoader.loadLibrary(). So this returns the same handle,
// and increments the reference counter. We can't just use
@ -46,9 +37,9 @@ CxxModuleWrapper::CxxModuleWrapper(const std::string& soPath, const std::string&
fname.c_str(), soPath.c_str());
}
auto factory = reinterpret_cast<CxxModule* (*)()>(sym);
module_.reset((*factory)());
return CxxModuleWrapper::newObjectCxxArgs(std::unique_ptr<CxxModule>((*factory)()));
}
std::string CxxModuleWrapper::getName() {
return module_->getName();
}
}

View File

@ -2,43 +2,31 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <cxxreact/CxxModule.h>
#include <fb/fbjni.h>
#include "CxxModuleWrapperBase.h"
namespace facebook {
namespace react {
struct JNativeModule : jni::JavaClass<JNativeModule> {
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/bridge/NativeModule;";
};
class CxxModuleWrapper :
public jni::HybridClass<CxxModuleWrapper, JNativeModule> {
class CxxModuleWrapper : public jni::HybridClass<CxxModuleWrapper, CxxModuleWrapperBase> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/cxxbridge/CxxModuleWrapper;";
static void registerNatives();
CxxModuleWrapper(const std::string& soPath, const std::string& fname);
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject>, const std::string& soPath, const std::string& fname) {
return makeCxxInstance(soPath, fname);
static void registerNatives() {
registerHybrid({
makeNativeMethod("makeDsoNative", CxxModuleWrapper::makeDsoNative)
});
}
// JNI methods
std::string getName();
static jni::local_ref<CxxModuleWrapper::javaobject> makeDsoNative(
jni::alias_ref<jclass>, const std::string& soPath, const std::string& fname);
std::string getName() override {
return module_->getName();
}
// This steals ownership of the underlying module for use by the C++ bridge
std::unique_ptr<xplat::module::CxxModule> getModule() {
// TODO mhorowitz: remove this (and a lot of other code) once the java
// bridge is dead.
std::unique_ptr<xplat::module::CxxModule> getModule() override {
return std::move(module_);
}

View File

@ -0,0 +1,43 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <string>
#include <cxxreact/CxxModule.h>
#include <fb/fbjni.h>
namespace facebook {
namespace react {
struct JNativeModule : jni::JavaClass<JNativeModule> {
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/bridge/NativeModule;";
};
/**
* The C++ part of a CxxModuleWrapper is not a unique class, but it
* must extend this base class.
*/
class CxxModuleWrapperBase
: public jni::HybridClass<CxxModuleWrapperBase, JNativeModule> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/cxxbridge/CxxModuleWrapperBase;";
static void registerNatives() {
registerHybrid({
makeNativeMethod("getName", CxxModuleWrapperBase::getName)
});
}
// JNI method
virtual std::string getName() = 0;
// Called by ModuleRegistryBuilder
virtual std::unique_ptr<xplat::module::CxxModule> getModule() = 0;
};
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cxxreact/SharedProxyCxxModule.h>
#include "CxxModuleWrapperBase.h"
namespace facebook {
namespace react {
class CxxSharedModuleWrapper: public CxxModuleWrapperBase {
public:
std::string getName() override {
return shared_->getName();
}
std::unique_ptr<xplat::module::CxxModule> getModule() override {
// Instead of just moving out the stored CxxModule, this creates a
// proxy which passes calls to the shared stored CxxModule.
return std::make_unique<xplat::module::SharedProxyCxxModule>(shared_);
}
protected:
explicit CxxSharedModuleWrapper(std::unique_ptr<xplat::module::CxxModule> module)
: shared_(std::move(module)) {}
std::shared_ptr<xplat::module::CxxModule> shared_;
};
}
}

View File

@ -21,9 +21,9 @@ xplat::module::CxxModule::Provider ModuleHolder::getProvider() const {
// This is the call which uses the lazy Java Provider to instantiate the
// Java CxxModuleWrapper which contains the CxxModule.
auto module = method(self);
CHECK(module->isInstanceOf(CxxModuleWrapper::javaClassStatic()))
CHECK(module->isInstanceOf(CxxModuleWrapperBase::javaClassStatic()))
<< "module isn't a C++ module";
auto cxxModule = jni::static_ref_cast<CxxModuleWrapper::javaobject>(module);
auto cxxModule = jni::static_ref_cast<CxxModuleWrapperBase::javaobject>(module);
// Then, we grab the CxxModule from the wrapper, which is no longer needed.
return cxxModule->cthis()->getModule();
};

View File

@ -9,6 +9,7 @@
#include <cxxreact/Platform.h>
#include <jschelpers/Value.h>
#include "CatalystInstanceImpl.h"
#include "CxxModuleWrapper.h"
#include "JavaScriptExecutorHolder.h"
#include "JSCPerfLogging.h"
#include "JSLoader.h"
@ -167,6 +168,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JSCJavaScriptExecutorHolder::registerNatives();
ProxyJavaScriptExecutorHolder::registerNatives();
CatalystInstanceImpl::registerNatives();
CxxModuleWrapperBase::registerNatives();
CxxModuleWrapper::registerNatives();
JCallbackImpl::registerNatives();
#ifdef WITH_INSPECTOR

View File

@ -16,7 +16,11 @@ using facebook::jni::alias_ref;
namespace {
// This is a wrapper around the Java proxy to the javascript module. This
// allows us to call functions on the js module from c++.
// allows us to call functions on the js module from c++. Are you seeing
// crashes in this class? Android 6+ crashes when you try to call a
// method on a Proxy. Switch to an older version of Android. If you're
// really desperate, you can fix this by using ToReflectedMethod on the
// underlying jmethodid and invoking that.
class JavaJSModule : public jni::JavaClass<JavaJSModule> {
public:
static constexpr auto kJavaDescriptor =