com.facebook.react.cxxbridge.JavaModuleWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of react-native Show documentation
Show all versions of react-native Show documentation
A framework for building native apps with React
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.cxxbridge;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.facebook.common.logging.FLog;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.ExecutorToken;
import com.facebook.react.bridge.NativeArray;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.PromiseImpl;
import com.facebook.react.bridge.ReadableNativeArray;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.systrace.Systrace;
import com.facebook.systrace.SystraceMessage;
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
/**
* This is part of the glue which wraps a java BaseJavaModule in a C++
* NativeModule. This could all be in C++, but it's android-specific
* initialization code, and writing it this way is easier to read and means
* fewer JNI calls.
*/
@DoNotStrip
/* package */ class JavaModuleWrapper {
@DoNotStrip
static class MethodDescriptor {
@DoNotStrip
Method method;
@DoNotStrip
String signature;
@DoNotStrip
String name;
@DoNotStrip
String type;
}
private final CatalystInstance mCatalystInstance;
private final ModuleHolder mModuleHolder;
public JavaModuleWrapper(CatalystInstance catalystinstance, ModuleHolder moduleHolder) {
mCatalystInstance = catalystinstance;
mModuleHolder = moduleHolder;
BaseJavaModule module = (BaseJavaModule) moduleHolder.getModule();
AbstractModuleHelper helper = null;
if (mCoreModuleProvider != null) {
helper = mCoreModuleProvider.getModuleHelper(module);
}
if (helper == null && mCustomModuleProvider != null) {
helper = mCustomModuleProvider.getModuleHelper(module);
}
mModuleHelper = helper == null ? new FallbackModuleHelper(module) : helper;
}
@DoNotStrip
public BaseJavaModule getModule() {
return (BaseJavaModule) mModuleHolder.getModule();
}
@DoNotStrip
public String getName() {
return mModuleHolder.getInfo().name();
}
@DoNotStrip
public List getMethodDescriptors() {
return mModuleHelper.getMethodDescriptors();
}
@DoNotStrip
public List newGetMethodDescriptors() {
return mModuleHelper.newGetMethodDescriptors();
}
// TODO mhorowitz: make this return NativeMap, which requires moving
// NativeMap out of OnLoad.
@DoNotStrip
public NativeArray getConstants() {
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "Map constants")
.arg("moduleName", getName())
.flush();
Map map = getModule().getConstants();
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "WritableNativeMap constants")
.arg("moduleName", getName())
.flush();
WritableNativeMap writableNativeMap;
try {
writableNativeMap = Arguments.makeNativeMap(map);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
WritableNativeArray array = new WritableNativeArray();
array.pushMap(writableNativeMap);
return array;
}
@DoNotStrip
public boolean supportsWebWorkers() {
return getModule().supportsWebWorkers();
}
@DoNotStrip
public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters) {
mModuleHelper.invoke(mCatalystInstance, token, methodId, parameters);
}
// Optimization start here
private static ModuleProvider mCoreModuleProvider;
private static ModuleProvider mCustomModuleProvider;
private final AbstractModuleHelper mModuleHelper;
static {
String clsName = JavaModuleWrapper.class.getName();
try {
Class coreHelperCls = Class.forName(clsName + "$CoreModuleProvider");
//noinspection unchecked
mCoreModuleProvider = (ModuleProvider) coreHelperCls.newInstance();
} catch (ClassNotFoundException e) {
FLog.w("JavaModuleHelper", "Could not find generated core module provider");
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate core module provider ", e);
}
try {
Class customHelperCls = Class.forName(clsName + "$CustomModuleProvider");
//noinspection unchecked
mCustomModuleProvider = (ModuleProvider) customHelperCls.newInstance();
} catch (ClassNotFoundException e) {
FLog.w("JavaModuleHelper", "Could not find generated custom module provider");
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate custom module provider ", e);
}
}
/**
* Native module helper, aim to improve performance
* Created by ransj on 30/09/2016.
*
* @author ransj
*/
public interface ModuleHelper {
/**
* @return
*/
List getMethodDescriptors();
/**
* @return
*/
List newGetMethodDescriptors();
/**
* @param catalystInstance
* @param executorToken
* @param methodId
* @param parameters
*/
void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, int methodId, ReadableNativeArray parameters);
}
static abstract class AbstractModuleHelper implements ModuleHelper {
protected void checkMethodSignature(BaseJavaModule module, MethodDescriptor methodInfo) {
String methodName = methodInfo.name;
String signature = methodInfo.signature;
if (module.supportsWebWorkers() && signature.charAt(2) != 'T') {
throw new RuntimeException("Module" + module.getName()
+ " supports web workers, but " + methodName
+ " does not take an ExecutorToken as its first parameter.");
}
if (signature.contains("T") && !module.supportsWebWorkers()) {
throw new RuntimeException("Module" + module.getName()
+ " doesn't support web workers, but "
+ methodName + " takes an ExecutorToken.");
}
}
protected com.facebook.react.bridge.CallbackImpl createCallback(
CatalystInstance catalystinstance, ExecutorToken executorToken, int index, ReadableNativeArray jsArguments) {
if (jsArguments.isNull(index)) {
return null;
} else {
int id = (int) jsArguments.getDouble(index);
return new com.facebook.react.bridge.CallbackImpl(catalystinstance, executorToken, id);
}
}
protected PromiseImpl createPromise(
CatalystInstance catalystinstance, ExecutorToken executorToken, int index, ReadableNativeArray jsArguments) {
Callback resolve = createCallback(catalystinstance, executorToken, index, jsArguments);
Callback reject = createCallback(catalystinstance, executorToken, ++index, jsArguments);
return new PromiseImpl(resolve, reject);
}
}
static interface ModuleProvider {
public AbstractModuleHelper getModuleHelper(BaseJavaModule module);
}
static class FallbackModuleHelper extends AbstractModuleHelper {
private BaseJavaModule mModule;
private final ArrayList mMethods;
public FallbackModuleHelper(BaseJavaModule module) {
mModule = module;
mMethods = new ArrayList();
}
@Override
public List getMethodDescriptors() {
ArrayList descs = new ArrayList<>();
for (Map.Entry entry :
mModule.getMethods().entrySet()) {
MethodDescriptor md = new MethodDescriptor();
md.name = entry.getKey();
md.type = entry.getValue().getType();
BaseJavaModule.JavaMethod method = (BaseJavaModule.JavaMethod) entry.getValue();
mMethods.add(method);
descs.add(md);
}
return descs;
}
@Override
public List newGetMethodDescriptors() {
ArrayList descs = new ArrayList<>();
for (Map.Entry entry :
mModule.getMethods().entrySet()) {
MethodDescriptor md = new MethodDescriptor();
md.name = entry.getKey();
md.type = entry.getValue().getType();
BaseJavaModule.JavaMethod method = (BaseJavaModule.JavaMethod) entry.getValue();
md.method = method.getMethod();
md.signature = method.getSignature();
descs.add(md);
}
for (Map.Entry entry :
mModule.getSyncHooks().entrySet()) {
MethodDescriptor md = new MethodDescriptor();
md.name = entry.getKey();
md.type = BaseJavaModule.METHOD_TYPE_SYNC;
BaseJavaModule.SyncJavaHook method = (BaseJavaModule.SyncJavaHook) entry.getValue();
md.method = method.getMethod();
md.signature = method.getSignature();
descs.add(md);
}
return descs;
}
@Override
public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, int methodId, ReadableNativeArray parameters) {
if (mMethods == null || methodId >= mMethods.size()) {
return;
}
mMethods.get(methodId).invoke(catalystInstance, executorToken, parameters);
}
}
}