All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.react.cxxbridge.JavaModuleWrapper Maven / Gradle / Ivy

There is a newer version: 0.52.u
Show newest version
/**
 * 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);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy