Delete old bridge

Reviewed By: astreet

Differential Revision: D3510660

fbshipit-source-id: 031b9dcf19dd4e6677a6c9417917930bcbbe3219
This commit is contained in:
Chris Hopman 2016-08-02 17:56:11 -07:00 committed by Facebook Github Bot 3
parent 5617d41327
commit 1a690d5674
69 changed files with 95 additions and 7235 deletions

View File

@ -13,6 +13,7 @@ android_library(
react_native_dep('third-party/java/testing-support-lib:runner'),
react_native_target('java/com/facebook/react/bridge:bridge'),
react_native_target('java/com/facebook/react/common:common'),
react_native_target('java/com/facebook/react/cxxbridge:bridge'),
react_native_target('java/com/facebook/react/devsupport:devsupport'),
react_native_target('java/com/facebook/react/modules/core:core'),
react_native_target('java/com/facebook/react/modules/debug:debug'),

View File

@ -25,7 +25,6 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactRootView;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.uimanager.UIImplementationProvider;

View File

@ -20,13 +20,14 @@ import android.view.ViewGroup;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.CatalystInstanceImpl;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.JSCJavaScriptExecutor;
import com.facebook.react.cxxbridge.CatalystInstanceImpl;
import com.facebook.react.cxxbridge.JSBundleLoader;
import com.facebook.react.cxxbridge.NativeModuleRegistry;
import com.facebook.react.cxxbridge.JSCJavaScriptExecutor;
import com.facebook.react.cxxbridge.JavaScriptExecutor;
import com.facebook.react.bridge.JavaScriptModuleRegistry;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.NativeModuleRegistry;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
@ -61,9 +62,15 @@ public class ReactTestHelper {
@Override
public CatalystInstance build() {
JavaScriptExecutor executor = null;
try {
executor = new JSCJavaScriptExecutor.Factory(new WritableNativeMap()).create();
} catch (Exception e) {
throw new RuntimeException(e);
}
return new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(new JSCJavaScriptExecutor(new WritableNativeMap()))
.setJSExecutor(executor)
.setRegistry(mNativeModuleRegistryBuilder.build())
.setJSModuleRegistry(mJSModuleRegistryBuilder.build())
.setJSBundleLoader(JSBundleLoader.createFileLoader(

View File

@ -202,7 +202,6 @@ public abstract class ReactInstanceManager {
protected @Nullable Activity mCurrentActivity;
protected @Nullable DefaultHardwareBackBtnHandler mDefaultHardwareBackBtnHandler;
protected @Nullable RedBoxHandler mRedBoxHandler;
protected boolean mUseOldBridge;
protected Builder() {
}
@ -330,11 +329,6 @@ public abstract class ReactInstanceManager {
return this;
}
public Builder setUseOldBridge(boolean enable) {
mUseOldBridge = enable;
return this;
}
/**
* Instantiates a new {@link ReactInstanceManagerImpl}.
* Before calling {@code build}, the following must be called:
@ -350,10 +344,6 @@ public abstract class ReactInstanceManager {
mApplication,
"Application property has not been set with this builder");
Assertions.assertCondition(
mJSBundleLoader == null || !mUseOldBridge,
"JSBundleLoader can't be used with the old bridge");
Assertions.assertCondition(
mUseDeveloperSupport || mJSBundleFile != null || mJSBundleLoader != null,
"JS Bundle File has to be provided when dev support is disabled");
@ -367,22 +357,6 @@ public abstract class ReactInstanceManager {
mUIImplementationProvider = new UIImplementationProvider();
}
if (mUseOldBridge) {
return new ReactInstanceManagerImpl(
mApplication,
mCurrentActivity,
mDefaultHardwareBackBtnHandler,
mJSBundleFile,
mJSMainModuleName,
mPackages,
mUseDeveloperSupport,
mBridgeIdleDebugListener,
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
mUIImplementationProvider,
mNativeModuleCallExceptionHandler,
mJSCConfig,
mRedBoxHandler);
} else {
return new XReactInstanceManagerImpl(
mApplication,
mCurrentActivity,
@ -401,4 +375,3 @@ public abstract class ReactInstanceManager {
}
}
}
}

View File

@ -1,942 +0,0 @@
/**
* 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;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.net.Uri;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.CatalystInstanceImpl;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.JSCJavaScriptExecutor;
import com.facebook.react.bridge.JavaJSExecutor;
import com.facebook.react.bridge.JavaScriptExecutor;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.JavaScriptModuleRegistry;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.NativeModuleRegistry;
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
import com.facebook.react.bridge.ProxyJavaScriptExecutor;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
import com.facebook.react.common.ApplicationHolder;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.devsupport.DevServerHelper;
import com.facebook.react.devsupport.DevSupportManager;
import com.facebook.react.devsupport.DevSupportManagerFactory;
import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
import com.facebook.react.devsupport.RedBoxHandler;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
import com.facebook.react.uimanager.AppRegistry;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.soloader.SoLoader;
import com.facebook.systrace.Systrace;
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_NATIVE_MODULE_REGISTRY_END;
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_NATIVE_MODULE_REGISTRY_START;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_CATALYST_INSTANCE_END;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_CATALYST_INSTANCE_START;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_REACT_CONTEXT_START;
import static com.facebook.react.bridge.ReactMarkerConstants.PROCESS_PACKAGES_END;
import static com.facebook.react.bridge.ReactMarkerConstants.PROCESS_PACKAGES_START;
import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START;
/**
* This class manages instances of {@link CatalystInstance}. It exposes a way to configure
* catalyst instances using {@link ReactPackage} and keeps track of the lifecycle of that
* instance. It also sets up connection between the instance and developers support functionality
* of the framework.
*
* An instance of this manager is required to start JS application in {@link ReactRootView} (see
* {@link ReactRootView#startReactApplication} for more info).
*
* The lifecycle of the instance of {@link ReactInstanceManagerImpl} should be bound to the activity
* that owns the {@link ReactRootView} that is used to render react application using this
* instance manager (see {@link ReactRootView#startReactApplication}). It's required to pass
* owning activity's lifecycle events to the instance manager (see {@link #onHostPause},
* {@link #onHostDestroy} and {@link #onHostResume}).
*
* To instantiate an instance of this class use {@link #builder}.
*/
/* package */ class ReactInstanceManagerImpl extends ReactInstanceManager {
/* should only be accessed from main thread (UI thread) */
private final List<ReactRootView> mAttachedRootViews = new ArrayList<>();
private LifecycleState mLifecycleState;
private @Nullable ReactContextInitParams mPendingReactContextInitParams;
private @Nullable ReactContextInitAsyncTask mReactContextInitAsyncTask;
/* accessed from any thread */
private @Nullable String mJSBundleFile; /* path to JS bundle on file system */
private final @Nullable String mJSMainModuleName; /* path to JS bundle root on packager server */
private final List<ReactPackage> mPackages;
private final DevSupportManager mDevSupportManager;
private final boolean mUseDeveloperSupport;
private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener;
private @Nullable volatile ReactContext mCurrentReactContext;
private final Context mApplicationContext;
private @Nullable DefaultHardwareBackBtnHandler mDefaultBackButtonImpl;
private String mSourceUrl;
private @Nullable Activity mCurrentActivity;
private final Collection<ReactInstanceEventListener> mReactInstanceEventListeners =
Collections.synchronizedSet(new HashSet<ReactInstanceEventListener>());
private volatile boolean mHasStartedCreatingInitialContext = false;
private final UIImplementationProvider mUIImplementationProvider;
private final MemoryPressureRouter mMemoryPressureRouter;
private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
private final @Nullable JSCConfig mJSCConfig;
private @Nullable RedBoxHandler mRedBoxHandler;
private final ReactInstanceDevCommandsHandler mDevInterface =
new ReactInstanceDevCommandsHandler() {
@Override
public void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
ReactInstanceManagerImpl.this.onReloadWithJSDebugger(jsExecutorFactory);
}
@Override
public void onJSBundleLoadedFromServer() {
ReactInstanceManagerImpl.this.onJSBundleLoadedFromServer();
}
@Override
public void toggleElementInspector() {
ReactInstanceManagerImpl.this.toggleElementInspector();
}
};
private final DefaultHardwareBackBtnHandler mBackBtnHandler =
new DefaultHardwareBackBtnHandler() {
@Override
public void invokeDefaultOnBackPressed() {
ReactInstanceManagerImpl.this.invokeDefaultOnBackPressed();
}
};
private class ReactContextInitParams {
private final JavaScriptExecutor.Factory mJsExecutorFactory;
private final JSBundleLoader mJsBundleLoader;
public ReactContextInitParams(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
mJsExecutorFactory = Assertions.assertNotNull(jsExecutorFactory);
mJsBundleLoader = Assertions.assertNotNull(jsBundleLoader);
}
public JavaScriptExecutor.Factory getJsExecutorFactory() {
return mJsExecutorFactory;
}
public JSBundleLoader getJsBundleLoader() {
return mJsBundleLoader;
}
}
/*
* Task class responsible for (re)creating react context in the background. These tasks can only
* be executing one at time, see {@link #recreateReactContextInBackground()}.
*/
private final class ReactContextInitAsyncTask extends
AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {
@Override
protected void onPreExecute() {
if (mCurrentReactContext != null) {
tearDownReactContext(mCurrentReactContext);
mCurrentReactContext = null;
}
}
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
try {
JavaScriptExecutor jsExecutor =
params[0].getJsExecutorFactory().create(
mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap());
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
// Pass exception to onPostExecute() so it can be handled on the main thread
return Result.of(e);
}
}
@Override
protected void onPostExecute(Result<ReactApplicationContext> result) {
try {
setupReactContext(result.get());
} catch (Exception e) {
mDevSupportManager.handleException(e);
} finally {
mReactContextInitAsyncTask = null;
}
// Handle enqueued request to re-initialize react context.
if (mPendingReactContextInitParams != null) {
recreateReactContextInBackground(
mPendingReactContextInitParams.getJsExecutorFactory(),
mPendingReactContextInitParams.getJsBundleLoader());
mPendingReactContextInitParams = null;
}
}
@Override
protected void onCancelled(Result<ReactApplicationContext> reactApplicationContextResult) {
try {
mMemoryPressureRouter.destroy(reactApplicationContextResult.get());
} catch (Exception e) {
FLog.w(ReactConstants.TAG, "Caught exception after cancelling react context init", e);
} finally {
mReactContextInitAsyncTask = null;
}
}
}
private static class Result<T> {
@Nullable private final T mResult;
@Nullable private final Exception mException;
public static <T, U extends T> Result<T> of(U result) {
return new Result<T>(result);
}
public static <T> Result<T> of(Exception exception) {
return new Result<>(exception);
}
private Result(T result) {
mException = null;
mResult = result;
}
private Result(Exception exception) {
mException = exception;
mResult = null;
}
public T get() throws Exception {
if (mException != null) {
throw mException;
}
Assertions.assertNotNull(mResult);
return mResult;
}
}
/* package */ ReactInstanceManagerImpl(
Context applicationContext,
@Nullable Activity currentActivity,
@Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler,
@Nullable String jsBundleFile,
@Nullable String jsMainModuleName,
List<ReactPackage> packages,
boolean useDeveloperSupport,
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
LifecycleState initialLifecycleState,
UIImplementationProvider uiImplementationProvider,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler,
@Nullable JSCConfig jscConfig) {
this(applicationContext,
currentActivity,
defaultHardwareBackBtnHandler,
jsBundleFile,
jsMainModuleName,
packages,
useDeveloperSupport,
bridgeIdleDebugListener,
initialLifecycleState,
uiImplementationProvider,
nativeModuleCallExceptionHandler,
jscConfig,
null);
}
/* package */ ReactInstanceManagerImpl(
Context applicationContext,
@Nullable Activity currentActivity,
@Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler,
@Nullable String jsBundleFile,
@Nullable String jsMainModuleName,
List<ReactPackage> packages,
boolean useDeveloperSupport,
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
LifecycleState initialLifecycleState,
UIImplementationProvider uiImplementationProvider,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler,
@Nullable JSCConfig jscConfig,
@Nullable RedBoxHandler redBoxHandler) {
initializeSoLoaderIfNecessary(applicationContext);
// TODO(9577825): remove this
ApplicationHolder.setApplication((Application) applicationContext.getApplicationContext());
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext);
mApplicationContext = applicationContext;
mCurrentActivity = currentActivity;
mDefaultBackButtonImpl = defaultHardwareBackBtnHandler;
mJSBundleFile = jsBundleFile;
mJSMainModuleName = jsMainModuleName;
mPackages = packages;
mUseDeveloperSupport = useDeveloperSupport;
mRedBoxHandler = redBoxHandler;
mDevSupportManager = DevSupportManagerFactory.create(
applicationContext,
mDevInterface,
mJSMainModuleName,
useDeveloperSupport,
mRedBoxHandler);
mBridgeIdleDebugListener = bridgeIdleDebugListener;
mLifecycleState = initialLifecycleState;
mUIImplementationProvider = uiImplementationProvider;
mMemoryPressureRouter = new MemoryPressureRouter(applicationContext);
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mJSCConfig = jscConfig;
}
@Override
public DevSupportManager getDevSupportManager() {
return mDevSupportManager;
}
@Override
public MemoryPressureRouter getMemoryPressureRouter() {
return mMemoryPressureRouter;
}
private static void initializeSoLoaderIfNecessary(Context applicationContext) {
// Call SoLoader.initialize here, this is required for apps that does not use exopackage and
// does not use SoLoader for loading other native code except from the one used by React Native
// This way we don't need to require others to have additional initialization code and to
// subclass android.app.Application.
// Method SoLoader.init is idempotent, so if you wish to use native exopackage, just call
// SoLoader.init with appropriate args before initializing ReactInstanceManagerImpl
SoLoader.init(applicationContext, /* native exopackage */ false);
}
/**
* Trigger react context initialization asynchronously in a background async task. This enables
* applications to pre-load the application JS, and execute global code before
* {@link ReactRootView} is available and measured. This should only be called the first time the
* application is set up, which is enforced to keep developers from accidentally creating their
* application multiple times without realizing it.
*
* Called from UI thread.
*/
@Override
public void createReactContextInBackground() {
Assertions.assertCondition(
!mHasStartedCreatingInitialContext,
"createReactContextInBackground should only be called when creating the react " +
"application for the first time. When reloading JS, e.g. from a new file, explicitly " +
"use recreateReactContextInBackground");
mHasStartedCreatingInitialContext = true;
recreateReactContextInBackgroundInner();
}
/**
* Recreate the react application and context. This should be called if configuration has
* changed or the developer has requested the app to be reloaded. It should only be called after
* an initial call to createReactContextInBackground.
*
* Called from UI thread.
*/
public void recreateReactContextInBackground() {
Assertions.assertCondition(
mHasStartedCreatingInitialContext,
"recreateReactContextInBackground should only be called after the initial " +
"createReactContextInBackground call.");
recreateReactContextInBackgroundInner();
}
private void recreateReactContextInBackgroundInner() {
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModuleName != null) {
if (mDevSupportManager.hasUpToDateJSBundleInCache()) {
// If there is a up-to-date bundle downloaded from server, always use that
onJSBundleLoadedFromServer();
} else if (mJSBundleFile == null) {
mDevSupportManager.handleReloadJS();
} else {
mDevSupportManager.isPackagerRunning(
new DevServerHelper.PackagerStatusCallback() {
@Override
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (packagerIsRunning) {
mDevSupportManager.handleReloadJS();
} else {
recreateReactContextInBackgroundFromBundleFile();
}
}
});
}
});
}
return;
}
recreateReactContextInBackgroundFromBundleFile();
}
private void recreateReactContextInBackgroundFromBundleFile() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(),
JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile));
}
/**
* @return whether createReactContextInBackground has been called. Will return false after
* onDestroy until a new initial context has been created.
*/
public boolean hasStartedCreatingInitialContext() {
return mHasStartedCreatingInitialContext;
}
/**
* This method will give JS the opportunity to consume the back button event. If JS does not
* consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS.
*/
@Override
public void onBackPressed() {
UiThreadUtil.assertOnUiThread();
ReactContext reactContext = mCurrentReactContext;
if (mCurrentReactContext == null) {
// Invoke without round trip to JS.
FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
invokeDefaultOnBackPressed();
} else {
DeviceEventManagerModule deviceEventManagerModule =
Assertions.assertNotNull(reactContext).getNativeModule(DeviceEventManagerModule.class);
deviceEventManagerModule.emitHardwareBackPressed();
}
}
private void invokeDefaultOnBackPressed() {
UiThreadUtil.assertOnUiThread();
if (mDefaultBackButtonImpl != null) {
mDefaultBackButtonImpl.invokeDefaultOnBackPressed();
}
}
@Override
public void onNewIntent(Intent intent) {
if (mCurrentReactContext == null) {
FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
} else {
String action = intent.getAction();
Uri uri = intent.getData();
if (Intent.ACTION_VIEW.equals(action) && uri != null) {
DeviceEventManagerModule deviceEventManagerModule =
Assertions.assertNotNull(mCurrentReactContext).getNativeModule(DeviceEventManagerModule.class);
deviceEventManagerModule.emitNewIntentReceived(uri);
}
mCurrentReactContext.onNewIntent(mCurrentActivity, intent);
}
}
private void toggleElementInspector() {
if (mCurrentReactContext != null) {
mCurrentReactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("toggleElementInspector", null);
}
}
@Override
public void onHostPause() {
UiThreadUtil.assertOnUiThread();
mDefaultBackButtonImpl = null;
if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false);
}
moveToBeforeResumeLifecycleState();
mCurrentActivity = null;
}
/**
* Use this method when the activity resumes to enable invoking the back button directly from JS.
*
* This method retains an instance to provided mDefaultBackButtonImpl. Thus it's
* important to pass from the activity instance that owns this particular instance of {@link
* ReactInstanceManagerImpl}, so that once this instance receive {@link #onHostDestroy} event it will
* clear the reference to that defaultBackButtonImpl.
*
* @param defaultBackButtonImpl a {@link DefaultHardwareBackBtnHandler} from an Activity that owns
* this instance of {@link ReactInstanceManagerImpl}.
*/
@Override
public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) {
UiThreadUtil.assertOnUiThread();
mDefaultBackButtonImpl = defaultBackButtonImpl;
if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(true);
}
mCurrentActivity = activity;
moveToResumedLifecycleState(false);
}
@Override
public void onHostDestroy() {
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false);
}
moveToBeforeCreateLifecycleState();
mCurrentActivity = null;
}
@Override
public void destroy() {
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false);
}
moveToBeforeCreateLifecycleState();
if (mReactContextInitAsyncTask != null) {
mReactContextInitAsyncTask.cancel(true);
}
mMemoryPressureRouter.destroy(mApplicationContext);
if (mCurrentReactContext != null) {
mCurrentReactContext.destroy();
mCurrentReactContext = null;
mHasStartedCreatingInitialContext = false;
}
mCurrentActivity = null;
ResourceDrawableIdHelper.getInstance().clear();
}
private void moveToResumedLifecycleState(boolean force) {
if (mCurrentReactContext != null) {
// we currently don't have an onCreate callback so we call onResume for both transitions
if (force ||
mLifecycleState == LifecycleState.BEFORE_RESUME ||
mLifecycleState == LifecycleState.BEFORE_CREATE) {
mCurrentReactContext.onHostResume(mCurrentActivity);
}
}
mLifecycleState = LifecycleState.RESUMED;
}
private void moveToBeforeResumeLifecycleState() {
if (mCurrentReactContext != null) {
if (mLifecycleState == LifecycleState.BEFORE_CREATE) {
mCurrentReactContext.onHostResume(mCurrentActivity);
mCurrentReactContext.onHostPause();
} else if (mLifecycleState == LifecycleState.RESUMED) {
mCurrentReactContext.onHostPause();
}
}
mLifecycleState = LifecycleState.BEFORE_RESUME;
}
private void moveToBeforeCreateLifecycleState() {
if (mCurrentReactContext != null) {
if (mLifecycleState == LifecycleState.RESUMED) {
mCurrentReactContext.onHostPause();
mLifecycleState = LifecycleState.BEFORE_RESUME;
}
if (mLifecycleState == LifecycleState.BEFORE_RESUME) {
mCurrentReactContext.onHostDestroy();
}
}
mLifecycleState = LifecycleState.BEFORE_CREATE;
}
public LifecycleState getLifecycleState() {
return mLifecycleState;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mCurrentReactContext != null) {
mCurrentReactContext.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void showDevOptionsDialog() {
UiThreadUtil.assertOnUiThread();
mDevSupportManager.showDevOptionsDialog();
}
/**
* Get the URL where the last bundle was loaded from.
*/
@Override
public String getSourceUrl() {
return Assertions.assertNotNull(mSourceUrl);
}
@Override
public @Nullable String getJSBundleFile() {
return mJSBundleFile;
}
/**
* Attach given {@param rootView} to a catalyst instance manager and start JS application using
* JS module provided by {@link ReactRootView#getJSModuleName}. If the react context is currently
* being (re)-created, or if react context has not been created yet, the JS application associated
* with the provided root view will be started asynchronously, i.e this method won't block.
* This view will then be tracked by this manager and in case of catalyst instance restart it will
* be re-attached.
*/
@Override
public void attachMeasuredRootView(ReactRootView rootView) {
UiThreadUtil.assertOnUiThread();
mAttachedRootViews.add(rootView);
// If react context is being created in the background, JS application will be started
// automatically when creation completes, as root view is part of the attached root view list.
if (mReactContextInitAsyncTask == null && mCurrentReactContext != null) {
attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
}
}
/**
* Detach given {@param rootView} from current catalyst instance. It's safe to call this method
* multiple times on the same {@param rootView} - in that case view will be detached with the
* first call.
*/
@Override
public void detachRootView(ReactRootView rootView) {
UiThreadUtil.assertOnUiThread();
if (mAttachedRootViews.remove(rootView)) {
if (mCurrentReactContext != null && mCurrentReactContext.hasActiveCatalystInstance()) {
detachViewFromInstance(rootView, mCurrentReactContext.getCatalystInstance());
}
}
}
/**
* Uses configured {@link ReactPackage} instances to create all view managers
*/
@Override
public List<ViewManager> createAllViewManagers(
ReactApplicationContext catalystApplicationContext) {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createAllViewManagers");
try {
List<ViewManager> allViewManagers = new ArrayList<>();
for (ReactPackage reactPackage : mPackages) {
allViewManagers.addAll(reactPackage.createViewManagers(catalystApplicationContext));
}
return allViewManagers;
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
@Override
public void addReactInstanceEventListener(ReactInstanceEventListener listener) {
mReactInstanceEventListeners.add(listener);
}
@Override
public void removeReactInstanceEventListener(ReactInstanceEventListener listener) {
mReactInstanceEventListeners.remove(listener);
}
@VisibleForTesting
@Override
public @Nullable ReactContext getCurrentReactContext() {
return mCurrentReactContext;
}
private void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
recreateReactContextInBackground(
new ProxyJavaScriptExecutor.Factory(jsExecutorFactory),
JSBundleLoader.createRemoteDebuggerBundleLoader(
mDevSupportManager.getJSBundleURLForRemoteDebugging(),
mDevSupportManager.getSourceUrl()));
}
private void onJSBundleLoadedFromServer() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(),
JSBundleLoader.createCachedBundleFromNetworkLoader(
mDevSupportManager.getSourceUrl(),
mDevSupportManager.getDownloadedJSBundleFile()));
}
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null) {
// No background task to create react context is currently running, create and execute one.
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.execute(initParams);
} else {
// Background task is currently running, queue up most recent init params to recreate context
// once task completes.
mPendingReactContextInitParams = initParams;
}
}
private void setupReactContext(ReactApplicationContext reactContext) {
UiThreadUtil.assertOnUiThread();
Assertions.assertCondition(mCurrentReactContext == null);
mCurrentReactContext = Assertions.assertNotNull(reactContext);
CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance());
catalystInstance.initialize();
mDevSupportManager.onNewReactContextCreated(reactContext);
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
moveReactContextToCurrentLifecycleState();
for (ReactRootView rootView : mAttachedRootViews) {
attachMeasuredRootViewToInstance(rootView, catalystInstance);
}
ReactInstanceEventListener[] listeners =
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
listeners = mReactInstanceEventListeners.toArray(listeners);
for (ReactInstanceEventListener listener : listeners) {
listener.onReactContextInitialized(reactContext);
}
}
private void attachMeasuredRootViewToInstance(
ReactRootView rootView,
CatalystInstance catalystInstance) {
UiThreadUtil.assertOnUiThread();
// Reset view content as it's going to be populated by the application content from JS
rootView.removeAllViews();
rootView.setId(View.NO_ID);
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
rootView.setRootViewTag(rootTag);
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
WritableMap initialProps = launchOptions != null
? Arguments.fromBundle(launchOptions)
: Arguments.createMap();
String jsAppModuleName = rootView.getJSModuleName();
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}
private void detachViewFromInstance(
ReactRootView rootView,
CatalystInstance catalystInstance) {
UiThreadUtil.assertOnUiThread();
catalystInstance.getJSModule(AppRegistry.class)
.unmountApplicationComponentAtRootTag(rootView.getId());
}
private void tearDownReactContext(ReactContext reactContext) {
UiThreadUtil.assertOnUiThread();
if (mLifecycleState == LifecycleState.RESUMED) {
reactContext.onHostPause();
}
for (ReactRootView rootView : mAttachedRootViews) {
detachViewFromInstance(rootView, reactContext.getCatalystInstance());
}
reactContext.destroy();
mDevSupportManager.onReactInstanceDestroyed(reactContext);
mMemoryPressureRouter.removeMemoryPressureListener(reactContext.getCatalystInstance());
}
/**
* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
*/
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
FLog.i(ReactConstants.TAG, "Creating react context.");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
// CREATE_REACT_CONTEXT_END is in JSCExecutor.cpp
mSourceUrl = jsBundleLoader.getSourceUrl();
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
ReactMarker.logMarker(PROCESS_PACKAGES_START);
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCoreModulesPackage");
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
// TODO(6818138): Solve use-case of native/js modules overriding
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
try {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
ReactMarker.logMarker(PROCESS_PACKAGES_END);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
try {
catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
new Callable<Void>() {
@Override
public Void call() {
// We want to ensure that any code that checks ReactContext#hasActiveCatalystInstance
// can be sure that it's safe to call a JS module function. As JS module function calls
// execute on the JS thread, and this Runnable runs on the JS thread, at this point we
// know that no JS module function calls will be executed until after this Runnable completes.
//
// This means it is now safe to say the instance is initialized.
//
// The reason we call this here instead of after this Runnable completes is so that we can
// reduce the amount of time until the React instance is able to start accepting JS calls,
// and so that any native module calls that result from runJSBundle can access JS modules.
reactContext.initializeWithInstance(catalystInstance);
ReactMarker.logMarker(RUN_JS_BUNDLE_START);
// RUN_JS_BUNDLE_END is in JSCExecutor.cpp
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
try {
catalystInstance.runJSBundle();
} finally {
// This will actually finish when `JSCExecutor#loadApplicationScript()` finishes
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
return null;
}
}).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
return reactContext;
}
private void processPackage(
ReactPackage reactPackage,
ReactApplicationContext reactContext,
NativeModuleRegistry.Builder nativeRegistryBuilder,
JavaScriptModuleRegistry.Builder jsModulesBuilder) {
for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
nativeRegistryBuilder.add(nativeModule);
}
for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
jsModulesBuilder.add(jsModuleClass);
}
}
private void moveReactContextToCurrentLifecycleState() {
if (mLifecycleState == LifecycleState.RESUMED) {
moveToResumedLifecycleState(true);
}
}
}

View File

@ -1,568 +0,0 @@
/**
* 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.bridge;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
import com.facebook.react.bridge.queue.ReactQueueConfigurationImpl;
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
import com.facebook.react.bridge.queue.QueueThreadExceptionHandler;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.common.futures.SimpleSettableFuture;
import com.facebook.systrace.Systrace;
import com.facebook.systrace.TraceListener;
/**
* This provides an implementation of the public CatalystInstance instance. It is public because
* it is built by ReactInstanceManager which is in a different package.
*/
@DoNotStrip
public class CatalystInstanceImpl implements CatalystInstance {
private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger(1);
// Access from any thread
private final ReactQueueConfigurationImpl mReactQueueConfiguration;
private final CopyOnWriteArrayList<NotThreadSafeBridgeIdleDebugListener> mBridgeIdleListeners;
private final AtomicInteger mPendingJSCalls = new AtomicInteger(0);
private final String mJsPendingCallsTitleForTrace =
"pending_js_calls_instance" + sNextInstanceIdForTrace.getAndIncrement();
private volatile boolean mIsBeingDestroyed = false;
private volatile boolean mDestroyed = false;
private final TraceListener mTraceListener;
private final JavaScriptModuleRegistry mJSModuleRegistry;
private final JSBundleLoader mJSBundleLoader;
private @Nullable ExecutorToken mMainExecutorToken;
// These locks prevent additional calls from going JS<->Java after the bridge has been torn down.
// There are separate ones for each direction because a JS to Java call can trigger a Java to JS
// call: this would cause a deadlock with a traditional mutex (maybe we should be using a reader-
// writer lock but then we'd have to worry about starving the destroy call).
private final Object mJSToJavaCallsTeardownLock = new Object();
private final Object mJavaToJSCallsTeardownLock = new Object();
// Access from native modules thread
private final NativeModuleRegistry mJavaRegistry;
private final NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
private boolean mInitialized = false;
// Access from JS thread
private final ReactBridge mBridge;
private boolean mJSBundleHasLoaded;
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
FLog.d(ReactConstants.TAG, "Initializing React Bridge.");
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mJavaRegistry = registry;
mJSModuleRegistry = jsModuleRegistry;
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mTraceListener = new JSProfilerTraceListener();
try {
mBridge = mReactQueueConfiguration.getJSQueueThread().callOnQueue(
new Callable<ReactBridge>() {
@Override
public ReactBridge call() throws Exception {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
try {
return initializeBridge(jsExecutor);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}).get();
} catch (Exception t) {
throw new RuntimeException("Failed to initialize bridge", t);
}
}
private ReactBridge initializeBridge(JavaScriptExecutor jsExecutor) {
mReactQueueConfiguration.getJSQueueThread().assertIsOnThread();
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
ReactBridge bridge;
try {
bridge = new ReactBridge(
jsExecutor,
new NativeModulesReactCallback(),
mReactQueueConfiguration.getNativeModulesQueueThread());
mMainExecutorToken = bridge.getMainExecutorToken();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
try {
bridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(mJavaRegistry));
bridge.setGlobalVariable(
"__RCTProfileIsProfiling",
Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
mJavaRegistry.notifyReactBridgeInitialized(bridge);
return bridge;
}
@Override
public void runJSBundle() {
mReactQueueConfiguration.getJSQueueThread().assertIsOnThread();
Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
incrementPendingJSCalls();
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "loadJSScript");
try {
mJSBundleLoader.loadScript(mBridge);
// This is registered after JS starts since it makes a JS call
Systrace.registerListener(mTraceListener);
} catch (JSExecutionException e) {
mNativeModuleCallExceptionHandler.handleException(e);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
mJSBundleHasLoaded = true;
}
@Override
public void callFunction(
ExecutorToken executorToken,
String module,
String method,
NativeArray arguments) {
if (mIsBeingDestroyed) {
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
return;
}
synchronized (mJavaToJSCallsTeardownLock) {
if (mDestroyed) {
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
return;
}
incrementPendingJSCalls();
Assertions.assertNotNull(mBridge).callFunction(
executorToken,
module,
method, arguments);
}
}
// This is called from java code, so it won't be stripped anyway, but proguard will rename it,
// which this prevents.
@DoNotStrip
@Override
public void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments) {
if (mIsBeingDestroyed) {
FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed.");
return;
}
synchronized (mJavaToJSCallsTeardownLock) {
if (mDestroyed) {
FLog.w(ReactConstants.TAG, "Invoking JS callback after bridge has been destroyed.");
return;
}
incrementPendingJSCalls();
Assertions.assertNotNull(mBridge).invokeCallback(executorToken, callbackID, arguments);
}
}
/**
* Destroys this catalyst instance, waiting for any other threads in ReactQueueConfiguration
* (besides the UI thread) to finish running. Must be called from the UI thread so that we can
* fully shut down other threads.
*/
@Override
public void destroy() {
UiThreadUtil.assertOnUiThread();
// This ordering is important. A JS to Java call that triggers a Java to JS call will also
// acquire these locks in the same order
mIsBeingDestroyed = true;
synchronized (mJSToJavaCallsTeardownLock) {
synchronized (mJavaToJSCallsTeardownLock) {
if (mDestroyed) {
return;
}
// TODO: tell all APIs to shut down
mDestroyed = true;
mJavaRegistry.notifyCatalystInstanceDestroy();
Systrace.unregisterListener(mTraceListener);
synchronouslyDisposeBridgeOnJSThread();
}
}
mReactQueueConfiguration.destroy();
boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0);
if (!wasIdle && !mBridgeIdleListeners.isEmpty()) {
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
listener.onTransitionToBridgeIdle();
}
}
}
private void synchronouslyDisposeBridgeOnJSThread() {
final SimpleSettableFuture<Void> bridgeDisposeFuture = new SimpleSettableFuture<>();
mReactQueueConfiguration.getJSQueueThread().runOnQueue(
new Runnable() {
@Override
public void run() {
mBridge.destroy();
mBridge.dispose();
bridgeDisposeFuture.set(null);
}
});
bridgeDisposeFuture.getOrThrow();
}
@Override
public boolean isDestroyed() {
return mDestroyed;
}
/**
* Initialize all the native modules
*/
@VisibleForTesting
@Override
public void initialize() {
UiThreadUtil.assertOnUiThread();
Assertions.assertCondition(
!mInitialized,
"This catalyst instance has already been initialized");
mInitialized = true;
mJavaRegistry.notifyCatalystInstanceInitialized();
}
@Override
public ReactQueueConfiguration getReactQueueConfiguration() {
return mReactQueueConfiguration;
}
@Override
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
return getJSModule(Assertions.assertNotNull(mMainExecutorToken), jsInterface);
}
@Override
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
return Assertions.assertNotNull(mJSModuleRegistry)
.getJavaScriptModule(this, executorToken, jsInterface);
}
@Override
public <T extends NativeModule> boolean hasNativeModule(Class<T> nativeModuleInterface) {
return mJavaRegistry.hasModule(nativeModuleInterface);
}
@Override
public <T extends NativeModule> T getNativeModule(Class<T> nativeModuleInterface) {
return mJavaRegistry.getModule(nativeModuleInterface);
}
@Override
public Collection<NativeModule> getNativeModules() {
return mJavaRegistry.getAllModules();
}
@Override
public void handleMemoryPressure(final MemoryPressure level) {
mReactQueueConfiguration.getJSQueueThread().runOnQueue(
new Runnable() {
@Override
public void run() {
Assertions.assertNotNull(mBridge).handleMemoryPressure(level);
}
});
}
/**
* Adds a idle listener for this Catalyst instance. The listener will receive notifications
* whenever the bridge transitions from idle to busy and vice-versa, where the busy state is
* defined as there being some non-zero number of calls to JS that haven't resolved via a
* onBatchCompleted call. The listener should be purely passive and not affect application logic.
*/
@Override
public void addBridgeIdleDebugListener(NotThreadSafeBridgeIdleDebugListener listener) {
mBridgeIdleListeners.add(listener);
}
/**
* Removes a NotThreadSafeBridgeIdleDebugListener previously added with
* {@link #addBridgeIdleDebugListener}
*/
@Override
public void removeBridgeIdleDebugListener(NotThreadSafeBridgeIdleDebugListener listener) {
mBridgeIdleListeners.remove(listener);
}
@Override
public boolean supportsProfiling() {
return mBridge.supportsProfiling();
}
@Override
public void startProfiler(String title) {
mBridge.startProfiler(title);
}
@Override
public void stopProfiler(String title, String filename) {
mBridge.stopProfiler(title, filename);
}
@VisibleForTesting
@Override
public void setGlobalVariable(String propName, String jsonValue) {
mBridge.setGlobalVariable(propName, jsonValue);
}
private String buildModulesConfigJSONProperty(NativeModuleRegistry nativeModuleRegistry) {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
try {
writer.beginObject();
writer.name("remoteModuleConfig");
nativeModuleRegistry.writeModuleDescriptions(writer);
writer.endObject();
return stringWriter.toString();
} catch (IOException ioe) {
throw new RuntimeException("Unable to serialize JavaScript module declaration", ioe);
} finally {
try {
writer.close();
} catch (IOException ignored) {
}
}
}
private void incrementPendingJSCalls() {
int oldPendingCalls = mPendingJSCalls.getAndIncrement();
boolean wasIdle = oldPendingCalls == 0;
Systrace.traceCounter(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
mJsPendingCallsTitleForTrace,
oldPendingCalls + 1);
if (wasIdle && !mBridgeIdleListeners.isEmpty()) {
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
listener.onTransitionToBridgeBusy();
}
}
}
private void decrementPendingJSCalls() {
int newPendingCalls = mPendingJSCalls.decrementAndGet();
// TODO(9604406): handle case of web workers injecting messages to main thread
//Assertions.assertCondition(newPendingCalls >= 0);
boolean isNowIdle = newPendingCalls == 0;
Systrace.traceCounter(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
mJsPendingCallsTitleForTrace,
newPendingCalls);
if (isNowIdle && !mBridgeIdleListeners.isEmpty()) {
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
listener.onTransitionToBridgeIdle();
}
}
}
@Override
protected void finalize() throws Throwable {
Assertions.assertCondition(mDestroyed, "Bridge was not destroyed before finalizer!");
super.finalize();
}
private class NativeModulesReactCallback implements ReactCallback {
@Override
public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
if (mIsBeingDestroyed) {
return;
}
synchronized (mJSToJavaCallsTeardownLock) {
// Suppress any callbacks if destroyed - will only lead to sadness.
if (mDestroyed) {
return;
}
mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
}
}
@Override
public void onBatchComplete() {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
// The bridge may have been destroyed due to an exception during the batch. In that case
// native modules could be in a bad state so we don't want to call anything on them. We
// still want to trigger the debug listener since it allows instrumentation tests to end and
// check their assertions without waiting for a timeout.
if (mIsBeingDestroyed) {
return;
}
synchronized (mJSToJavaCallsTeardownLock) {
if (mDestroyed) {
return;
}
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onBatchComplete");
try {
mJavaRegistry.onBatchComplete();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
decrementPendingJSCalls();
}
@Override
public void onExecutorUnregistered(ExecutorToken executorToken) {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
// Since onCatalystInstanceDestroy happens on the UI thread, we don't want to also execute
// this callback on the native modules thread at the same time. Longer term, onCatalystInstanceDestroy
// should probably be executed on the native modules thread as well instead.
if (mIsBeingDestroyed) {
return;
}
synchronized (mJSToJavaCallsTeardownLock) {
if (mDestroyed) {
return;
}
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onExecutorUnregistered");
try {
mJavaRegistry.onExecutorUnregistered(executorToken);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
}
private class NativeExceptionHandler implements QueueThreadExceptionHandler {
@Override
public void handleException(Exception e) {
// Any Exception caught here is because of something in JS. Even if it's a bug in the
// framework/native code, it was triggered by JS and theoretically since we were able
// to set up the bridge, JS could change its logic, reload, and not trigger that crash.
mNativeModuleCallExceptionHandler.handleException(e);
mReactQueueConfiguration.getUIQueueThread().runOnQueue(
new Runnable() {
@Override
public void run() {
destroy();
}
});
}
}
private class JSProfilerTraceListener implements TraceListener {
@Override
public void onTraceStarted() {
getJSModule(Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class).setEnabled(
true);
}
@Override
public void onTraceStopped() {
getJSModule(Assertions.assertNotNull(mMainExecutorToken), com.facebook.react.bridge.Systrace.class).setEnabled(false);
}
}
public static class Builder {
private @Nullable ReactQueueConfigurationSpec mReactQueueConfigurationSpec;
private @Nullable JSBundleLoader mJSBundleLoader;
private @Nullable NativeModuleRegistry mRegistry;
private @Nullable JavaScriptModuleRegistry mJSModuleRegistry;
private @Nullable JavaScriptExecutor mJSExecutor;
private @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
public Builder setReactQueueConfigurationSpec(
ReactQueueConfigurationSpec ReactQueueConfigurationSpec) {
mReactQueueConfigurationSpec = ReactQueueConfigurationSpec;
return this;
}
public Builder setRegistry(NativeModuleRegistry registry) {
mRegistry = registry;
return this;
}
public Builder setJSModuleRegistry(JavaScriptModuleRegistry jsModuleRegistry) {
mJSModuleRegistry = jsModuleRegistry;
return this;
}
public Builder setJSBundleLoader(JSBundleLoader jsBundleLoader) {
mJSBundleLoader = jsBundleLoader;
return this;
}
public Builder setJSExecutor(JavaScriptExecutor jsExecutor) {
mJSExecutor = jsExecutor;
return this;
}
public Builder setNativeModuleCallExceptionHandler(
NativeModuleCallExceptionHandler handler) {
mNativeModuleCallExceptionHandler = handler;
return this;
}
public CatalystInstanceImpl build() {
return new CatalystInstanceImpl(
Assertions.assertNotNull(mReactQueueConfigurationSpec),
Assertions.assertNotNull(mJSExecutor),
Assertions.assertNotNull(mRegistry),
Assertions.assertNotNull(mJSModuleRegistry),
Assertions.assertNotNull(mJSBundleLoader),
Assertions.assertNotNull(mNativeModuleCallExceptionHandler));
}
}
}

View File

@ -1,93 +0,0 @@
/**
* 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.bridge;
import android.content.Context;
/**
* A class that stores JS bundle information and allows {@link CatalystInstance} to load a correct
* bundle through {@link ReactBridge}.
*/
public abstract class JSBundleLoader {
/**
* This loader is recommended one for release version of your app. In that case local JS executor
* should be used. JS bundle will be read from assets directory in native code to save on passing
* large strings from java to native memory.
*/
public static JSBundleLoader createFileLoader(
final Context context,
final String fileName) {
return new JSBundleLoader() {
@Override
public void loadScript(ReactBridge bridge) {
if (fileName.startsWith("assets://")) {
bridge.loadScriptFromAssets(context.getAssets(), fileName.replaceFirst("assets://", ""));
} else {
bridge.loadScriptFromFile(fileName, "file://" + fileName);
}
}
@Override
public String getSourceUrl() {
return (fileName.startsWith("assets://") ? "" : "file://") + fileName;
}
};
}
/**
* This loader is used when bundle gets reloaded from dev server. In that case loader expect JS
* bundle to be prefetched and stored in local file. We do that to avoid passing large strings
* between java and native code and avoid allocating memory in java to fit whole JS bundle in it.
* Providing correct {@param sourceURL} of downloaded bundle is required for JS stacktraces to
* work correctly and allows for source maps to correctly symbolize those.
*/
public static JSBundleLoader createCachedBundleFromNetworkLoader(
final String sourceURL,
final String cachedFileLocation) {
return new JSBundleLoader() {
@Override
public void loadScript(ReactBridge bridge) {
bridge.loadScriptFromFile(cachedFileLocation, sourceURL);
}
@Override
public String getSourceUrl() {
return sourceURL;
}
};
}
/**
* This loader is used when proxy debugging is enabled. In that case there is no point in fetching
* the bundle from device as remote executor will have to do it anyway.
*
* @param proxySourceURL the URL to load the JS bundle from in the JavaScript proxy
* @param realSourceURL the URL to report as the source URL, e.g. for asset loading
*/
public static JSBundleLoader createRemoteDebuggerBundleLoader(
final String proxySourceURL,
final String realSourceURL) {
return new JSBundleLoader() {
@Override
public void loadScript(ReactBridge bridge) {
bridge.loadScriptFromFile(null, proxySourceURL);
}
@Override
public String getSourceUrl() {
return realSourceURL;
}
};
}
public abstract void loadScript(ReactBridge bridge);
public abstract String getSourceUrl();
}

View File

@ -1,34 +0,0 @@
/**
* 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.bridge;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class JSCJavaScriptExecutor extends JavaScriptExecutor {
static {
ReactBridge.staticInit();
}
public static class Factory implements JavaScriptExecutor.Factory {
@Override
public JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception {
return new JSCJavaScriptExecutor(jscConfig);
}
}
public JSCJavaScriptExecutor(WritableNativeMap jscConfig) {
initialize(jscConfig);
}
private native void initialize(WritableNativeMap jscConfig);
}

View File

@ -1,17 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.bridge;
import com.facebook.proguard.annotations.DoNotStrip;
/**
* Exception thrown when there is an error evaluating JS, e.g. a syntax error.
*/
@DoNotStrip
public class JSExecutionException extends RuntimeException {
@DoNotStrip
public JSExecutionException(String detailMessage) {
super(detailMessage);
}
}

View File

@ -1,28 +0,0 @@
/**
* 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.bridge;
import com.facebook.jni.Countable;
import com.facebook.proguard.annotations.DoNotStrip;
@DoNotStrip
public abstract class JavaScriptExecutor extends Countable {
public interface Factory {
JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception;
}
/**
* Close this executor and cleanup any resources that it was using. No further calls are
* expected after this.
*/
public void close() {
}
}

View File

@ -1,235 +0,0 @@
/**
* 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.bridge;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.facebook.react.common.MapBuilder;
import com.facebook.infer.annotation.Assertions;
import com.facebook.systrace.Systrace;
/**
* A set of Java APIs to expose to a particular JavaScript instance.
*/
public class NativeModuleRegistry {
private final List<ModuleDefinition> mModuleTable;
private final Map<Class<? extends NativeModule>, NativeModule> mModuleInstances;
private final ArrayList<OnBatchCompleteListener> mBatchCompleteListenerModules;
private final ArrayList<OnExecutorUnregisteredListener> mOnExecutorUnregisteredListenerModules;
private NativeModuleRegistry(
List<ModuleDefinition> moduleTable,
Map<Class<? extends NativeModule>, NativeModule> moduleInstances) {
mModuleTable = moduleTable;
mModuleInstances = moduleInstances;
mBatchCompleteListenerModules = new ArrayList<>(mModuleTable.size());
mOnExecutorUnregisteredListenerModules = new ArrayList<>(mModuleTable.size());
for (int i = 0; i < mModuleTable.size(); i++) {
ModuleDefinition definition = mModuleTable.get(i);
if (definition.target instanceof OnBatchCompleteListener) {
mBatchCompleteListenerModules.add((OnBatchCompleteListener) definition.target);
}
if (definition.target instanceof OnExecutorUnregisteredListener) {
mOnExecutorUnregisteredListenerModules.add((OnExecutorUnregisteredListener) definition.target);
}
}
}
/* package */ void call(
CatalystInstance catalystInstance,
ExecutorToken executorToken,
int moduleId,
int methodId,
ReadableNativeArray parameters) {
ModuleDefinition definition = mModuleTable.get(moduleId);
if (definition == null) {
throw new RuntimeException("Call to unknown module: " + moduleId);
}
definition.call(catalystInstance, executorToken, methodId, parameters);
}
/* package */ void writeModuleDescriptions(JsonWriter writer) throws IOException {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CreateJSON");
try {
writer.beginObject();
for (ModuleDefinition moduleDef : mModuleTable) {
writer.name(moduleDef.name).beginObject();
writer.name("moduleID").value(moduleDef.id);
writer.name("supportsWebWorkers").value(moduleDef.target.supportsWebWorkers());
writer.name("methods").beginObject();
for (int i = 0; i < moduleDef.methods.size(); i++) {
MethodRegistration method = moduleDef.methods.get(i);
writer.name(method.name).beginObject();
writer.name("methodID").value(i);
writer.name("type").value(method.method.getType());
writer.endObject();
}
writer.endObject();
moduleDef.target.writeConstantsField(writer, "constants");
writer.endObject();
}
writer.endObject();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
/* package */ void notifyCatalystInstanceDestroy() {
UiThreadUtil.assertOnUiThread();
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"NativeModuleRegistry_notifyCatalystInstanceDestroy");
try {
for (NativeModule nativeModule : mModuleInstances.values()) {
nativeModule.onCatalystInstanceDestroy();
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
/* package */ void notifyCatalystInstanceInitialized() {
UiThreadUtil.assertOnUiThread();
ReactMarker.logMarker("NativeModule_start");
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"NativeModuleRegistry_notifyCatalystInstanceInitialized");
try {
for (NativeModule nativeModule : mModuleInstances.values()) {
nativeModule.initialize();
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker("NativeModule_end");
}
}
/* package */ void notifyReactBridgeInitialized(ReactBridge bridge) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"NativeModuleRegistry_notifyReactBridgeInitialized");
try {
for (NativeModule nativeModule : mModuleInstances.values()) {
nativeModule.onReactBridgeInitialized(bridge);
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
public void onBatchComplete() {
for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) {
mBatchCompleteListenerModules.get(i).onBatchComplete();
}
}
public void onExecutorUnregistered(ExecutorToken executorToken) {
for (int i = 0; i < mOnExecutorUnregisteredListenerModules.size(); i++) {
mOnExecutorUnregisteredListenerModules.get(i).onExecutorDestroyed(executorToken);
}
}
public <T extends NativeModule> boolean hasModule(Class<T> moduleInterface) {
return mModuleInstances.containsKey(moduleInterface);
}
public <T extends NativeModule> T getModule(Class<T> moduleInterface) {
return (T) Assertions.assertNotNull(mModuleInstances.get(moduleInterface));
}
public Collection<NativeModule> getAllModules() {
return mModuleInstances.values();
}
private static class ModuleDefinition {
public final int id;
public final String name;
public final NativeModule target;
public final ArrayList<MethodRegistration> methods;
public ModuleDefinition(int id, String name, NativeModule target) {
this.id = id;
this.name = name;
this.target = target;
this.methods = new ArrayList<MethodRegistration>();
for (Map.Entry<String, NativeModule.NativeMethod> entry : target.getMethods().entrySet()) {
this.methods.add(
new MethodRegistration(
entry.getKey(), "NativeCall__" + target.getName() + "_" + entry.getKey(),
entry.getValue()));
}
}
public void call(
CatalystInstance catalystInstance,
ExecutorToken executorToken,
int methodId,
ReadableNativeArray parameters) {
MethodRegistration method = this.methods.get(methodId);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
try {
this.methods.get(methodId).method.invoke(catalystInstance, executorToken, parameters);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
private static class MethodRegistration {
public MethodRegistration(String name, String tracingName, NativeModule.NativeMethod method) {
this.name = name;
this.tracingName = tracingName;
this.method = method;
}
public String name;
public String tracingName;
public NativeModule.NativeMethod method;
}
public static class Builder {
private final HashMap<String, NativeModule> mModules = MapBuilder.newHashMap();
public Builder add(NativeModule module) {
NativeModule existing = mModules.get(module.getName());
if (existing != null && !module.canOverrideExistingModule()) {
throw new IllegalStateException("Native module " + module.getClass().getSimpleName() +
" tried to override " + existing.getClass().getSimpleName() + " for module name " +
module.getName() + ". If this was your intention, return true from " +
module.getClass().getSimpleName() + "#canOverrideExistingModule()");
}
mModules.put(module.getName(), module);
return this;
}
public NativeModuleRegistry build() {
List<ModuleDefinition> moduleTable = new ArrayList<>();
Map<Class<? extends NativeModule>, NativeModule> moduleInstances = new HashMap<>();
int idx = 0;
for (NativeModule module : mModules.values()) {
ModuleDefinition moduleDef = new ModuleDefinition(idx++, module.getName(), module);
moduleTable.add(moduleDef);
moduleInstances.put(module.getClass(), module);
}
return new NativeModuleRegistry(moduleTable, moduleInstances);
}
}
}

View File

@ -1,66 +0,0 @@
/**
* 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.bridge;
import javax.annotation.Nullable;
import com.facebook.soloader.SoLoader;
import com.facebook.proguard.annotations.DoNotStrip;
/**
* JavaScript executor that delegates JS calls processed by native code back to a java version
* of the native executor interface.
*
* When set as a executor with {@link CatalystInstance.Builder}, catalyst native code will delegate
* low level javascript calls to the implementation of {@link JavaJSExecutor} interface provided
* with the constructor of this class.
*/
@DoNotStrip
public class ProxyJavaScriptExecutor extends JavaScriptExecutor {
static {
ReactBridge.staticInit();
}
public static class Factory implements JavaScriptExecutor.Factory {
private final JavaJSExecutor.Factory mJavaJSExecutorFactory;
public Factory(JavaJSExecutor.Factory javaJSExecutorFactory) {
mJavaJSExecutorFactory = javaJSExecutorFactory;
}
@Override
public JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception {
return new ProxyJavaScriptExecutor(mJavaJSExecutorFactory.create());
}
}
private @Nullable JavaJSExecutor mJavaJSExecutor;
/**
* Create {@link ProxyJavaScriptExecutor} instance
* @param executor implementation of {@link JavaJSExecutor} which will be responsible for handling
* javascript calls
*/
public ProxyJavaScriptExecutor(JavaJSExecutor executor) {
mJavaJSExecutor = executor;
initialize(executor);
}
@Override
public void close() {
if (mJavaJSExecutor != null) {
mJavaJSExecutor.close();
mJavaJSExecutor = null;
}
}
private native void initialize(JavaJSExecutor executor);
}

View File

@ -9,20 +9,9 @@
package com.facebook.react.bridge;
import javax.annotation.Nullable;
import android.content.res.AssetManager;
import com.facebook.react.bridge.queue.MessageQueueThread;
import com.facebook.jni.Countable;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
/**
* Interface to the JS execution environment and means of transport for messages Java<->JS.
*/
@DoNotStrip
public class ReactBridge extends Countable {
public class ReactBridge {
private static final String REACT_NATIVE_LIB = "reactnativejni";
private static final String XREACT_NATIVE_LIB = "reactnativejnifb";
@ -31,81 +20,8 @@ public class ReactBridge extends Countable {
staticInit();
}
private final ReactCallback mCallback;
private final JavaScriptExecutor mJSExecutor;
private final MessageQueueThread mNativeModulesQueueThread;
public static void staticInit() {
SoLoader.loadLibrary(REACT_NATIVE_LIB);
SoLoader.loadLibrary(XREACT_NATIVE_LIB);
}
/**
* @param jsExecutor the JS executor to use to run JS
* @param callback the callback class used to invoke native modules
* @param nativeModulesQueueThread the MessageQueueThread the callbacks should be invoked on
*/
public ReactBridge(
JavaScriptExecutor jsExecutor,
ReactCallback callback,
MessageQueueThread nativeModulesQueueThread) {
mJSExecutor = jsExecutor;
mCallback = callback;
mNativeModulesQueueThread = nativeModulesQueueThread;
initialize(jsExecutor, callback, mNativeModulesQueueThread);
}
@Override
public void dispose() {
mJSExecutor.close();
mJSExecutor.dispose();
super.dispose();
}
public void handleMemoryPressure(MemoryPressure level) {
switch (level) {
case UI_HIDDEN:
handleMemoryPressureUiHidden();
break;
case MODERATE:
handleMemoryPressureModerate();
break;
case CRITICAL:
handleMemoryPressureCritical();
break;
default:
throw new IllegalArgumentException("Unknown level: " + level);
}
}
private native void initialize(
JavaScriptExecutor jsExecutor,
ReactCallback callback,
MessageQueueThread nativeModulesQueueThread);
/**
* All native functions are not thread safe and appropriate queues should be used
*/
public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
public native void callFunction(ExecutorToken executorToken, String module, String method, NativeArray arguments);
public native void invokeCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments);
public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
public native boolean supportsProfiling();
public native void startProfiler(String title);
public native void stopProfiler(String title, String filename);
public native ExecutorToken getMainExecutorToken();
private native void handleMemoryPressureUiHidden();
private native void handleMemoryPressureModerate();
private native void handleMemoryPressureCritical();
public native void destroy();
/**
* This method will return a long representing the underlying JSGlobalContextRef pointer or
* 0 (representing NULL) when in Chrome debug mode, and is only useful if passed back through
* the JNI to native code that will use it with the JavaScriptCore C API.
* **WARNING:** This method is *experimental* and should only be used when no other option is
* available. It will likely change in a future release!
*/
public native long getJavaScriptContextNativePtrExperimental();
}

View File

@ -1,25 +0,0 @@
/**
* 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.bridge;
import com.facebook.proguard.annotations.DoNotStrip;
@DoNotStrip
public interface ReactCallback {
@DoNotStrip
void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters);
@DoNotStrip
void onBatchComplete();
@DoNotStrip
void onExecutorUnregistered(ExecutorToken executorToken);
}

View File

@ -1,76 +0,0 @@
/**
* 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.bridge.webworkers;
import java.io.File;
import java.io.IOException;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.queue.MessageQueueThread;
import com.facebook.react.bridge.queue.MessageQueueThreadImpl;
import com.facebook.react.bridge.queue.ProxyQueueThreadExceptionHandler;
import com.facebook.react.common.build.ReactBuildConfig;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.Okio;
import okio.Sink;
@DoNotStrip
public class WebWorkers {
/**
* Creates a new MessageQueueThread for a background web worker owned by the JS thread with the
* given MessageQueueThread.
*/
@DoNotStrip
public static MessageQueueThread createWebWorkerThread(int id, MessageQueueThread ownerThread) {
return MessageQueueThreadImpl.startNewBackgroundThread(
"web-worker-" + id,
new ProxyQueueThreadExceptionHandler(ownerThread));
}
/**
* Utility method used to help develop web workers on debug builds. In release builds, worker
* scripts need to be packaged with the app, but in dev mode we want to fetch/reload the worker
* script on the fly from the packager. This method fetches the given URL *synchronously* and
* writes it to the specified temp file.
*
* This is exposed from Java only because we don't want to add a C++ networking library dependency
*
* NB: The caller is responsible for deleting the file specified by outFileName when they're done
* with it.
* NB: We write to a temp file instead of returning a String because, depending on the size of the
* worker script, allocating the full script string on the Java heap can cause an OOM.
*/
public static void downloadScriptToFileSync(String url, String outFileName) {
if (!ReactBuildConfig.DEBUG) {
throw new RuntimeException(
"For security reasons, downloading scripts is only allowed in debug builds.");
}
OkHttpClient client = new OkHttpClient();
final File out = new File(outFileName);
Request request = new Request.Builder()
.url(url)
.build();
try {
Response response = client.newCall(request).execute();
Sink output = Okio.sink(out);
Okio.buffer(response.body().source()).readAll(output);
} catch (IOException e) {
throw new RuntimeException("Exception downloading web worker script to file", e);
}
}
}

View File

@ -1,33 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libreactnative
LOCAL_SRC_FILES := \
Bridge.cpp \
JSCExecutor.cpp \
JSCHelpers.cpp \
MethodCall.cpp \
Platform.cpp \
Value.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := \
-DLOG_TAG=\"ReactNative\"
LOCAL_CFLAGS += -Wall -Werror -fexceptions -frtti
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_SHARED_LIBRARIES := libfb libfolly_json libjsc libglog
include $(BUILD_STATIC_LIBRARY)
$(call import-module,fb)
$(call import-module,folly)
$(call import-module,jsc)
$(call import-module,glog)

View File

@ -1,78 +0,0 @@
include_defs('//ReactAndroid/DEFS')
# We depend on JSC, support the same platforms
SUPPORTED_PLATFORMS = '^android-(armv7|x86)$'
DEPS = [
'//native/third-party/android-ndk:android',
'//xplat/fbsystrace:fbsystrace',
'//xplat/folly:molly',
'//xplat/third-party/glog:glog',
]
PREPROCESSOR_FLAGS = [
'-DLOG_TAG="ReactNative"',
'-DWITH_JSC_EXTRA_TRACING=1',
'-DWITH_FBSYSTRACE=1',
'-DWITH_REACT_INTERNAL_SETTINGS=1',
]
def react_library(**kwargs):
cxx_library(
name = 'react',
visibility = [
react_native_target('jni/react/jni:jni'),
],
deps = DEPS + JSC_DEPS,
preprocessor_flags = PREPROCESSOR_FLAGS + [
'-DWITH_FB_MEMORY_PROFILING=1',
],
**kwargs
)
react_library(
soname = 'libreactnative.so',
header_namespace = 'react',
supported_platforms_regex = SUPPORTED_PLATFORMS,
force_static = True,
srcs = [
'Bridge.cpp',
'Value.cpp',
'MethodCall.cpp',
'JSCHelpers.cpp',
'JSCExecutor.cpp',
'JSCPerfStats.cpp',
'JSCTracing.cpp',
'JSCMemory.cpp',
'JSCLegacyProfiler.cpp',
'Platform.cpp',
],
headers = [
'JSCTracing.h',
'JSCLegacyProfiler.h',
'JSCMemory.h',
'JSCPerfStats.h',
],
exported_headers = [
'Bridge.h',
'ExecutorToken.h',
'ExecutorTokenFactory.h',
'Executor.h',
'JSCExecutor.h',
'JSCHelpers.h',
'MessageQueueThread.h',
'MethodCall.h',
'JSModulesUnbundle.h',
'Value.h',
'Platform.h',
'noncopyable.h',
],
compiler_flags = [
'-Wall',
'-std=c++11',
'-fexceptions',
'-fvisibility=hidden',
'-frtti',
'-Wno-pessimizing-move',
],
)

View File

@ -1,256 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Bridge.h"
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceSection;
using fbsystrace::FbSystraceAsyncFlow;
#endif
#include <folly/Memory.h>
#include "Platform.h"
namespace facebook {
namespace react {
Bridge::Bridge(
JSExecutorFactory* jsExecutorFactory,
std::unique_ptr<ExecutorTokenFactory> executorTokenFactory,
std::unique_ptr<BridgeCallback> callback) :
m_callback(std::move(callback)),
m_destroyed(std::make_shared<std::atomic_bool>(false)),
m_executorTokenFactory(std::move(executorTokenFactory)) {
std::unique_ptr<JSExecutor> mainExecutor = jsExecutorFactory->createJSExecutor(this);
// cached to avoid locked map lookup in the common case
m_mainExecutor = mainExecutor.get();
m_mainExecutorToken = folly::make_unique<ExecutorToken>(registerExecutor(
std::move(mainExecutor),
MessageQueues::getCurrentMessageQueueThread()));
}
// This must be called on the same thread on which the constructor was called.
Bridge::~Bridge() {
CHECK(m_destroyed->load(std::memory_order_acquire)) << "Bridge::destroy() must be called before deallocating the Bridge!";
}
void Bridge::loadApplicationScript(const std::string& script, const std::string& sourceURL) {
m_mainExecutor->loadApplicationScript(script, sourceURL);
}
void Bridge::loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle,
const std::string& startupCode,
const std::string& sourceURL) {
m_mainExecutor->loadApplicationUnbundle(std::move(unbundle), startupCode, sourceURL);
}
void Bridge::callFunction(
ExecutorToken executorToken,
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) {
#ifdef WITH_FBSYSTRACE
int systraceCookie = m_systraceCookie++;
std::string tracingName = fbsystrace_is_tracing(TRACE_TAG_REACT_CXX_BRIDGE) ?
folly::to<std::string>("JSCall__", moduleId, '_', methodId) : std::string();
FbSystraceAsyncFlow::begin(
TRACE_TAG_REACT_CXX_BRIDGE,
tracingName.c_str(),
systraceCookie);
#endif
#ifdef WITH_FBSYSTRACE
runOnExecutorQueue(executorToken, [moduleId, methodId, arguments, tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) {
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
tracingName.c_str(),
systraceCookie);
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, tracingName.c_str());
#else
runOnExecutorQueue(executorToken, [moduleId, methodId, arguments] (JSExecutor* executor) {
#endif
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
executor->callFunction(moduleId, methodId, arguments);
});
}
void Bridge::invokeCallback(ExecutorToken executorToken, const double callbackId, const folly::dynamic& arguments) {
#ifdef WITH_FBSYSTRACE
int systraceCookie = m_systraceCookie++;
FbSystraceAsyncFlow::begin(
TRACE_TAG_REACT_CXX_BRIDGE,
"<callback>",
systraceCookie);
#endif
#ifdef WITH_FBSYSTRACE
runOnExecutorQueue(executorToken, [callbackId, arguments, systraceCookie] (JSExecutor* executor) {
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
"<callback>",
systraceCookie);
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "Bridge.invokeCallback");
#else
runOnExecutorQueue(executorToken, [callbackId, arguments] (JSExecutor* executor) {
#endif
executor->invokeCallback(callbackId, arguments);
});
}
void Bridge::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
executor->setGlobalVariable(propName, jsonValue);
});
}
void* Bridge::getJavaScriptContext() {
// TODO(cjhopman): this seems unsafe unless we require that it is only called on the main js queue.
return m_mainExecutor->getJavaScriptContext();
}
bool Bridge::supportsProfiling() {
// Intentionally doesn't post to jsqueue. supportsProfiling() can be called from any thread.
return m_mainExecutor->supportsProfiling();
}
void Bridge::startProfiler(const std::string& title) {
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
executor->startProfiler(title);
});
}
void Bridge::stopProfiler(const std::string& title, const std::string& filename) {
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
executor->stopProfiler(title, filename);
});
}
void Bridge::handleMemoryPressureUiHidden() {
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
executor->handleMemoryPressureUiHidden();
});
}
void Bridge::handleMemoryPressureModerate() {
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
executor->handleMemoryPressureModerate();
});
}
void Bridge::handleMemoryPressureCritical() {
runOnExecutorQueue(*m_mainExecutorToken, [=] (JSExecutor* executor) {
executor->handleMemoryPressureCritical();
});
}
void Bridge::callNativeModules(JSExecutor& executor, const std::string& callJSON, bool isEndOfBatch) {
m_callback->onCallNativeModules(getTokenForExecutor(executor), callJSON, isEndOfBatch);
}
ExecutorToken Bridge::getMainExecutorToken() const {
return *m_mainExecutorToken.get();
}
ExecutorToken Bridge::registerExecutor(
std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> messageQueueThread) {
auto token = m_executorTokenFactory->createExecutorToken();
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
CHECK(m_executorTokenMap.find(executor.get()) == m_executorTokenMap.end())
<< "Trying to register an already registered executor!";
m_executorTokenMap.emplace(executor.get(), token);
m_executorMap.emplace(
token,
folly::make_unique<ExecutorRegistration>(std::move(executor), std::move(messageQueueThread)));
return token;
}
std::unique_ptr<JSExecutor> Bridge::unregisterExecutor(ExecutorToken executorToken) {
std::unique_ptr<JSExecutor> executor;
{
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
auto it = m_executorMap.find(executorToken);
CHECK(it != m_executorMap.end())
<< "Trying to unregister an executor that was never registered!";
executor = std::move(it->second->executor_);
m_executorMap.erase(it);
m_executorTokenMap.erase(executor.get());
}
m_callback->onExecutorUnregistered(executorToken);
return executor;
}
MessageQueueThread* Bridge::getMessageQueueThread(const ExecutorToken& executorToken) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
auto it = m_executorMap.find(executorToken);
if (it == m_executorMap.end()) {
return nullptr;
}
return it->second->messageQueueThread_.get();
}
JSExecutor* Bridge::getExecutor(const ExecutorToken& executorToken) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
auto it = m_executorMap.find(executorToken);
if (it == m_executorMap.end()) {
return nullptr;
}
return it->second->executor_.get();
}
ExecutorToken Bridge::getTokenForExecutor(JSExecutor& executor) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
return m_executorTokenMap.at(&executor);
}
void Bridge::destroy() {
m_destroyed->store(true, std::memory_order_release);
m_mainExecutor = nullptr;
std::unique_ptr<JSExecutor> mainExecutor = unregisterExecutor(*m_mainExecutorToken);
mainExecutor->destroy();
}
void Bridge::runOnExecutorQueue(ExecutorToken executorToken, std::function<void(JSExecutor*)> task) {
if (m_destroyed->load(std::memory_order_acquire)) {
return;
}
auto executorMessageQueueThread = getMessageQueueThread(executorToken);
if (executorMessageQueueThread == nullptr) {
LOG(WARNING) << "Dropping JS action for executor that has been unregistered...";
return;
}
std::shared_ptr<std::atomic_bool> isDestroyed = m_destroyed;
executorMessageQueueThread->runOnQueue([this, isDestroyed, executorToken, task=std::move(task)] {
if (isDestroyed->load(std::memory_order_acquire)) {
return;
}
JSExecutor *executor = getExecutor(executorToken);
if (executor == nullptr) {
LOG(WARNING) << "Dropping JS call for executor that has been unregistered...";
return;
}
// The executor is guaranteed to be valid for the duration of the task because:
// 1. the executor is only destroyed after it is unregistered
// 2. the executor is unregistered on this queue
// 3. we just confirmed that the executor hasn't been unregistered above
task(executor);
});
}
} }

View File

@ -1,162 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <atomic>
#include <functional>
#include <map>
#include <vector>
#include "ExecutorToken.h"
#include "ExecutorTokenFactory.h"
#include "Executor.h"
#include "MessageQueueThread.h"
#include "MethodCall.h"
#include "JSModulesUnbundle.h"
#include "Value.h"
namespace folly {
struct dynamic;
}
namespace facebook {
namespace react {
class BridgeCallback {
public:
virtual ~BridgeCallback() {};
virtual void onCallNativeModules(
ExecutorToken executorToken,
const std::string& callJSON,
bool isEndOfBatch) = 0;
virtual void onExecutorUnregistered(ExecutorToken executorToken) = 0;
};
class Bridge;
class ExecutorRegistration {
public:
ExecutorRegistration(
std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> executorMessageQueueThread) :
executor_(std::move(executor)),
messageQueueThread_(executorMessageQueueThread) {}
std::unique_ptr<JSExecutor> executor_;
std::shared_ptr<MessageQueueThread> messageQueueThread_;
};
class Bridge {
public:
/**
* This must be called on the main JS thread.
*/
Bridge(
JSExecutorFactory* jsExecutorFactory,
std::unique_ptr<ExecutorTokenFactory> executorTokenFactory,
std::unique_ptr<BridgeCallback> callback);
virtual ~Bridge();
/**
* Executes a function with the module ID and method ID and any additional
* arguments in JS.
*/
void callFunction(
ExecutorToken executorToken,
const std::string& module,
const std::string& method,
const folly::dynamic& args);
/**
* Invokes a callback with the cbID, and optional additional arguments in JS.
*/
void invokeCallback(ExecutorToken executorToken, const double callbackId, const folly::dynamic& args);
/**
* Starts the JS application from an "bundle", i.e. a JavaScript file that
* contains code for all modules and a runtime that resolves and
* executes modules.
*/
void loadApplicationScript(const std::string& script, const std::string& sourceURL);
/**
* Starts the JS application from an "unbundle", i.e. a backend that stores
* and injects each module as individual file.
*/
void loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle,
const std::string& startupCode,
const std::string& sourceURL);
void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
void* getJavaScriptContext();
bool supportsProfiling();
void startProfiler(const std::string& title);
void stopProfiler(const std::string& title, const std::string& filename);
void handleMemoryPressureUiHidden();
void handleMemoryPressureModerate();
void handleMemoryPressureCritical();
/**
* Invokes a set of native module calls on behalf of the given executor.
*
* TODO: get rid of isEndOfBatch
*/
void callNativeModules(JSExecutor& executor, const std::string& callJSON, bool isEndOfBatch);
/**
* Returns the ExecutorToken corresponding to the main JSExecutor.
*/
ExecutorToken getMainExecutorToken() const;
/**
* Registers the given JSExecutor which runs on the given MessageQueueThread
* with the Bridge. Part of this registration is transfering ownership of this
* JSExecutor to the Bridge for the duration of the registration.
*
* Returns a ExecutorToken which can be used to refer to this JSExecutor
* in the Bridge.
*/
ExecutorToken registerExecutor(
std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> executorMessageQueueThread);
/**
* Unregisters a JSExecutor that was previously registered with this Bridge
* using registerExecutor. Use the ExecutorToken returned from this
* registerExecutor call. This method will return ownership of the unregistered
* executor to the caller for it to retain or tear down.
*
* Returns ownership of the unregistered executor.
*/
std::unique_ptr<JSExecutor> unregisterExecutor(ExecutorToken executorToken);
/**
* Synchronously tears down the bridge and the main executor.
*/
void destroy();
private:
void runOnExecutorQueue(ExecutorToken token, std::function<void(JSExecutor*)> task);
std::unique_ptr<BridgeCallback> m_callback;
// This is used to avoid a race condition where a proxyCallback gets queued after ~Bridge(),
// on the same thread. In that case, the callback will try to run the task on m_callback which
// will have been destroyed within ~Bridge(), thus causing a SIGSEGV.
std::shared_ptr<std::atomic_bool> m_destroyed;
JSExecutor* m_mainExecutor;
std::unique_ptr<ExecutorToken> m_mainExecutorToken;
std::unique_ptr<ExecutorTokenFactory> m_executorTokenFactory;
std::unordered_map<JSExecutor*, ExecutorToken> m_executorTokenMap;
std::unordered_map<ExecutorToken, std::unique_ptr<ExecutorRegistration>> m_executorMap;
std::mutex m_registrationMutex;
#ifdef WITH_FBSYSTRACE
std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT();
#endif
MessageQueueThread* getMessageQueueThread(const ExecutorToken& executorToken);
JSExecutor* getExecutor(const ExecutorToken& executorToken);
inline ExecutorToken getTokenForExecutor(JSExecutor& executor);
};
} }

View File

@ -1,80 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <string>
#include <vector>
#include <memory>
#include "JSModulesUnbundle.h"
namespace folly {
struct dynamic;
}
namespace facebook {
namespace react {
class Bridge;
class JSExecutor;
class JSExecutorFactory {
public:
virtual std::unique_ptr<JSExecutor> createJSExecutor(Bridge *bridge) = 0;
virtual ~JSExecutorFactory() {};
};
class JSExecutor {
public:
/**
* Execute an application script bundle in the JS context.
*/
virtual void loadApplicationScript(
const std::string& script,
const std::string& sourceURL) = 0;
/**
* Add an application "unbundle" file
*/
virtual void loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> bundle,
const std::string& startupCode,
const std::string& sourceURL) = 0;
/**
* Executes BatchedBridge.callFunctionReturnFlushedQueue with the module ID,
* method ID and optional additional arguments in JS. The executor is responsible
* for using Bridge->callNativeModules to invoke any necessary native modules methods.
*/
virtual void callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) = 0;
/**
* Executes BatchedBridge.invokeCallbackAndReturnFlushedQueue with the cbID,
* and optional additional arguments in JS and returns the next queue. The executor
* is responsible for using Bridge->callNativeModules to invoke any necessary
* native modules methods.
*/
virtual void invokeCallback(const double callbackId, const folly::dynamic& arguments) = 0;
virtual void setGlobalVariable(
const std::string& propName,
const std::string& jsonValue) = 0;
virtual void* getJavaScriptContext() {
return nullptr;
};
virtual bool supportsProfiling() {
return false;
};
virtual void startProfiler(const std::string &titleString) {};
virtual void stopProfiler(const std::string &titleString, const std::string &filename) {};
virtual void handleMemoryPressureUiHidden() {};
virtual void handleMemoryPressureModerate() {};
virtual void handleMemoryPressureCritical() {
handleMemoryPressureModerate();
};
virtual void destroy() {};
virtual ~JSExecutor() {};
};
} }

View File

@ -1,54 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Executor.h"
namespace facebook {
namespace react {
/**
* This class exists so that we have a type for the shared_ptr on ExecutorToken
* that implements a virtual destructor.
*/
class PlatformExecutorToken {
public:
virtual ~PlatformExecutorToken() {}
};
/**
* Class corresponding to a JS VM that can call into native modules. This is
* passed to native modules to allow their JS module calls/callbacks to be
* routed back to the proper JS VM on the proper thread.
*/
class ExecutorToken {
public:
/**
* This should only be used by the implementation of the platform ExecutorToken.
* Do not use as a client of ExecutorToken.
*/
explicit ExecutorToken(std::shared_ptr<PlatformExecutorToken> platformToken) :
platformToken_(platformToken) {}
std::shared_ptr<PlatformExecutorToken> getPlatformExecutorToken() const {
return platformToken_;
}
bool operator==(const ExecutorToken& other) const {
return platformToken_.get() == other.platformToken_.get();
}
private:
std::shared_ptr<PlatformExecutorToken> platformToken_;
};
} }
namespace std {
template<>
struct hash<facebook::react::ExecutorToken> {
const size_t operator()(const facebook::react::ExecutorToken& token) const {
return (size_t) token.getPlatformExecutorToken().get();
}
};
}

View File

@ -1,25 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "ExecutorToken.h"
#include "Executor.h"
namespace facebook {
namespace react {
/**
* Class that knows how to create the platform-specific implementation
* of ExecutorToken.
*/
class ExecutorTokenFactory {
public:
virtual ~ExecutorTokenFactory() {}
/**
* Creates a new ExecutorToken.
*/
virtual ExecutorToken createExecutorToken() const = 0;
};
} }

View File

@ -1,709 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCExecutor.h"
#include <algorithm>
#include <condition_variable>
#include <mutex>
#include <sstream>
#include <string>
#include <glog/logging.h>
#include <folly/json.h>
#include <folly/Memory.h>
#include <folly/String.h>
#include <sys/time.h>
#include "Bridge.h"
#include "JSCHelpers.h"
#include "Platform.h"
#include "Value.h"
#ifdef WITH_JSC_EXTRA_TRACING
#include "JSCTracing.h"
#include "JSCLegacyProfiler.h"
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#endif
#ifdef WITH_JSC_MEMORY_PRESSURE
#include <jsc_memory.h>
#endif
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceSection;
#endif
#ifdef WITH_FB_MEMORY_PROFILING
#include "JSCMemory.h"
#endif
#ifdef WITH_FB_JSC_TUNING
#include <jsc_config_android.h>
#endif
#ifdef JSC_HAS_PERF_STATS_API
#include "JSCPerfStats.h"
#endif
static const int64_t NANOSECONDS_IN_SECOND = 1000000000LL;
static const int64_t NANOSECONDS_IN_MILLISECOND = 1000000LL;
namespace facebook {
namespace react {
static std::unordered_map<JSContextRef, JSCExecutor*> s_globalContextRefToJSCExecutor;
static JSValueRef nativePerformanceNow(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
static JSValueRef nativeInjectHMRUpdate(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(Bridge *bridge) {
return std::unique_ptr<JSExecutor>(new JSCExecutor(bridge, cacheDir_, m_jscConfig));
}
JSCExecutor::JSCExecutor(Bridge *bridge, const std::string& cacheDir, const folly::dynamic& jscConfig) :
m_bridge(bridge),
m_deviceCacheDir(cacheDir),
m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()),
m_jscConfig(jscConfig) {
initOnJSVMThread();
}
JSCExecutor::JSCExecutor(
Bridge *bridge,
int workerId,
JSCExecutor *owner,
const std::string& script,
const std::unordered_map<std::string, std::string>& globalObjAsJSON,
const folly::dynamic& jscConfig) :
m_bridge(bridge),
m_workerId(workerId),
m_owner(owner),
m_deviceCacheDir(owner->m_deviceCacheDir),
m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()),
m_jscConfig(jscConfig) {
// We post initOnJSVMThread here so that the owner doesn't have to wait for
// initialization on its own thread
m_messageQueueThread->runOnQueue([this, script, globalObjAsJSON] () {
initOnJSVMThread();
installGlobalFunction(m_context, "postMessage", nativePostMessage);
for (auto& it : globalObjAsJSON) {
setGlobalVariable(it.first, it.second);
}
// Try to load the script from the network if script is a URL
// NB: For security, this will only work in debug builds
std::string scriptSrc;
if (script.find("http://") == 0 || script.find("https://") == 0) {
std::stringstream outfileBuilder;
outfileBuilder << m_deviceCacheDir << "/workerScript" << m_workerId << ".js";
scriptSrc = WebWorkerUtil::loadScriptFromNetworkSync(script, outfileBuilder.str());
} else {
// TODO(9604438): Protect against script does not exist
scriptSrc = WebWorkerUtil::loadScriptFromAssets(script);
}
// TODO(9994180): Throw on error
loadApplicationScript(scriptSrc, script);
});
}
JSCExecutor::~JSCExecutor() {
CHECK(*m_isDestroyed) << "JSCExecutor::destroy() must be called before its destructor!";
}
void JSCExecutor::destroy() {
*m_isDestroyed = true;
if (m_messageQueueThread->isOnThread()) {
terminateOnJSVMThread();
} else {
m_messageQueueThread->runOnQueueSync([this] () {
terminateOnJSVMThread();
});
}
}
void JSCExecutor::initOnJSVMThread() {
#if defined(WITH_FB_JSC_TUNING)
configureJSCForAndroid(m_jscConfig);
#endif
m_context = JSGlobalContextCreateInGroup(nullptr, nullptr);
s_globalContextRefToJSCExecutor[m_context] = this;
installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate);
installGlobalFunction(m_context, "nativePerformanceNow", nativePerformanceNow);
installGlobalFunction(m_context, "nativeStartWorker", nativeStartWorker);
installGlobalFunction(m_context, "nativePostMessageToWorker", nativePostMessageToWorker);
installGlobalFunction(m_context, "nativeTerminateWorker", nativeTerminateWorker);
installGlobalFunction(m_context, "nativeInjectHMRUpdate", nativeInjectHMRUpdate);
installGlobalFunction(m_context, "nativeLoggingHook", JSLogging::nativeHook);
#ifdef WITH_JSC_EXTRA_TRACING
addNativeTracingHooks(m_context);
addNativeProfilingHooks(m_context);
PerfLogging::installNativeHooks(m_context);
#endif
#ifdef WITH_FB_MEMORY_PROFILING
addNativeMemoryHooks(m_context);
#endif
#ifdef JSC_HAS_PERF_STATS_API
addJSCPerfStatsHooks(m_context);
#endif
#if defined(WITH_FB_JSC_TUNING)
configureJSContextForAndroid(m_context, m_jscConfig, m_deviceCacheDir);
#endif
}
void JSCExecutor::terminateOnJSVMThread() {
// terminateOwnedWebWorker mutates m_ownedWorkers so collect all the workers
// to terminate first
std::vector<int> workerIds;
for (auto& it : m_ownedWorkers) {
workerIds.push_back(it.first);
}
for (int workerId : workerIds) {
terminateOwnedWebWorker(workerId);
}
m_batchedBridge.reset();
m_flushedQueueObj.reset();
m_callFunctionObj.reset();
m_invokeCallbackObj.reset();
s_globalContextRefToJSCExecutor.erase(m_context);
JSGlobalContextRelease(m_context);
m_context = nullptr;
}
// Checks if the user is in the pre-parsing cache & StringRef QE.
// Should be removed when these features are no longer gated.
bool JSCExecutor::usePreparsingAndStringRef(){
return m_jscConfig.getDefault("PreparsingStringRef", true).getBool();
}
void JSCExecutor::loadApplicationScript(
const std::string& script,
const std::string& sourceURL) {
ReactMarker::logMarker("loadApplicationScript_startStringConvert");
#if WITH_FBJSCEXTENSIONS
JSStringRef jsScriptRef;
if (usePreparsingAndStringRef()){
jsScriptRef = JSStringCreateWithUTF8CStringExpectAscii(script.c_str(), script.size());
} else {
jsScriptRef = JSStringCreateWithUTF8CString(script.c_str());
}
String jsScript = String::adopt(jsScriptRef);
#else
String jsScript = String::createExpectingAscii(script);
#endif
ReactMarker::logMarker("loadApplicationScript_endStringConvert");
String jsSourceURL(sourceURL.c_str());
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
#endif
evaluateScript(m_context, jsScript, jsSourceURL);
flush();
ReactMarker::logMarker("RUN_JS_BUNDLE_END");
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
}
void JSCExecutor::loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle,
const std::string& startupCode,
const std::string& sourceURL) {
if (!m_unbundle) {
installGlobalFunction(m_context, "nativeRequire", nativeRequire);
}
m_unbundle = std::move(unbundle);
loadApplicationScript(startupCode, sourceURL);
}
bool JSCExecutor::ensureBatchedBridgeObject() {
if (m_batchedBridge) {
return true;
}
Value batchedBridgeValue = Object::getGlobalObject(m_context).getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
return false;
}
m_batchedBridge = folly::make_unique<Object>(batchedBridgeValue.asObject());
m_flushedQueueObj = folly::make_unique<Object>(m_batchedBridge->getProperty("flushedQueue").asObject());
m_callFunctionObj = folly::make_unique<Object>(m_batchedBridge->getProperty("callFunctionReturnFlushedQueue").asObject());
m_invokeCallbackObj = folly::make_unique<Object>(m_batchedBridge->getProperty("invokeCallbackAndReturnFlushedQueue").asObject());
return true;
}
void JSCExecutor::flush() {
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(
TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.flush");
#endif
if (!ensureBatchedBridgeObject()) {
throwJSExecutionException(
"Could not connect to development server.\n"
"Try the following to fix the issue:\n"
"Ensure that the packager server is running\n"
"Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices\n"
"If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device\n"
"If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081");
}
std::string calls = m_flushedQueueObj->callAsFunction().toJSONString();
m_bridge->callNativeModules(*this, calls, true);
}
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.callFunction");
#endif
if (!ensureBatchedBridgeObject()) {
throwJSExecutionException(
"Couldn't call JS module %s, method %s: bridge configuration isn't available. This "
"probably means you're calling a JS module method before bridge setup has completed or without a JS bundle loaded.",
moduleId.c_str(),
methodId.c_str());
}
String argsString = String(folly::toJson(std::move(arguments)).c_str());
String moduleIdStr(moduleId.c_str());
String methodIdStr(methodId.c_str());
JSValueRef args[] = {
JSValueMakeString(m_context, moduleIdStr),
JSValueMakeString(m_context, methodIdStr),
Value::fromJSON(m_context, argsString)
};
auto result = m_callFunctionObj->callAsFunction(3, args);
m_bridge->callNativeModules(*this, result.toJSONString(), true);
}
void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.invokeCallback");
#endif
if (!ensureBatchedBridgeObject()) {
throwJSExecutionException(
"Couldn't invoke JS callback %d: bridge configuration isn't available. This shouldn't be possible. Congratulations.", (int) callbackId);
}
String argsString = String(folly::toJson(std::move(arguments)).c_str());
JSValueRef args[] = {
JSValueMakeNumber(m_context, callbackId),
Value::fromJSON(m_context, argsString)
};
auto result = m_invokeCallbackObj->callAsFunction(2, args);
m_bridge->callNativeModules(*this, result.toJSONString(), true);
}
void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
auto globalObject = JSContextGetGlobalObject(m_context);
String jsPropertyName(propName.c_str());
String jsValueJSON(jsonValue.c_str());
auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}
void* JSCExecutor::getJavaScriptContext() {
return m_context;
}
bool JSCExecutor::supportsProfiling() {
#ifdef WITH_FBSYSTRACE
return true;
#else
return false;
#endif
}
void JSCExecutor::startProfiler(const std::string &titleString) {
#ifdef WITH_JSC_EXTRA_TRACING
JSStringRef title = JSStringCreateWithUTF8CString(titleString.c_str());
#if WITH_REACT_INTERNAL_SETTINGS
JSStartProfiling(m_context, title, false);
#else
JSStartProfiling(m_context, title);
#endif
JSStringRelease(title);
#endif
}
void JSCExecutor::stopProfiler(const std::string &titleString, const std::string& filename) {
#ifdef WITH_JSC_EXTRA_TRACING
JSStringRef title = JSStringCreateWithUTF8CString(titleString.c_str());
facebook::react::stopAndOutputProfilingFile(m_context, title, filename.c_str());
JSStringRelease(title);
#endif
}
void JSCExecutor::handleMemoryPressureUiHidden() {
#ifdef WITH_JSC_MEMORY_PRESSURE
JSHandleMemoryPressure(this, m_context, JSMemoryPressure::UI_HIDDEN);
#endif
}
void JSCExecutor::handleMemoryPressureModerate() {
#ifdef WITH_JSC_MEMORY_PRESSURE
JSHandleMemoryPressure(this, m_context, JSMemoryPressure::MODERATE);
#endif
}
void JSCExecutor::handleMemoryPressureCritical() {
#ifdef WITH_JSC_MEMORY_PRESSURE
JSHandleMemoryPressure(this, m_context, JSMemoryPressure::CRITICAL);
#endif
}
void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
m_bridge->callNativeModules(*this, queueJSON, false);
}
void JSCExecutor::loadModule(uint32_t moduleId) {
auto module = m_unbundle->getModule(moduleId);
auto sourceUrl = String::createExpectingAscii(module.name);
auto source = String::createExpectingAscii(module.code);
evaluateScript(m_context, source, sourceUrl);
}
int JSCExecutor::addWebWorker(
const std::string& script,
JSValueRef workerRef,
JSValueRef globalObjRef) {
static std::atomic_int nextWorkerId(1);
int workerId = nextWorkerId++;
Object globalObj = Value(m_context, globalObjRef).asObject();
auto workerJscConfig = m_jscConfig;
workerJscConfig["isWebWorker"] = true;
auto workerMQT = WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get());
std::unique_ptr<JSCExecutor> worker;
workerMQT->runOnQueueSync([this, &worker, &script, &globalObj, workerId, &workerJscConfig] () {
worker.reset(new JSCExecutor(m_bridge, workerId, this, script, globalObj.toJSONMap(), workerJscConfig));
});
Object workerObj = Value(m_context, workerRef).asObject();
workerObj.makeProtected();
JSCExecutor *workerPtr = worker.get();
std::shared_ptr<MessageQueueThread> sharedMessageQueueThread = worker->m_messageQueueThread;
ExecutorToken token = m_bridge->registerExecutor(
std::move(worker),
std::move(sharedMessageQueueThread));
m_ownedWorkers.emplace(
std::piecewise_construct,
std::forward_as_tuple(workerId),
std::forward_as_tuple(workerPtr, token, std::move(workerObj)));
return workerId;
}
void JSCExecutor::postMessageToOwnedWebWorker(int workerId, JSValueRef message, JSValueRef *exn) {
auto worker = m_ownedWorkers.at(workerId).executor;
std::string msgString = Value(m_context, message).toJSONString();
std::shared_ptr<bool> isWorkerDestroyed = worker->m_isDestroyed;
worker->m_messageQueueThread->runOnQueue([isWorkerDestroyed, worker, msgString] () {
if (*isWorkerDestroyed) {
return;
}
worker->receiveMessageFromOwner(msgString);
});
}
void JSCExecutor::postMessageToOwner(JSValueRef msg) {
std::string msgString = Value(m_context, msg).toJSONString();
std::shared_ptr<bool> ownerIsDestroyed = m_owner->m_isDestroyed;
m_owner->m_messageQueueThread->runOnQueue([workerId=m_workerId, owner=m_owner, ownerIsDestroyed, msgString] () {
if (*ownerIsDestroyed) {
return;
}
owner->receiveMessageFromOwnedWebWorker(workerId, msgString);
});
}
void JSCExecutor::receiveMessageFromOwnedWebWorker(int workerId, const std::string& json) {
Object* workerObj;
try {
workerObj = &m_ownedWorkers.at(workerId).jsObj;
} catch (std::out_of_range& e) {
// Worker was already terminated
return;
}
Value onmessageValue = workerObj->getProperty("onmessage");
if (onmessageValue.isUndefined()) {
return;
}
JSValueRef args[] = { createMessageObject(json) };
onmessageValue.asObject().callAsFunction(1, args);
flush();
}
void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) {
CHECK(m_owner) << "Received message in a Executor that doesn't have an owner!";
JSValueRef args[] = { createMessageObject(msgString) };
Value onmessageValue = Object::getGlobalObject(m_context).getProperty("onmessage");
onmessageValue.asObject().callAsFunction(1, args);
}
void JSCExecutor::terminateOwnedWebWorker(int workerId) {
auto& workerRegistration = m_ownedWorkers.at(workerId);
std::shared_ptr<MessageQueueThread> workerMQT = workerRegistration.executor->m_messageQueueThread;
ExecutorToken workerExecutorToken = workerRegistration.executorToken;
m_ownedWorkers.erase(workerId);
std::unique_ptr<JSExecutor> worker = m_bridge->unregisterExecutor(workerExecutorToken);
worker->destroy();
worker.reset();
workerMQT->quitSynchronous();
}
Object JSCExecutor::createMessageObject(const std::string& msgJson) {
Value rebornJSMsg = Value::fromJSON(m_context, String(msgJson.c_str()));
Object messageObject = Object::create(m_context);
messageObject.setProperty("data", rebornJSMsg);
return messageObject;
}
// Native JS hooks
JSValueRef JSCExecutor::nativePostMessage(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 1) {
*exception = makeJSCException(ctx, "postMessage got wrong number of arguments");
return JSValueMakeUndefined(ctx);
}
JSValueRef msg = arguments[0];
JSCExecutor *webWorker = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
webWorker->postMessageToOwner(msg);
return JSValueMakeUndefined(ctx);
}
static JSValueRef makeInvalidModuleIdJSCException(
JSContextRef ctx,
const JSValueRef id,
JSValueRef *exception) {
std::string message = "Received invalid module ID: ";
message += String::adopt(JSValueToStringCopy(ctx, id, exception)).str();
return makeJSCException(ctx, message.c_str());
}
JSValueRef JSCExecutor::nativeRequire(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 1) {
*exception = makeJSCException(ctx, "Got wrong number of args");
return JSValueMakeUndefined(ctx);
}
JSCExecutor *executor;
try {
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
} catch (std::out_of_range& e) {
*exception = makeJSCException(ctx, "Global JS context didn't map to a valid executor");
return JSValueMakeUndefined(ctx);
}
double moduleId = JSValueToNumber(ctx, arguments[0], exception);
if (moduleId <= (double) std::numeric_limits<uint32_t>::max() && moduleId >= 0.0) {
try {
executor->loadModule(moduleId);
} catch (JSModulesUnbundle::ModuleNotFound&) {
*exception = makeInvalidModuleIdJSCException(ctx, arguments[0], exception);
}
} else {
*exception = makeInvalidModuleIdJSCException(ctx, arguments[0], exception);
}
return JSValueMakeUndefined(ctx);
}
static JSValueRef createErrorString(JSContextRef ctx, const char *msg) {
return JSValueMakeString(ctx, String(msg));
}
JSValueRef JSCExecutor::nativeFlushQueueImmediate(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 1) {
*exception = createErrorString(ctx, "Got wrong number of args");
return JSValueMakeUndefined(ctx);
}
JSCExecutor *executor;
try {
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
} catch (std::out_of_range& e) {
*exception = createErrorString(ctx, "Global JS context didn't map to a valid executor");
return JSValueMakeUndefined(ctx);
}
std::string resStr = Value(ctx, arguments[0]).toJSONString();
executor->flushQueueImmediate(resStr);
return JSValueMakeUndefined(ctx);
}
JSValueRef JSCExecutor::nativeStartWorker(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 3) {
*exception = createErrorString(ctx, "Got wrong number of args");
return JSValueMakeUndefined(ctx);
}
std::string scriptFile = Value(ctx, arguments[0]).toString().str();
JSValueRef worker = arguments[1];
JSValueRef globalObj = arguments[2];
JSCExecutor *executor;
try {
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
} catch (std::out_of_range& e) {
*exception = createErrorString(ctx, "Global JS context didn't map to a valid executor");
return JSValueMakeUndefined(ctx);
}
int workerId = executor->addWebWorker(scriptFile, worker, globalObj);
return JSValueMakeNumber(ctx, workerId);
}
JSValueRef JSCExecutor::nativePostMessageToWorker(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 2) {
*exception = createErrorString(ctx, "Got wrong number of args");
return JSValueMakeUndefined(ctx);
}
double workerDouble = JSValueToNumber(ctx, arguments[0], exception);
if (workerDouble != workerDouble) {
*exception = createErrorString(ctx, "Got invalid worker id");
return JSValueMakeUndefined(ctx);
}
JSCExecutor *executor;
try {
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
} catch (std::out_of_range& e) {
*exception = createErrorString(ctx, "Global JS context didn't map to a valid executor");
return JSValueMakeUndefined(ctx);
}
executor->postMessageToOwnedWebWorker((int) workerDouble, arguments[1], exception);
return JSValueMakeUndefined(ctx);
}
JSValueRef JSCExecutor::nativeTerminateWorker(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 1) {
*exception = createErrorString(ctx, "Got wrong number of args");
return JSValueMakeUndefined(ctx);
}
double workerDouble = JSValueToNumber(ctx, arguments[0], exception);
if (workerDouble != workerDouble) {
*exception = createErrorString(ctx, "Got invalid worker id");
return JSValueMakeUndefined(ctx);
}
JSCExecutor *executor;
try {
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
} catch (std::out_of_range& e) {
*exception = createErrorString(ctx, "Global JS context didn't map to a valid executor");
return JSValueMakeUndefined(ctx);
}
executor->terminateOwnedWebWorker((int) workerDouble);
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativePerformanceNow(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[], JSValueRef *exception) {
// This is equivalent to android.os.SystemClock.elapsedRealtime() in native
struct timespec now;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
int64_t nano = now.tv_sec * NANOSECONDS_IN_SECOND + now.tv_nsec;
return JSValueMakeNumber(ctx, (nano / (double)NANOSECONDS_IN_MILLISECOND));
}
static JSValueRef nativeInjectHMRUpdate(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[], JSValueRef *exception) {
String execJSString = Value(ctx, arguments[0]).toString();
String jsURL = Value(ctx, arguments[1]).toString();
evaluateScript(ctx, execJSString, jsURL);
return JSValueMakeUndefined(ctx);
}
} }

View File

@ -1,168 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <folly/json.h>
#include <JavaScriptCore/JSContextRef.h>
#include "ExecutorToken.h"
#include "Executor.h"
#include "JSCHelpers.h"
#include "Value.h"
namespace facebook {
namespace react {
class MessageQueueThread;
class JSCExecutorFactory : public JSExecutorFactory {
public:
JSCExecutorFactory(const std::string& cacheDir, const folly::dynamic& jscConfig) :
cacheDir_(cacheDir),
m_jscConfig(jscConfig) {}
virtual std::unique_ptr<JSExecutor> createJSExecutor(Bridge *bridge) override;
private:
std::string cacheDir_;
folly::dynamic m_jscConfig;
};
class JSCExecutor;
class WorkerRegistration : public noncopyable {
public:
explicit WorkerRegistration(JSCExecutor* executor, ExecutorToken executorToken, Object jsObj) :
executor(executor),
executorToken(executorToken),
jsObj(std::move(jsObj)) {}
JSCExecutor *executor;
ExecutorToken executorToken;
Object jsObj;
};
class JSCExecutor : public JSExecutor {
public:
/**
* Must be invoked from thread this Executor will run on.
*/
explicit JSCExecutor(Bridge *bridge, const std::string& cacheDir, const folly::dynamic& jscConfig);
~JSCExecutor() override;
virtual void loadApplicationScript(
const std::string& script,
const std::string& sourceURL) override;
virtual void loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> unbundle,
const std::string& startupCode,
const std::string& sourceURL) override;
virtual void callFunction(
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) override;
virtual void invokeCallback(
const double callbackId,
const folly::dynamic& arguments) override;
virtual void setGlobalVariable(
const std::string& propName,
const std::string& jsonValue) override;
virtual void* getJavaScriptContext() override;
virtual bool supportsProfiling() override;
virtual void startProfiler(const std::string &titleString) override;
virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;
virtual void handleMemoryPressureUiHidden() override;
virtual void handleMemoryPressureModerate() override;
virtual void handleMemoryPressureCritical() override;
virtual void destroy() override;
void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback);
private:
JSGlobalContextRef m_context;
Bridge *m_bridge;
int m_workerId = 0; // if this is a worker executor, this is non-zero
JSCExecutor *m_owner = nullptr; // if this is a worker executor, this is non-null
std::shared_ptr<bool> m_isDestroyed = std::shared_ptr<bool>(new bool(false));
std::unordered_map<int, WorkerRegistration> m_ownedWorkers;
std::string m_deviceCacheDir;
std::shared_ptr<MessageQueueThread> m_messageQueueThread;
std::unique_ptr<JSModulesUnbundle> m_unbundle;
folly::dynamic m_jscConfig;
std::unique_ptr<Object> m_batchedBridge;
std::unique_ptr<Object> m_flushedQueueObj;
std::unique_ptr<Object> m_callFunctionObj;
std::unique_ptr<Object> m_invokeCallbackObj;
/**
* WebWorker constructor. Must be invoked from thread this Executor will run on.
*/
explicit JSCExecutor(
Bridge *bridge,
int workerId,
JSCExecutor *owner,
const std::string& script,
const std::unordered_map<std::string, std::string>& globalObjAsJSON,
const folly::dynamic& jscConfig);
void initOnJSVMThread();
void terminateOnJSVMThread();
void flush();
void flushQueueImmediate(std::string queueJSON);
void loadModule(uint32_t moduleId);
bool ensureBatchedBridgeObject();
int addWebWorker(const std::string& script, JSValueRef workerRef, JSValueRef globalObjRef);
void postMessageToOwnedWebWorker(int worker, JSValueRef message, JSValueRef *exn);
void postMessageToOwner(JSValueRef result);
void receiveMessageFromOwnedWebWorker(int workerId, const std::string& message);
void receiveMessageFromOwner(const std::string &msgString);
void terminateOwnedWebWorker(int worker);
Object createMessageObject(const std::string& msgData);
bool usePreparsingAndStringRef();
static JSValueRef nativeStartWorker(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
static JSValueRef nativePostMessageToWorker(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
static JSValueRef nativeTerminateWorker(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
static JSValueRef nativePostMessage(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
static JSValueRef nativeRequire(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
static JSValueRef nativeFlushQueueImmediate(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
};
} }

View File

@ -1,59 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCHelpers.h"
#include <JavaScriptCore/JSStringRef.h>
#include <glog/logging.h>
#include "Value.h"
#if WITH_FBJSCEXTENSIONS
#include <jsc_preparsing_cache.h>
#endif
namespace facebook {
namespace react {
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback) {
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
JSObjectRef functionObj = JSObjectMakeFunctionWithCallback(
ctx, jsName, callback);
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
JSObjectSetProperty(ctx, globalObject, jsName, functionObj, 0, NULL);
JSStringRelease(jsName);
}
JSValueRef makeJSCException(
JSContextRef ctx,
const char* exception_text) {
JSStringRef message = JSStringCreateWithUTF8CString(exception_text);
JSValueRef exceptionString = JSValueMakeString(ctx, message);
JSStringRelease(message);
return JSValueToObject(ctx, exceptionString, NULL);
}
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source) {
JSValueRef exn, result;
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
if (result == nullptr) {
Value exception = Value(context, exn);
std::string exceptionText = exception.toString().str();
LOG(ERROR) << "Got JS Exception: " << exceptionText;
auto line = exception.asObject().getProperty("line");
std::ostringstream locationInfo;
std::string file = source != nullptr ? String::ref(source).str() : "";
locationInfo << "(" << (file.length() ? file : "<unknown file>");
if (line != nullptr && line.isNumber()) {
locationInfo << ":" << line.asInteger();
}
locationInfo << ")";
throwJSExecutionException("%s %s", exceptionText.c_str(), locationInfo.str().c_str());
}
return result;
}
} }

View File

@ -1,47 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSValueRef.h>
#include <alloca.h>
#include <stdexcept>
#include <algorithm>
namespace facebook {
namespace react {
struct JsException : std::runtime_error {
using std::runtime_error::runtime_error;
};
inline void throwJSExecutionException(const char* msg) {
throw JsException(msg);
}
template <typename... Args>
inline void throwJSExecutionException(const char* fmt, Args... args) {
int msgSize = snprintf(nullptr, 0, fmt, args...);
msgSize = std::min(512, msgSize + 1);
char *msg = (char*) alloca(msgSize);
snprintf(msg, msgSize, fmt, args...);
throw JsException(msg);
}
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback);
JSValueRef makeJSCException(
JSContextRef ctx,
const char* exception_text);
JSValueRef evaluateScript(
JSContextRef ctx,
JSStringRef script,
JSStringRef sourceURL);
} }

View File

@ -1,84 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <stdio.h>
#include <string.h>
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include <jsc_legacy_profiler.h>
#include "JSCHelpers.h"
#include "JSCLegacyProfiler.h"
#include "Value.h"
static JSValueRef nativeProfilerStart(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (argumentCount < 1) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"nativeProfilerStart: requires at least 1 argument");
}
return JSValueMakeUndefined(ctx);
}
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], exception);
#if WITH_REACT_INTERNAL_SETTINGS
JSStartProfiling(ctx, title, false);
#else
JSStartProfiling(ctx, title);
#endif
JSStringRelease(title);
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeProfilerEnd(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (argumentCount < 1) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"nativeProfilerEnd: requires at least 1 argument");
}
return JSValueMakeUndefined(ctx);
}
std::string writeLocation("/sdcard/");
if (argumentCount > 1) {
JSStringRef fileName = JSValueToStringCopy(ctx, arguments[1], exception);
writeLocation += facebook::react::String::ref(fileName).str();
JSStringRelease(fileName);
} else {
writeLocation += "profile.json";
}
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], exception);
JSEndProfilingAndRender(ctx, title, writeLocation.c_str());
JSStringRelease(title);
return JSValueMakeUndefined(ctx);
}
namespace facebook {
namespace react {
void stopAndOutputProfilingFile(
JSContextRef ctx,
JSStringRef title,
const char *filename) {
JSEndProfilingAndRender(ctx, title, filename);
}
void addNativeProfilingHooks(JSGlobalContextRef ctx) {
JSEnableByteCodeProfiling();
installGlobalFunction(ctx, "nativeProfilerStart", nativeProfilerStart);
installGlobalFunction(ctx, "nativeProfilerEnd", nativeProfilerEnd);
}
} }

View File

@ -1,15 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
void addNativeProfilingHooks(JSGlobalContextRef ctx);
void stopAndOutputProfilingFile(
JSContextRef ctx,
JSStringRef title,
const char *filename);
} }

View File

@ -1,48 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <stdio.h>
#include <string.h>
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include "JSCHelpers.h"
#include "Value.h"
#ifdef WITH_FB_MEMORY_PROFILING
static JSValueRef nativeCaptureHeap(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (argumentCount < 1) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"nativeCaptureHeap requires the path to save the capture");
}
return JSValueMakeUndefined(ctx);
}
JSStringRef outputFilename = JSValueToStringCopy(ctx, arguments[0], exception);
std::string finalFilename = facebook::react::String::ref(outputFilename).str();
JSCaptureHeap(ctx, finalFilename.c_str(), exception);
JSStringRelease(outputFilename);
return JSValueMakeUndefined(ctx);
}
#endif // WITH_FB_MEMORY_PROFILING
namespace facebook {
namespace react {
void addNativeMemoryHooks(JSGlobalContextRef ctx) {
#ifdef WITH_FB_MEMORY_PROFILING
installGlobalFunction(ctx, "nativeCaptureHeap", nativeCaptureHeap);
#endif // WITH_FB_MEMORY_PROFILING
}
} }

View File

@ -1,11 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
void addNativeMemoryHooks(JSGlobalContextRef ctx);
} }

View File

@ -1,65 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCPerfStats.h"
#ifdef JSC_HAS_PERF_STATS_API
#include <JavaScriptCore/JSPerfStats.h>
#include <JavaScriptCore/JSValueRef.h>
#include "JSCHelpers.h"
#include "Value.h"
static JSValueRef nativeGetHeapStats(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
JSHeapStats heapStats = {0};
JSGetHeapStats(ctx, &heapStats);
auto result = facebook::react::Object::create(ctx);
result.setProperty("size", {ctx, JSValueMakeNumber(ctx, heapStats.size)});
result.setProperty("extra_size", {ctx, JSValueMakeNumber(ctx, heapStats.extraSize)});
result.setProperty("capacity", {ctx, JSValueMakeNumber(ctx, heapStats.capacity)});
result.setProperty("object_count", {ctx, JSValueMakeNumber(ctx, heapStats.objectCount)});
return (JSObjectRef) result;
}
static JSValueRef nativeGetGCStats(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
JSGCStats gcStats = {0};
JSGetGCStats(ctx, &gcStats);
auto result = facebook::react::Object::create(ctx);
result.setProperty(
"last_full_gc_length",
{ctx, JSValueMakeNumber(ctx, gcStats.lastFullGCLength)});
result.setProperty(
"last_eden_gc_length",
{ctx, JSValueMakeNumber(ctx, gcStats.lastEdenGCLength)});
return (JSObjectRef) result;
}
#endif
namespace facebook {
namespace react {
void addJSCPerfStatsHooks(JSGlobalContextRef ctx) {
#ifdef JSC_HAS_PERF_STATS_API
installGlobalFunction(ctx, "nativeGetHeapStats", nativeGetHeapStats);
installGlobalFunction(ctx, "nativeGetGCStats", nativeGetGCStats);
#endif
}
} }

View File

@ -1,12 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
void addJSCPerfStatsHooks(JSGlobalContextRef ctx);
} }

View File

@ -1,478 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <algorithm>
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include <fbsystrace.h>
#include <sys/types.h>
#include <unistd.h>
#include "JSCHelpers.h"
using std::min;
static const char *ENABLED_FBSYSTRACE_PROFILE_NAME = "__fbsystrace__";
static uint64_t tagFromJSValue(
JSContextRef ctx,
JSValueRef value,
JSValueRef* exception) {
// XXX validate that this is a lossless conversion.
// XXX should we just have separate functions for bridge, infra, and apps,
// then drop this argument to save time?
(void)exception;
uint64_t tag = (uint64_t) JSValueToNumber(ctx, value, NULL);
return tag;
}
static int64_t int64FromJSValue(
JSContextRef ctx,
JSValueRef value,
JSValueRef* exception) {
(void)exception;
int64_t num = (int64_t) JSValueToNumber(ctx, value, NULL);
return num;
}
static size_t copyTruncatedAsciiChars(
char* buf,
size_t bufLen,
JSContextRef ctx,
JSValueRef value,
size_t maxLen) {
JSStringRef jsString = JSValueToStringCopy(ctx, value, NULL);
size_t stringLen = JSStringGetLength(jsString);
// Unlike the Java version, we truncate from the end of the string,
// rather than the beginning.
size_t toWrite = min(stringLen, min(bufLen, maxLen));
const char *startBuf = buf;
const JSChar* chars = JSStringGetCharactersPtr(jsString);
while (toWrite-- > 0) {
*(buf++) = (char)*(chars++);
}
JSStringRelease(jsString);
// Return the number of bytes written
return buf - startBuf;
}
static size_t copyArgsToBuffer(
char* buf,
size_t bufLen,
size_t pos,
JSContextRef ctx,
size_t argumentCount,
const JSValueRef arguments[]) {
char separator = '|';
for (
size_t idx = 0;
idx + 1 < argumentCount; // Make sure key and value are present.
idx += 2) {
JSValueRef key = arguments[idx];
JSValueRef value = arguments[idx+1];
buf[pos++] = separator;
separator = ';';
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
pos += copyTruncatedAsciiChars(
buf + pos, bufLen - pos, ctx, key, FBSYSTRACE_MAX_MESSAGE_LENGTH);
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
buf[pos++] = '=';
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
pos += copyTruncatedAsciiChars(
buf + pos, bufLen - pos, ctx, value, FBSYSTRACE_MAX_MESSAGE_LENGTH);
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
}
return pos;
}
static JSValueRef nativeTraceBeginSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 2)) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"nativeTraceBeginSection: requires at least 2 arguments");
}
return JSValueMakeUndefined(ctx);
}
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
pos += snprintf(buf + pos, sizeof(buf) - pos, "B|%d|", getpid());
// Skip the overflow check here because the int will be small.
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
// Skip the overflow check here because the section name will be small-ish.
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 2, arguments + 2);
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
goto flush;
}
flush:
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeTraceEndSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 1)) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"nativeTraceEndSection: requires at least 1 argument");
}
return JSValueMakeUndefined(ctx);
}
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
if (FBSYSTRACE_LIKELY(argumentCount == 1)) {
fbsystrace_end_section(tag);
} else {
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
buf[pos++] = 'E';
buf[pos++] = '|';
buf[pos++] = '|';
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 1, arguments + 1);
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
goto flush;
}
flush:
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
}
return JSValueMakeUndefined(ctx);
}
static JSValueRef beginOrEndAsync(
bool isEnd,
bool isFlow,
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 3)) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"beginOrEndAsync: requires at least 3 arguments");
}
return JSValueMakeUndefined(ctx);
}
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
// This uses an if-then-else instruction in ARMv7, which should be cheaper
// than a full branch.
buf[pos++] = ((isFlow) ? (isEnd ? 'f' : 's') : (isEnd ? 'F' : 'S'));
pos += snprintf(buf + pos, sizeof(buf) - pos, "|%d|", getpid());
// Skip the overflow check here because the int will be small.
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
// Skip the overflow check here because the section name will be small-ish.
// I tried some trickery to avoid a branch here, but gcc did not cooperate.
// We could consider changing the implementation to be lest branchy in the
// future.
// This is not required for flow use an or to avoid introducing another branch
if (!(isEnd | isFlow)) {
buf[pos++] = '<';
buf[pos++] = '0';
buf[pos++] = '>';
}
buf[pos++] = '|';
// Append the cookie. It should be an integer, but copyTruncatedAsciiChars
// will automatically convert it to a string. We might be able to get more
// performance by just getting the number and doing to string
// conversion ourselves. We truncate to FBSYSTRACE_MAX_SECTION_NAME_LENGTH
// just to make sure we can avoid the overflow check even if the caller
// passes in something bad.
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[2], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 3, arguments + 3);
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
goto flush;
}
flush:
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
return JSValueMakeUndefined(ctx);
}
static JSValueRef stageAsync(
bool isFlow,
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 4)) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"stageAsync: requires at least 4 arguments");
}
return JSValueMakeUndefined(ctx);
}
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
buf[pos++] = (isFlow ? 't' : 'T');
pos += snprintf(buf + pos, sizeof(buf) - pos, "|%d", getpid());
// Skip the overflow check here because the int will be small.
// Arguments are section name, cookie, and stage name.
// All added together, they still cannot cause an overflow.
for (int i = 1; i < 4; i++) {
buf[pos++] = '|';
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[i], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
}
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeTraceBeginAsyncSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(
false /* isEnd */,
false /* isFlow */,
ctx,
function,
thisObject,
argumentCount,
arguments,
exception);
}
static JSValueRef nativeTraceEndAsyncSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(
true /* isEnd */,
false /* isFlow */,
ctx,
function,
thisObject,
argumentCount,
arguments,
exception);
}
static JSValueRef nativeTraceAsyncSectionStage(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return stageAsync(
false /* isFlow */,
ctx,
function,
thisObject,
argumentCount,
arguments,
exception);
}
static JSValueRef nativeTraceBeginAsyncFlow(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(
false /* isEnd */,
true /* isFlow */,
ctx,
function,
thisObject,
argumentCount,
arguments,
exception);
}
static JSValueRef nativeTraceEndAsyncFlow(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(
true /* isEnd */,
true /* isFlow */,
ctx,
function,
thisObject,
argumentCount,
arguments,
exception);
}
static JSValueRef nativeTraceAsyncFlowStage(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return stageAsync(
true /* isFlow */,
ctx,
function,
thisObject,
argumentCount,
arguments,
exception);
}
static JSValueRef nativeTraceCounter(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 3)) {
if (exception) {
*exception = facebook::react::makeJSCException(
ctx,
"nativeTraceCounter: requires at least 3 arguments");
}
return JSValueMakeUndefined(ctx);
}
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t len = copyTruncatedAsciiChars(buf, sizeof(buf), ctx,
arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
buf[min(len,(FBSYSTRACE_MAX_MESSAGE_LENGTH-1))] = 0;
int64_t value = int64FromJSValue(ctx, arguments[2], exception);
fbsystrace_counter(tag, buf, value);
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeTraceBeginLegacy(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_LIKELY(argumentCount >= 1)) {
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
}
JSStringRef title = JSStringCreateWithUTF8CString(ENABLED_FBSYSTRACE_PROFILE_NAME);
#if WITH_REACT_INTERNAL_SETTINGS
JSStartProfiling(ctx, title, true);
#else
JSStartProfiling(ctx, title);
#endif
JSStringRelease(title);
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeTraceEndLegacy(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_LIKELY(argumentCount >= 1)) {
uint64_t tag = tagFromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return JSValueMakeUndefined(ctx);
}
}
JSStringRef title = JSStringCreateWithUTF8CString(ENABLED_FBSYSTRACE_PROFILE_NAME);
JSEndProfiling(ctx, title);
JSStringRelease(title);
return JSValueMakeUndefined(ctx);
}
namespace facebook {
namespace react {
void addNativeTracingHooks(JSGlobalContextRef ctx) {
installGlobalFunction(ctx, "nativeTraceBeginSection", nativeTraceBeginSection);
installGlobalFunction(ctx, "nativeTraceEndSection", nativeTraceEndSection);
installGlobalFunction(ctx, "nativeTraceBeginLegacy", nativeTraceBeginLegacy);
installGlobalFunction(ctx, "nativeTraceEndLegacy", nativeTraceEndLegacy);
installGlobalFunction(ctx, "nativeTraceBeginAsyncSection", nativeTraceBeginAsyncSection);
installGlobalFunction(ctx, "nativeTraceEndAsyncSection", nativeTraceEndAsyncSection);
installGlobalFunction(ctx, "nativeTraceAsyncSectionStage", nativeTraceAsyncSectionStage);
installGlobalFunction(ctx, "nativeTraceBeginAsyncFlow", nativeTraceBeginAsyncFlow);
installGlobalFunction(ctx, "nativeTraceEndAsyncFlow", nativeTraceEndAsyncFlow);
installGlobalFunction(ctx, "nativeTraceAsyncFlowStage", nativeTraceAsyncFlowStage);
installGlobalFunction(ctx, "nativeTraceCounter", nativeTraceCounter);
}
} }

View File

@ -1,11 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
void addNativeTracingHooks(JSGlobalContextRef ctx);
} }

View File

@ -1,35 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cstdint>
#include <string>
#include <stdexcept>
#include "noncopyable.h"
namespace facebook {
namespace react {
class JSModulesUnbundle : noncopyable {
/**
* Represents the set of JavaScript modules that the application consists of.
* The source code of each module can be retrieved by module ID.
*
* The class is non-copyable because copying instances might involve copying
* several megabytes of memory.
*/
public:
class ModuleNotFound : public std::out_of_range {
using std::out_of_range::out_of_range;
};
struct Module {
std::string name;
std::string code;
};
virtual ~JSModulesUnbundle() {}
virtual Module getModule(uint32_t moduleId) const = 0;
};
}
}

View File

@ -1,39 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <condition_variable>
#include <functional>
#include <mutex>
namespace facebook {
namespace react {
class MessageQueueThread {
public:
virtual ~MessageQueueThread() {}
virtual void runOnQueue(std::function<void()>&&) = 0;
virtual bool isOnThread() = 0;
// quitSynchronous() should synchronously ensure that no further tasks will run on the queue.
virtual void quitSynchronous() = 0;
void runOnQueueSync(std::function<void()>&& runnable) {
std::mutex signalMutex;
std::condition_variable signalCv;
bool runnableComplete = false;
runOnQueue([&] () mutable {
std::lock_guard<std::mutex> lock(signalMutex);
runnable();
runnableComplete = true;
signalCv.notify_one();
});
std::unique_lock<std::mutex> lock(signalMutex);
signalCv.wait(lock, [&runnableComplete] { return runnableComplete; });
}
};
}}

View File

@ -1,74 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "MethodCall.h"
#include <folly/json.h>
#include <stdexcept>
namespace facebook {
namespace react {
#define REQUEST_MODULE_IDS 0
#define REQUEST_METHOD_IDS 1
#define REQUEST_PARAMSS 2
#define REQUEST_CALLID 3
std::vector<MethodCall> parseMethodCalls(const std::string& json) {
folly::dynamic jsonData = folly::parseJson(json);
if (jsonData.isNull()) {
return {};
}
if (!jsonData.isArray()) {
throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: ", jsonData.typeName()));
}
if (jsonData.size() < REQUEST_PARAMSS + 1) {
throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: size == ", jsonData.size()));
}
auto moduleIds = jsonData[REQUEST_MODULE_IDS];
auto methodIds = jsonData[REQUEST_METHOD_IDS];
auto params = jsonData[REQUEST_PARAMSS];
int callId = -1;
if (!moduleIds.isArray() || !methodIds.isArray() || !params.isArray()) {
throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: ", json.c_str()));
}
if (jsonData.size() > REQUEST_CALLID) {
if (!jsonData[REQUEST_CALLID].isInt()) {
throw std::invalid_argument(
folly::to<std::string>("Did not get valid calls back from JS: %s", json.c_str()));
} else {
callId = jsonData[REQUEST_CALLID].getInt();
}
}
std::vector<MethodCall> methodCalls;
for (size_t i = 0; i < moduleIds.size(); i++) {
auto paramsValue = params[i];
if (!paramsValue.isArray()) {
throw std::invalid_argument(
folly::to<std::string>("Call argument isn't an array"));
}
methodCalls.emplace_back(
moduleIds[i].getInt(),
methodIds[i].getInt(),
std::move(params[i]),
callId);
// only incremement callid if contains valid callid as callid is optional
callId += (callId != -1) ? 1 : 0;
}
return methodCalls;
}
}}

View File

@ -1,29 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <string>
#include <vector>
#include <map>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
struct MethodCall {
int moduleId;
int methodId;
folly::dynamic arguments;
int callId;
MethodCall(int mod, int meth, folly::dynamic args, int cid)
: moduleId(mod)
, methodId(meth)
, arguments(std::move(args))
, callId(cid) {}
};
std::vector<MethodCall> parseMethodCalls(const std::string& json);
} }

View File

@ -1,30 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Platform.h"
namespace facebook {
namespace react {
namespace ReactMarker {
LogMarker logMarker;
};
namespace MessageQueues {
GetCurrentMessageQueueThread getCurrentMessageQueueThread;
};
namespace WebWorkerUtil {
WebWorkerQueueFactory createWebWorkerThread;
LoadScriptFromAssets loadScriptFromAssets;
LoadScriptFromNetworkSync loadScriptFromNetworkSync;
};
namespace PerfLogging {
InstallNativeHooks installNativeHooks;
};
namespace JSLogging {
JSCNativeHook nativeHook = nullptr;
};
} }

View File

@ -1,52 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <functional>
#include <memory>
#include <string>
#include "MessageQueueThread.h"
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
namespace ReactMarker {
using LogMarker = std::function<void(const std::string&)>;
extern LogMarker logMarker;
};
namespace MessageQueues {
using GetCurrentMessageQueueThread = std::function<std::unique_ptr<MessageQueueThread>()>;
extern GetCurrentMessageQueueThread getCurrentMessageQueueThread;
};
namespace WebWorkerUtil {
using WebWorkerQueueFactory = std::function<std::unique_ptr<MessageQueueThread>(int id, MessageQueueThread* ownerMessageQueue)>;
extern WebWorkerQueueFactory createWebWorkerThread;
using LoadScriptFromAssets = std::function<std::string(const std::string& assetName)>;
extern LoadScriptFromAssets loadScriptFromAssets;
using LoadScriptFromNetworkSync = std::function<std::string(const std::string& url, const std::string& tempfileName)>;
extern LoadScriptFromNetworkSync loadScriptFromNetworkSync;
};
namespace PerfLogging {
using InstallNativeHooks = std::function<void(JSGlobalContextRef)>;
extern InstallNativeHooks installNativeHooks;
};
namespace JSLogging {
using JSCNativeHook = JSValueRef (*) (
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[], JSValueRef *exception);
extern JSCNativeHook nativeHook;
};
} }

View File

@ -1,147 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Value.h"
#include "JSCHelpers.h"
namespace facebook {
namespace react {
Value::Value(JSContextRef context, JSValueRef value) :
m_context(context),
m_value(value)
{
}
Value::Value(Value&& other) :
m_context(other.m_context),
m_value(other.m_value)
{
other.m_value = nullptr;
}
JSContextRef Value::context() const {
return m_context;
}
std::string Value::toJSONString(unsigned indent) const {
JSValueRef exn;
auto stringToAdopt = JSValueCreateJSONString(m_context, m_value, indent, &exn);
if (stringToAdopt == nullptr) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Exception creating JSON string: %s", exceptionText.c_str());
}
return String::adopt(stringToAdopt).str();
}
/* static */
Value Value::fromJSON(JSContextRef ctx, const String& json) {
auto result = JSValueMakeFromJSONString(ctx, json);
if (!result) {
throwJSExecutionException("Failed to create String from JSON");
}
return Value(ctx, result);
}
Object Value::asObject() {
JSValueRef exn;
JSObjectRef jsObj = JSValueToObject(context(), m_value, &exn);
if (!jsObj) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to convert to object: %s", exceptionText.c_str());
}
Object ret = Object(context(), jsObj);
m_value = nullptr;
return std::move(ret);
}
Object::operator Value() const {
return Value(m_context, m_obj);
}
Value Object::callAsFunction(int nArgs, JSValueRef args[]) {
JSValueRef exn;
JSValueRef result = JSObjectCallAsFunction(m_context, m_obj, NULL, nArgs, args, &exn);
if (!result) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Exception calling JS function: %s", exceptionText.c_str());
}
return Value(m_context, result);
}
Value Object::callAsFunction() {
JSValueRef args[0];
return callAsFunction(0, args);
}
Value Object::getProperty(const String& propName) const {
JSValueRef exn;
JSValueRef property = JSObjectGetProperty(m_context, m_obj, propName, &exn);
if (!property) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to get property: %s", exceptionText.c_str());
}
return Value(m_context, property);
}
Value Object::getPropertyAtIndex(unsigned index) const {
JSValueRef exn;
JSValueRef property = JSObjectGetPropertyAtIndex(m_context, m_obj, index, &exn);
if (!property) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to get property at index %u: %s", index, exceptionText.c_str());
}
return Value(m_context, property);
}
Value Object::getProperty(const char *propName) const {
return getProperty(String(propName));
}
void Object::setProperty(const String& propName, const Value& value) const {
JSValueRef exn = NULL;
JSObjectSetProperty(m_context, m_obj, propName, value, kJSPropertyAttributeNone, &exn);
if (exn) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to set property: %s", exceptionText.c_str());
}
}
void Object::setProperty(const char *propName, const Value& value) const {
setProperty(String(propName), value);
}
std::vector<std::string> Object::getPropertyNames() const {
std::vector<std::string> names;
auto namesRef = JSObjectCopyPropertyNames(m_context, m_obj);
size_t count = JSPropertyNameArrayGetCount(namesRef);
for (size_t i = 0; i < count; i++) {
auto string = String::ref(JSPropertyNameArrayGetNameAtIndex(namesRef, i));
names.emplace_back(string.str());
}
JSPropertyNameArrayRelease(namesRef);
return names;
}
std::unordered_map<std::string, std::string> Object::toJSONMap() const {
std::unordered_map<std::string, std::string> map;
auto namesRef = JSObjectCopyPropertyNames(m_context, m_obj);
size_t count = JSPropertyNameArrayGetCount(namesRef);
for (size_t i = 0; i < count; i++) {
auto key = String::ref(JSPropertyNameArrayGetNameAtIndex(namesRef, i));
map.emplace(key.str(), getProperty(key).toJSONString());
}
JSPropertyNameArrayRelease(namesRef);
return map;
}
/* static */
Object Object::create(JSContextRef ctx) {
JSObjectRef newObj = JSObjectMake(
ctx,
NULL, // create instance of default object class
NULL); // no private data
return Object(ctx, newObj);
}
} }

View File

@ -1,228 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSRetainPtr.h>
#include <JavaScriptCore/JSStringRef.h>
#include <JavaScriptCore/JSValueRef.h>
#include "noncopyable.h"
#if WITH_FBJSCEXTENSIONS
#include <jsc_stringref.h>
#endif
namespace facebook {
namespace react {
class Value;
class Context;
class String : public noncopyable {
public:
explicit String(const char* utf8) :
m_string(Adopt, JSStringCreateWithUTF8CString(utf8))
{}
String(String&& other) :
m_string(Adopt, other.m_string.leakRef())
{}
String(const String& other) :
m_string(other.m_string)
{}
operator JSStringRef() const {
return m_string.get();
}
// Length in characters
size_t length() const {
return JSStringGetLength(m_string.get());
}
// Length in bytes of a null-terminated utf8 encoded value
size_t utf8Size() const {
return JSStringGetMaximumUTF8CStringSize(m_string.get());
}
std::string str() const {
size_t reserved = utf8Size();
char* bytes = new char[reserved];
size_t length = JSStringGetUTF8CString(m_string.get(), bytes, reserved) - 1;
std::unique_ptr<char[]> retainedBytes(bytes);
return std::string(bytes, length);
}
// Assumes that utf8 is null terminated
bool equals(const char* utf8) {
return JSStringIsEqualToUTF8CString(m_string.get(), utf8);
}
static String createExpectingAscii(std::string const &utf8) {
#if WITH_FBJSCEXTENSIONS
return String(Adopt, JSStringCreateWithUTF8CStringExpectAscii(utf8.c_str(), utf8.size()));
#else
return String(Adopt, JSStringCreateWithUTF8CString(utf8.c_str()));
#endif
}
static String ref(JSStringRef string) {
return String(string);
}
static String adopt(JSStringRef string) {
return String(Adopt, string);
}
private:
explicit String(JSStringRef string) :
m_string(string)
{}
String(AdoptTag tag, JSStringRef string) :
m_string(tag, string)
{}
JSRetainPtr<JSStringRef> m_string;
};
class Object : public noncopyable {
public:
Object(JSContextRef context, JSObjectRef obj) :
m_context(context),
m_obj(obj)
{}
Object(Object&& other) :
m_context(other.m_context),
m_obj(other.m_obj),
m_isProtected(other.m_isProtected) {
other.m_obj = nullptr;
other.m_isProtected = false;
}
~Object() {
if (m_isProtected && m_obj) {
JSValueUnprotect(m_context, m_obj);
}
}
operator JSObjectRef() const {
return m_obj;
}
operator Value() const;
bool isFunction() const {
return JSObjectIsFunction(m_context, m_obj);
}
Value callAsFunction(int nArgs, JSValueRef args[]);
Value callAsFunction();
Value getProperty(const String& propName) const;
Value getProperty(const char *propName) const;
Value getPropertyAtIndex(unsigned index) const;
void setProperty(const String& propName, const Value& value) const;
void setProperty(const char *propName, const Value& value) const;
std::vector<std::string> getPropertyNames() const;
std::unordered_map<std::string, std::string> toJSONMap() const;
void makeProtected() {
if (!m_isProtected && m_obj) {
JSValueProtect(m_context, m_obj);
m_isProtected = true;
}
}
static Object getGlobalObject(JSContextRef ctx) {
auto globalObj = JSContextGetGlobalObject(ctx);
return Object(ctx, globalObj);
}
/**
* Creates an instance of the default object class.
*/
static Object create(JSContextRef ctx);
private:
JSContextRef m_context;
JSObjectRef m_obj;
bool m_isProtected = false;
};
class Value : public noncopyable {
public:
Value(JSContextRef context, JSValueRef value);
Value(Value&&);
operator JSValueRef() const {
return m_value;
}
bool isBoolean() const {
return JSValueIsBoolean(context(), m_value);
}
bool asBoolean() const {
return JSValueToBoolean(context(), m_value);
}
bool isNumber() const {
return JSValueIsNumber(context(), m_value);
}
bool isNull() const {
return JSValueIsNull(context(), m_value);
}
bool isUndefined() const {
return JSValueIsUndefined(context(), m_value);
}
double asNumber() const {
if (isNumber()) {
return JSValueToNumber(context(), m_value, nullptr);
} else {
return 0.0f;
}
}
int32_t asInteger() const {
return static_cast<int32_t>(asNumber());
}
uint32_t asUnsignedInteger() const {
return static_cast<uint32_t>(asNumber());
}
bool isObject() const {
return JSValueIsObject(context(), m_value);
}
Object asObject();
bool isString() const {
return JSValueIsString(context(), m_value);
}
String toString() {
return String::adopt(JSValueToStringCopy(context(), m_value, nullptr));
}
std::string toJSONString(unsigned indent = 0) const;
static Value fromJSON(JSContextRef ctx, const String& json);
protected:
JSContextRef context() const;
JSContextRef m_context;
JSValueRef m_value;
};
} }

View File

@ -5,14 +5,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := libreactnativejni
LOCAL_SRC_FILES := \
JExecutorToken.cpp \
JMessageQueueThread.cpp \
JSCPerfLogging.cpp \
JSLoader.cpp \
JSLogging.cpp \
JniJSModulesUnbundle.cpp \
OnLoad.cpp \
ProxyExecutor.cpp \
Dummy.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../.. $(LOCAL_PATH)/..
@ -23,15 +16,9 @@ LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_LDLIBS += -landroid
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libjsc libglog_init libreactnativejnifb
LOCAL_STATIC_LIBRARIES := libreactnative
LOCAL_SHARED_LIBRARIES := libreactnativejnifb
LOCAL_STATIC_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY)
$(call import-module,react)
$(call import-module,jsc)
$(call import-module,folly)
$(call import-module,fbgloginit)
$(call import-module,fb)
$(call import-module,jsc)
$(call import-module,xreact/jni)

View File

@ -3,66 +3,20 @@ include_defs('//ReactAndroid/DEFS')
# We depend on JSC, support the same platforms
SUPPORTED_PLATFORMS = '^android-(armv7|x86)$'
DEPS = [
FBGLOGINIT_TARGET,
'//native/fb:fb',
'//native/third-party/android-ndk:android',
'//xplat/folly:molly',
]
def jni_library(**kwargs):
# TODO(cjhopman): Remove this target (or move the xreact target to this directory).
cxx_library(
name='jni',
visibility = [
'PUBLIC',
],
deps = DEPS + JSC_DEPS + [
react_native_target('jni/react:react'),
react_native_target('jni/xreact/jni:jni'),
],
**kwargs
)
jni_library(
soname = 'libreactnativejni.so',
header_namespace = 'react/jni',
supported_platforms_regex = SUPPORTED_PLATFORMS,
srcs = [
'JExecutorToken.cpp',
'JMessageQueueThread.cpp',
'JSCPerfLogging.cpp',
'JSLoader.cpp',
'JSLogging.cpp',
'JniJSModulesUnbundle.cpp',
'OnLoad.cpp',
'ProxyExecutor.cpp',
'Dummy.cpp',
],
headers = [
'JSLoader.h',
'JExecutorToken.h',
'JExecutorTokenFactory.h',
'JMessageQueueThread.h',
'JNativeRunnable.h',
'JniJSModulesUnbundle.h',
'JSCPerfLogging.h',
'JSLogging.h',
'ProxyExecutor.h',
'WebWorkers.h',
deps = [
react_native_target('jni/xreact/jni:jni'),
],
exported_headers = [
],
preprocessor_flags = [
'-DLOG_TAG="ReactNativeJNI"',
'-DWITH_FBSYSTRACE=1',
'-DWITH_FBJSCEXTENSIONS=1',
],
compiler_flags = [
'-Wall',
'-Werror',
'-fexceptions',
'-std=c++11',
'-fvisibility=hidden',
'-frtti',
visibility = [
'PUBLIC',
],
)

View File

@ -0,0 +1,7 @@
// Copyright 2004-present Facebook. All Rights Reserved.
namespace facebook {
namespace react {
void ThisWhatsLeftOfTheOldBridge() {
}
}
}

View File

@ -1,20 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JExecutorToken.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
ExecutorToken JExecutorToken::getExecutorToken(alias_ref<JExecutorToken::javaobject> jobj) {
std::lock_guard<std::mutex> guard(createTokenGuard_);
auto sharedOwner = owner_.lock();
if (!sharedOwner) {
sharedOwner = std::shared_ptr<PlatformExecutorToken>(new JExecutorTokenHolder(jobj));
owner_ = sharedOwner;
}
return ExecutorToken(sharedOwner);
}
} }

View File

@ -1,59 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <mutex>
#include <fb/fbjni.h>
#include <react/ExecutorToken.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
class JExecutorTokenHolder;
class JExecutorToken : public HybridClass<JExecutorToken> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/ExecutorToken;";
ExecutorToken getExecutorToken(alias_ref<JExecutorToken::javaobject> jobj);
private:
friend HybridBase;
friend JExecutorTokenHolder;
JExecutorToken() {}
std::weak_ptr<PlatformExecutorToken> owner_;
std::mutex createTokenGuard_;
};
/**
* Wrapper class to hold references to both the c++ and Java parts of the
* ExecutorToken object. The goal is to allow a reference to a token from either
* c++ or Java to keep both the Java object and c++ hybrid part alive. For c++
* references, we accomplish this by having JExecutorTokenHolder keep a reference
* to the Java object (which has a reference to the JExecutorToken hybrid part).
* For Java references, we allow the JExecutorTokenHolder to be deallocated if there
* are no references to it in c++ from a PlatformExecutorToken, but will dynamically
* create a new one in JExecutorToken.getExecutorToken if needed.
*/
class JExecutorTokenHolder : public PlatformExecutorToken, public noncopyable {
public:
explicit JExecutorTokenHolder(alias_ref<JExecutorToken::javaobject> jobj) :
jobj_(make_global(jobj)),
impl_(cthis(jobj)) {
}
JExecutorToken::javaobject getJobj() {
return jobj_.get();
}
private:
global_ref<JExecutorToken::javaobject> jobj_;
JExecutorToken *impl_;
};
} }

View File

@ -1,24 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <react/ExecutorTokenFactory.h>
#include "JExecutorToken.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
class JExecutorTokenFactory : public ExecutorTokenFactory {
public:
virtual ExecutorToken createExecutorToken() const override {
auto jExecutorToken = JExecutorToken::newObjectCxxArgs();
auto jExecutorTokenNativePart = cthis(jExecutorToken);
return jExecutorTokenNativePart->getExecutorToken(jExecutorToken);
}
};
} }

View File

@ -1,44 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JMessageQueueThread.h"
#include <fb/log.h>
#include <folly/Memory.h>
#include <fb/fbjni.h>
#include "JNativeRunnable.h"
namespace facebook {
namespace react {
JMessageQueueThread::JMessageQueueThread(alias_ref<JavaMessageQueueThread::javaobject> jobj) :
m_jobj(make_global(jobj)) {
}
void JMessageQueueThread::runOnQueue(std::function<void()>&& runnable) {
static auto method = JavaMessageQueueThread::javaClassStatic()->
getMethod<void(Runnable::javaobject)>("runOnQueue");
method(m_jobj, JNativeRunnable::newObjectCxxArgs(runnable).get());
}
bool JMessageQueueThread::isOnThread() {
static auto method = JavaMessageQueueThread::javaClassStatic()->
getMethod<jboolean()>("isOnThread");
return method(m_jobj);
}
void JMessageQueueThread::quitSynchronous() {
static auto method = JavaMessageQueueThread::javaClassStatic()->
getMethod<void()>("quitSynchronous");
method(m_jobj);
}
/* static */
std::unique_ptr<JMessageQueueThread> JMessageQueueThread::currentMessageQueueThread() {
static auto method = MessageQueueThreadRegistry::javaClassStatic()->
getStaticMethod<JavaMessageQueueThread::javaobject()>("myMessageQueueThread");
return folly::make_unique<JMessageQueueThread>(method(MessageQueueThreadRegistry::javaClassStatic()));
}
} }

View File

@ -1,58 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <functional>
#include <react/MessageQueueThread.h>
#include <fb/fbjni.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
class JavaMessageQueueThread : public jni::JavaClass<JavaMessageQueueThread> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/queue/MessageQueueThread;";
};
class JMessageQueueThread : public MessageQueueThread {
public:
JMessageQueueThread(alias_ref<JavaMessageQueueThread::javaobject> jobj);
/**
* Enqueues the given function to run on this MessageQueueThread.
*/
void runOnQueue(std::function<void()>&& runnable) override;
/**
* Returns whether the currently executing thread is this MessageQueueThread.
*/
bool isOnThread() override;
/**
* Synchronously quits the current MessageQueueThread. Can be called from any thread, but will
* block if not called on this MessageQueueThread.
*/
void quitSynchronous() override;
JavaMessageQueueThread::javaobject jobj() {
return m_jobj.get();
}
/**
* Returns the current MessageQueueThread that owns this thread.
*/
static std::unique_ptr<JMessageQueueThread> currentMessageQueueThread();
private:
global_ref<JavaMessageQueueThread::javaobject> m_jobj;
};
class MessageQueueThreadRegistry : public jni::JavaClass<MessageQueueThreadRegistry> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/queue/MessageQueueThreadRegistry;";
};
} }

View File

@ -1,44 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <functional>
#include <jni.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
class Runnable : public JavaClass<Runnable> {
public:
static constexpr auto kJavaDescriptor = "Ljava/lang/Runnable;";
};
/**
* The c++ interface for the Java NativeRunnable class
*/
class JNativeRunnable : public HybridClass<JNativeRunnable, Runnable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/queue/NativeRunnable;";
void run() {
m_runnable();
}
static void registerNatives() {
javaClassStatic()->registerNatives({
makeNativeMethod("run", JNativeRunnable::run),
});
}
private:
friend HybridBase;
JNativeRunnable(std::function<void()> runnable)
: m_runnable(std::move(runnable)) {}
std::function<void()> m_runnable;
};
} }

View File

@ -1,244 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCPerfLogging.h"
#include <fb/log.h>
#include <fb/fbjni.h>
#include <react/JSCHelpers.h>
using namespace facebook::jni;
struct _jqplProvider : _jobject {};
using jqplProvider = _jqplProvider*;
struct _jqpl : _jobject {};
using jqpl = _jqpl*;
namespace facebook { namespace jni {
template<>
class JObjectWrapper<jqpl> : public JObjectWrapper<jobject> {
public:
static constexpr const char* kJavaDescriptor = "Lcom/facebook/quicklog/QuickPerformanceLogger;";
using JObjectWrapper<jobject>::JObjectWrapper;
void markerStart(int markerId, int instanceKey, long timestamp) {
static auto markerStartMethod =
qplClass()->getMethod<void(int32_t, int32_t, int64_t)>("markerStart");
markerStartMethod(this_, markerId, instanceKey, timestamp);
}
void markerEnd(int markerId, int instanceKey, short actionId, long timestamp) {
static auto markerEndMethod =
qplClass()->getMethod<void(int32_t, int32_t, int16_t, int64_t)>("markerEnd");
markerEndMethod(this_, markerId, instanceKey, actionId, timestamp);
}
void markerNote(int markerId, int instanceKey, short actionId, long timestamp) {
static auto markerNoteMethod =
qplClass()->getMethod<void(int32_t, int32_t, int16_t, int64_t)>("markerNote");
markerNoteMethod(this_, markerId, instanceKey, actionId, timestamp);
}
void markerCancel(int markerId, int instanceKey) {
static auto markerCancelMethod =
qplClass()->getMethod<void(int32_t, int32_t)>("markerCancel");
markerCancelMethod(this_, markerId, instanceKey);
}
int64_t currentMonotonicTimestamp() {
static auto currentTimestampMethod =
qplClass()->getMethod<int64_t()>("currentMonotonicTimestamp");
return currentTimestampMethod(this_);
}
private:
static alias_ref<jclass> qplClass() {
static auto cls = findClassStatic("com/facebook/quicklog/QuickPerformanceLogger");
return cls;
}
};
using JQuickPerformanceLogger = JObjectWrapper<jqpl>;
template<>
class JObjectWrapper<jqplProvider> : public JObjectWrapper<jobject> {
public:
static constexpr const char* kJavaDescriptor =
"Lcom/facebook/quicklog/QuickPerformanceLoggerProvider;";
using JObjectWrapper<jobject>::JObjectWrapper;
static global_ref<jqpl> get() {
static auto getQPLInstMethod = qplProviderClass()->getStaticMethod<jqpl()>("getQPLInstance");
static global_ref<jqpl> theQpl = make_global(getQPLInstMethod(qplProviderClass().get()));
return theQpl;
}
static bool check() {
static auto getQPLInstMethod = qplProviderClass()->getStaticMethod<jqpl()>("getQPLInstance");
auto theQpl = getQPLInstMethod(qplProviderClass().get());
return (theQpl.get() != nullptr);
}
private:
static alias_ref<jclass> qplProviderClass() {
static auto cls = findClassStatic("com/facebook/quicklog/QuickPerformanceLoggerProvider");
return cls;
}
};
using JQuickPerformanceLoggerProvider = JObjectWrapper<jqplProvider>;
}}
static bool isReady() {
static bool ready = false;
if (!ready) {
try {
findClassStatic("com/facebook/quicklog/QuickPerformanceLoggerProvider");
} catch(...) {
// Swallow this exception - we don't want to crash the app, an error is enough.
FBLOGE("Calling QPL from JS before class has been loaded in Java. Ignored.");
return false;
}
if (JQuickPerformanceLoggerProvider::check()) {
ready = true;
} else {
FBLOGE("Calling QPL from JS before it has been initialized in Java. Ignored.");
return false;
}
}
return ready;
}
// After having read the implementation of PNaN that is returned from JSValueToNumber, and some
// more material on how NaNs are constructed, I think this is the most consistent way to verify
// NaN with how we generate it.
// Once the integration completes, I'll play around with it some more and potentially change this
// implementation to use std::isnan() if it is exactly commensurate with our usage.
static bool isNan(double value) {
return (value != value);
}
// Safely translates JSValues to an array of doubles.
static bool grabDoubles(
size_t targetsCount,
double targets[],
JSContextRef ctx,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (argumentCount < targetsCount) {
return false;
}
for (size_t i = 0 ; i < targetsCount ; i++) {
targets[i] = JSValueToNumber(ctx, arguments[i], exception);
if (isNan(targets[i])) {
return false;
}
}
return true;
}
static JSValueRef nativeQPLMarkerStart(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
double targets[3];
if (isReady() && grabDoubles(3, targets, ctx, argumentCount, arguments, exception)) {
int32_t markerId = (int32_t) targets[0];
int32_t instanceKey = (int32_t) targets[1];
int64_t timestamp = (int64_t) targets[2];
JQuickPerformanceLoggerProvider::get()->markerStart(markerId, instanceKey, timestamp);
}
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeQPLMarkerEnd(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
double targets[4];
if (isReady() && grabDoubles(4, targets, ctx, argumentCount, arguments, exception)) {
int32_t markerId = (int32_t) targets[0];
int32_t instanceKey = (int32_t) targets[1];
int16_t actionId = (int16_t) targets[2];
int64_t timestamp = (int64_t) targets[3];
JQuickPerformanceLoggerProvider::get()->markerEnd(markerId, instanceKey, actionId, timestamp);
}
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeQPLMarkerNote(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
double targets[4];
if (isReady() && grabDoubles(4, targets, ctx, argumentCount, arguments, exception)) {
int32_t markerId = (int32_t) targets[0];
int32_t instanceKey = (int32_t) targets[1];
int16_t actionId = (int16_t) targets[2];
int64_t timestamp = (int64_t) targets[3];
JQuickPerformanceLoggerProvider::get()->markerNote(markerId, instanceKey, actionId, timestamp);
}
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeQPLMarkerCancel(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
double targets[2];
if (isReady() && grabDoubles(2, targets, ctx, argumentCount, arguments, exception)) {
int32_t markerId = (int32_t) targets[0];
int32_t instanceKey = (int32_t) targets[1];
JQuickPerformanceLoggerProvider::get()->markerCancel(markerId, instanceKey);
}
return JSValueMakeUndefined(ctx);
}
static JSValueRef nativeQPLTimestamp(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (!isReady()) {
return JSValueMakeNumber(ctx, 0);
}
int64_t timestamp = JQuickPerformanceLoggerProvider::get()->currentMonotonicTimestamp();
// Since this is monotonic time, I assume the 52 bits of mantissa are enough in the double value.
return JSValueMakeNumber(ctx, timestamp);
}
namespace facebook {
namespace react {
void addNativePerfLoggingHooks(JSGlobalContextRef ctx) {
installGlobalFunction(ctx, "nativeQPLMarkerStart", nativeQPLMarkerStart);
installGlobalFunction(ctx, "nativeQPLMarkerEnd", nativeQPLMarkerEnd);
installGlobalFunction(ctx, "nativeQPLMarkerNote", nativeQPLMarkerNote);
installGlobalFunction(ctx, "nativeQPLMarkerCancel", nativeQPLMarkerCancel);
installGlobalFunction(ctx, "nativeQPLTimestamp", nativeQPLTimestamp);
}
} }

View File

@ -1,11 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
void addNativePerfLoggingHooks(JSGlobalContextRef ctx);
} }

View File

@ -1,93 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSLoader.h"
#include <android/asset_manager_jni.h>
#include <fb/Environment.h>
#include <fstream>
#include <sstream>
#include <streambuf>
#include <string>
#include <fb/log.h>
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceSection;
#endif
namespace facebook {
namespace react {
static jclass gApplicationHolderClass;
static jmethodID gGetApplicationMethod;
static jmethodID gGetAssetManagerMethod;
std::string loadScriptFromAssets(const std::string& assetName) {
JNIEnv *env = jni::Environment::current();
jobject application = env->CallStaticObjectMethod(
gApplicationHolderClass,
gGetApplicationMethod);
jobject assetManager = env->CallObjectMethod(application, gGetAssetManagerMethod);
return loadScriptFromAssets(AAssetManager_fromJava(env, assetManager), assetName);
}
std::string loadScriptFromAssets(
AAssetManager *manager,
const std::string& assetName) {
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_loadScriptFromAssets",
"assetName", assetName);
#endif
if (manager) {
auto asset = AAssetManager_open(
manager,
assetName.c_str(),
AASSET_MODE_STREAMING); // Optimized for sequential read: see AssetManager.java for docs
if (asset) {
std::stringbuf buf;
char BUF[0x800];
int readbytes;
while ((readbytes = AAsset_read(asset, BUF, sizeof(BUF))) > 0) {
buf.sputn(BUF, readbytes);
}
AAsset_close(asset);
if (readbytes == 0) { // EOF!
return buf.str();
}
}
}
FBLOGE("Unable to load script from assets: %s", assetName.c_str());
return "";
}
std::string loadScriptFromFile(const std::string& fileName) {
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_loadScriptFromFile",
"fileName", fileName);
#endif
std::ifstream jsfile(fileName);
if (jsfile) {
std::string output;
jsfile.seekg(0, std::ios::end);
output.reserve(jsfile.tellg());
jsfile.seekg(0, std::ios::beg);
output.assign(
(std::istreambuf_iterator<char>(jsfile)),
std::istreambuf_iterator<char>());
return output;
}
FBLOGE("Unable to load script from file: %s", fileName.c_str());
return "";
}
void registerJSLoaderNatives() {
JNIEnv *env = jni::Environment::current();
jclass applicationHolderClass = env->FindClass("com/facebook/react/common/ApplicationHolder");
gApplicationHolderClass = (jclass)env->NewGlobalRef(applicationHolderClass);
gGetApplicationMethod = env->GetStaticMethodID(applicationHolderClass, "getApplication", "()Landroid/app/Application;");
jclass appClass = env->FindClass("android/app/Application");
gGetAssetManagerMethod = env->GetMethodID(appClass, "getAssets", "()Landroid/content/res/AssetManager;");
}
} }

View File

@ -1,30 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <android/asset_manager.h>
#include <string>
#include <jni.h>
namespace facebook {
namespace react {
/**
* Helper method for loading a JS script from Android assets without
* a reference to an AssetManager.
*/
std::string loadScriptFromAssets(const std::string& assetName);
/**
* Helper method for loading JS script from android asset
*/
std::string loadScriptFromAssets(AAssetManager *assetManager, const std::string& assetName);
/**
* Helper method for loading JS script from a file
*/
std::string loadScriptFromFile(const std::string& fileName);
void registerJSLoaderNatives();
} }

View File

@ -1,36 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSLogging.h"
#include <android/log.h>
#include <algorithm>
#include <react/Value.h>
#include <fb/log.h>
namespace facebook {
namespace react {
JSValueRef nativeLoggingHook(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[], JSValueRef *exception) {
android_LogPriority logLevel = ANDROID_LOG_DEBUG;
if (argumentCount > 1) {
int level = (int) JSValueToNumber(ctx, arguments[1], NULL);
// The lowest log level we get from JS is 0. We shift and cap it to be
// in the range the Android logging method expects.
logLevel = std::min(
static_cast<android_LogPriority>(level + ANDROID_LOG_DEBUG),
ANDROID_LOG_FATAL);
}
if (argumentCount > 0) {
JSStringRef jsString = JSValueToStringCopy(ctx, arguments[0], NULL);
String message = String::adopt(jsString);
FBLOG_PRI(logLevel, "ReactNativeJS", "%s", message.str().c_str());
}
return JSValueMakeUndefined(ctx);
}
}};

View File

@ -1,15 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <JavaScriptCore/JSContextRef.h>
namespace facebook {
namespace react {
JSValueRef nativeLoggingHook(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[], JSValueRef *exception);
}}

View File

@ -1,83 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JniJSModulesUnbundle.h"
#include <cstdint>
#include <fb/assert.h>
#include <libgen.h>
#include <memory>
#include <sstream>
#include <sys/endian.h>
#include <utility>
using magic_number_t = uint32_t;
const magic_number_t MAGIC_FILE_HEADER = 0xFB0BD1E5;
const std::string MAGIC_FILE_NAME = "UNBUNDLE";
namespace facebook {
namespace react {
using asset_ptr =
std::unique_ptr<AAsset, std::function<decltype(AAsset_close)>>;
static std::string jsModulesDir(const std::string& entryFile) {
std::string dir = dirname(entryFile.c_str());
// android's asset manager does not work with paths that start with a dot
return dir == "." ? "js-modules/" : dir + "/js-modules/";
}
static asset_ptr openAsset(
AAssetManager *manager,
const std::string& fileName,
int mode = AASSET_MODE_STREAMING) {
return asset_ptr(
AAssetManager_open(manager, fileName.c_str(), mode),
AAsset_close);
}
JniJSModulesUnbundle::JniJSModulesUnbundle(AAssetManager *assetManager, const std::string& entryFile) :
m_assetManager(assetManager),
m_moduleDirectory(jsModulesDir(entryFile)) {}
bool JniJSModulesUnbundle::isUnbundle(
AAssetManager *assetManager,
const std::string& assetName) {
if (!assetManager) {
return false;
}
auto magicFileName = jsModulesDir(assetName) + MAGIC_FILE_NAME;
auto asset = openAsset(assetManager, magicFileName.c_str());
if (asset == nullptr) {
return false;
}
magic_number_t fileHeader = 0;
AAsset_read(asset.get(), &fileHeader, sizeof(fileHeader));
return fileHeader == htole32(MAGIC_FILE_HEADER);
}
JSModulesUnbundle::Module JniJSModulesUnbundle::getModule(uint32_t moduleId) const {
// can be nullptr for default constructor.
FBASSERTMSGF(m_assetManager != nullptr, "Unbundle has not been initialized with an asset manager");
std::ostringstream sourceUrlBuilder;
sourceUrlBuilder << moduleId << ".js";
auto sourceUrl = sourceUrlBuilder.str();
auto fileName = m_moduleDirectory + sourceUrl;
auto asset = openAsset(m_assetManager, fileName, AASSET_MODE_BUFFER);
const char *buffer = nullptr;
if (asset != nullptr) {
buffer = static_cast<const char *>(AAsset_getBuffer(asset.get()));
}
if (buffer == nullptr) {
throw ModuleNotFound("Module not found: " + sourceUrl);
}
return {sourceUrl, std::string(buffer, AAsset_getLength(asset.get()))};
}
}
}

View File

@ -1,31 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <android/asset_manager.h>
#include <react/JSModulesUnbundle.h>
namespace facebook {
namespace react {
class JniJSModulesUnbundle : public JSModulesUnbundle {
/**
* This implementation reads modules as single file from the assets of an apk.
*/
public:
JniJSModulesUnbundle() = default;
JniJSModulesUnbundle(AAssetManager *assetManager, const std::string& entryFile);
JniJSModulesUnbundle(JniJSModulesUnbundle&& other) = delete;
JniJSModulesUnbundle& operator= (JSModulesUnbundle&& other) = delete;
static bool isUnbundle(
AAssetManager *assetManager,
const std::string& assetName);
virtual Module getModule(uint32_t moduleId) const override;
private:
AAssetManager *m_assetManager = nullptr;
std::string m_moduleDirectory;
};
}
}

View File

@ -1,516 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <android/asset_manager_jni.h>
#include <android/input.h>
#include <fb/log.h>
#include <fb/glog_init.h>
#include <folly/json.h>
#include <jni/Countable.h>
#include <fb/Environment.h>
#include <fb/fbjni.h>
#include <jni/LocalReference.h>
#include <jni/LocalString.h>
#include <jni/WeakReference.h>
#include <fb/fbjni/Exceptions.h>
#include <react/Bridge.h>
#include <react/Executor.h>
#include <react/JSCExecutor.h>
#include <react/JSModulesUnbundle.h>
#include <react/MethodCall.h>
#include <react/Platform.h>
#include "JExecutorToken.h"
#include "JExecutorTokenFactory.h"
#include "JNativeRunnable.h"
#include "JSLoader.h"
#include "ProxyExecutor.h"
#include "OnLoad.h"
#include "JMessageQueueThread.h"
#include "JniJSModulesUnbundle.h"
#include "JSLogging.h"
#include "JSCPerfLogging.h"
#include "WebWorkers.h"
#include <xreact/jni/ReadableNativeArray.h>
#include <xreact/jni/WritableNativeMap.h>
#include <algorithm>
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceSection;
#endif
using namespace facebook::jni;
namespace facebook {
namespace react {
namespace {
namespace runnable {
struct NativeRunnable : public Countable {
std::function<void()> callable;
};
static jclass gNativeRunnableClass;
static jmethodID gNativeRunnableCtor;
static LocalReference<jobject> createNativeRunnable(JNIEnv* env, decltype(NativeRunnable::callable)&& callable) {
LocalReference<jobject> jRunnable{env->NewObject(gNativeRunnableClass, gNativeRunnableCtor)};
if (env->ExceptionCheck()) {
return nullptr;
}
auto nativeRunnable = createNew<NativeRunnable>();
nativeRunnable->callable = std::move(callable);
setCountableForJava(env, jRunnable.get(), std::move(nativeRunnable));
return jRunnable;
}
static void run(JNIEnv* env, jobject jNativeRunnable) {
auto nativeRunnable = extractRefPtr<NativeRunnable>(env, jNativeRunnable);
nativeRunnable->callable();
}
} // namespace runnable
namespace queue {
static jmethodID gRunOnQueueThreadMethod;
static void enqueueNativeRunnableOnQueue(JNIEnv* env, jobject callbackQueueThread, jobject nativeRunnable) {
env->CallVoidMethod(callbackQueueThread, gRunOnQueueThreadMethod, nativeRunnable);
}
} // namespace queue
namespace bridge {
static jmethodID gCallbackMethod;
static jmethodID gOnBatchCompleteMethod;
static jmethodID gOnExecutorUnregisteredMethod;
static jmethodID gLogMarkerMethod;
struct CountableBridge : Bridge, Countable {
using Bridge::Bridge;
};
static void logMarker(const std::string& marker) {
JNIEnv* env = Environment::current();
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
jstring jmarker = env->NewStringUTF(marker.c_str());
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, jmarker);
env->DeleteLocalRef(markerClass);
env->DeleteLocalRef(jmarker);
}
static void makeJavaCall(JNIEnv* env, ExecutorToken executorToken, jobject callback, const MethodCall& call) {
if (call.arguments.isNull()) {
return;
}
#ifdef WITH_FBSYSTRACE
if (call.callId != -1) {
fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", call.callId);
}
#endif
auto newArray = ReadableNativeArray::newObjectCxxArgs(std::move(call.arguments));
env->CallVoidMethod(
callback,
gCallbackMethod,
static_cast<JExecutorTokenHolder*>(executorToken.getPlatformExecutorToken().get())->getJobj(),
call.moduleId,
call.methodId,
newArray.get());
}
static void signalBatchComplete(JNIEnv* env, jobject callback) {
env->CallVoidMethod(callback, gOnBatchCompleteMethod);
}
class PlatformBridgeCallback : public BridgeCallback {
public:
PlatformBridgeCallback(
RefPtr<WeakReference> weakCallback_,
RefPtr<WeakReference> weakCallbackQueueThread_) :
weakCallback_(std::move(weakCallback_)),
weakCallbackQueueThread_(std::move(weakCallbackQueueThread_)) {}
void executeCallbackOnCallbackQueueThread(std::function<void(ResolvedWeakReference&)>&& runnable) {
auto env = Environment::current();
if (env->ExceptionCheck()) {
FBLOGW("Dropped callback because of pending exception");
return;
}
ResolvedWeakReference callbackQueueThread(weakCallbackQueueThread_);
if (!callbackQueueThread) {
FBLOGW("Dropped callback because callback queue thread went away");
return;
}
auto runnableWrapper = std::bind([weakCallback=weakCallback_] (std::function<void(ResolvedWeakReference&)>& runnable) {
auto env = Environment::current();
if (env->ExceptionCheck()) {
FBLOGW("Dropped calls because of pending exception");
return;
}
ResolvedWeakReference callback(weakCallback);
if (callback) {
runnable(callback);
}
}, std::move(runnable));
auto jNativeRunnable = runnable::createNativeRunnable(env, std::move(runnableWrapper));
queue::enqueueNativeRunnableOnQueue(env, callbackQueueThread, jNativeRunnable.get());
}
virtual void onCallNativeModules(
ExecutorToken executorToken,
const std::string& callJSON,
bool isEndOfBatch) override {
executeCallbackOnCallbackQueueThread([executorToken, callJSON, isEndOfBatch] (ResolvedWeakReference& callback) {
JNIEnv* env = Environment::current();
for (auto& call : react::parseMethodCalls(callJSON)) {
makeJavaCall(env, executorToken, callback, call);
if (env->ExceptionCheck()) {
return;
}
}
if (isEndOfBatch) {
signalBatchComplete(env, callback);
}
});
}
virtual void onExecutorUnregistered(ExecutorToken executorToken) override {
executeCallbackOnCallbackQueueThread([executorToken] (ResolvedWeakReference& callback) {
JNIEnv *env = Environment::current();
env->CallVoidMethod(
callback,
gOnExecutorUnregisteredMethod,
static_cast<JExecutorTokenHolder*>(executorToken.getPlatformExecutorToken().get())->getJobj());
});
}
private:
RefPtr<WeakReference> weakCallback_;
RefPtr<WeakReference> weakCallbackQueueThread_;
};
static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback,
jobject callbackQueueThread) {
auto weakCallback = createNew<WeakReference>(callback);
auto weakCallbackQueueThread = createNew<WeakReference>(callbackQueueThread);
auto bridgeCallback = folly::make_unique<PlatformBridgeCallback>(weakCallback, weakCallbackQueueThread);
auto nativeExecutorFactory = extractRefPtr<CountableJSExecutorFactory>(env, executor);
auto executorTokenFactory = folly::make_unique<JExecutorTokenFactory>();
auto bridge = createNew<CountableBridge>(nativeExecutorFactory.get(), std::move(executorTokenFactory), std::move(bridgeCallback));
setCountableForJava(env, obj, std::move(bridge));
}
static void destroy(JNIEnv* env, jobject jbridge) {
auto bridge = extractRefPtr<CountableBridge>(env, jbridge);
try {
bridge->destroy();
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void loadApplicationScript(
const RefPtr<CountableBridge>& bridge,
const std::string& script,
const std::string& sourceUri) {
try {
bridge->loadApplicationScript(script, sourceUri);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void loadApplicationUnbundle(
const RefPtr<CountableBridge>& bridge,
AAssetManager *assetManager,
const std::string& startupCode,
const std::string& startupFileName) {
try {
bridge->loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle>(
new JniJSModulesUnbundle(assetManager, startupFileName)),
startupCode,
startupFileName);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager,
jstring assetName) {
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
auto manager = AAssetManager_fromJava(env, assetManager);
auto bridge = extractRefPtr<CountableBridge>(env, obj);
auto assetNameStr = fromJString(env, assetName);
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_start"));
auto script = react::loadScriptFromAssets(manager, assetNameStr);
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_"
"loadApplicationScript",
"assetName", assetNameStr);
#endif
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_read"));
if (JniJSModulesUnbundle::isUnbundle(manager, assetNameStr)) {
loadApplicationUnbundle(bridge, manager, script, assetNameStr);
} else {
loadApplicationScript(bridge, script, "file://" + assetNameStr);
}
if (env->ExceptionCheck()) {
return;
}
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_done"));
}
static void loadScriptFromFile(JNIEnv* env, jobject obj, jstring fileName, jstring sourceURL) {
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
auto bridge = extractRefPtr<CountableBridge>(env, obj);
auto fileNameStr = fileName == NULL ? "" : fromJString(env, fileName);
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_start"));
auto script = fileName == NULL ? "" : react::loadScriptFromFile(fileNameStr);
#ifdef WITH_FBSYSTRACE
auto sourceURLStr = sourceURL == NULL ? fileNameStr : fromJString(env, sourceURL);
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_"
"loadApplicationScript",
"sourceURL", sourceURLStr);
#endif
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_read"));
loadApplicationScript(bridge, script, fromJString(env, sourceURL));
if (env->ExceptionCheck()) {
return;
}
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_exec"));
}
static void callFunction(JNIEnv* env, jobject obj, JExecutorToken::jhybridobject jExecutorToken, jstring module, jstring method,
NativeArray::jhybridobject args) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
auto arguments = cthis(wrap_alias(args));
try {
bridge->callFunction(
cthis(wrap_alias(jExecutorToken))->getExecutorToken(wrap_alias(jExecutorToken)),
fromJString(env, module),
fromJString(env, method),
std::move(arguments->array)
);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void invokeCallback(JNIEnv* env, jobject obj, JExecutorToken::jhybridobject jExecutorToken, jint callbackId,
NativeArray::jhybridobject args) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
auto arguments = cthis(wrap_alias(args));
try {
bridge->invokeCallback(
cthis(wrap_alias(jExecutorToken))->getExecutorToken(wrap_alias(jExecutorToken)),
(double) callbackId,
std::move(arguments->array)
);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstring jsonValue) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
}
static jlong getJavaScriptContext(JNIEnv *env, jobject obj) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
return (uintptr_t) bridge->getJavaScriptContext();
}
static jobject getMainExecutorToken(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
auto token = bridge->getMainExecutorToken();
return static_cast<JExecutorTokenHolder*>(token.getPlatformExecutorToken().get())->getJobj();
}
static jboolean supportsProfiling(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
return bridge->supportsProfiling() ? JNI_TRUE : JNI_FALSE;
}
static void startProfiler(JNIEnv* env, jobject obj, jstring title) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
bridge->startProfiler(fromJString(env, title));
}
static void stopProfiler(JNIEnv* env, jobject obj, jstring title, jstring filename) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
bridge->stopProfiler(fromJString(env, title), fromJString(env, filename));
}
static void handleMemoryPressureUiHidden(JNIEnv* env, jobject obj) {
LOG(WARNING) << "handleMemoryPressureUiHidden";
auto bridge = extractRefPtr<CountableBridge>(env, obj);
bridge->handleMemoryPressureUiHidden();
}
static void handleMemoryPressureModerate(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
bridge->handleMemoryPressureModerate();
}
static void handleMemoryPressureCritical(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<CountableBridge>(env, obj);
bridge->handleMemoryPressureCritical();
}
} // namespace bridge
namespace executors {
static std::string getApplicationDir(const char* methodName) {
// Get the Application Context object
auto getApplicationClass = findClassLocal(
"com/facebook/react/common/ApplicationHolder");
auto getApplicationMethod = getApplicationClass->getStaticMethod<jobject()>(
"getApplication",
"()Landroid/app/Application;"
);
auto application = getApplicationMethod(getApplicationClass);
// Get getCacheDir() from the context
auto getDirMethod = findClassLocal("android/app/Application")
->getMethod<jobject()>(methodName,
"()Ljava/io/File;"
);
auto dirObj = getDirMethod(application);
// Call getAbsolutePath() on the returned File object
auto getAbsolutePathMethod = findClassLocal("java/io/File")
->getMethod<jstring()>("getAbsolutePath");
return getAbsolutePathMethod(dirObj)->toStdString();
}
static std::string getApplicationCacheDir() {
return getApplicationDir("getCacheDir");
}
static std::string getApplicationPersistentDir() {
return getApplicationDir("getFilesDir");
}
struct CountableJSCExecutorFactory : CountableJSExecutorFactory {
public:
CountableJSCExecutorFactory(folly::dynamic jscConfig) : m_jscConfig(jscConfig) {}
virtual std::unique_ptr<JSExecutor> createJSExecutor(Bridge *bridge) override {
m_jscConfig["PersistentDirectory"] = getApplicationPersistentDir();
return JSCExecutorFactory(getApplicationCacheDir(), m_jscConfig).createJSExecutor(bridge);
}
private:
folly::dynamic m_jscConfig;
};
static void createJSCExecutor(alias_ref<jobject> obj, WritableNativeMap* jscConfig) {
auto executor = createNew<CountableJSCExecutorFactory>(jscConfig->consume());
setCountableForJava(Environment::current(), obj.get(), std::move(executor));
}
static void createProxyExecutor(JNIEnv *env, jobject obj, jobject executorInstance) {
auto executor =
createNew<ProxyExecutorOneTimeFactory>(make_global(adopt_local(executorInstance)));
setCountableForJava(env, obj, std::move(executor));
}
} // namespace executors
}
jmethodID getLogMarkerMethod() {
return bridge::gLogMarkerMethod;
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return initialize(vm, [] {
facebook::gloginit::initialize();
// Inject some behavior into react/
ReactMarker::logMarker = bridge::logMarker;
WebWorkerUtil::createWebWorkerThread = WebWorkers::createWebWorkerThread;
WebWorkerUtil::loadScriptFromAssets =
[] (const std::string& assetName) {
return loadScriptFromAssets(assetName);
};
WebWorkerUtil::loadScriptFromNetworkSync = WebWorkers::loadScriptFromNetworkSync;
MessageQueues::getCurrentMessageQueueThread =
[] {
return std::unique_ptr<MessageQueueThread>(
JMessageQueueThread::currentMessageQueueThread().release());
};
PerfLogging::installNativeHooks = addNativePerfLoggingHooks;
JSLogging::nativeHook = nativeLoggingHook;
JNativeRunnable::registerNatives();
registerJSLoaderNatives();
registerNatives("com/facebook/react/bridge/JSCJavaScriptExecutor", {
makeNativeMethod("initialize", executors::createJSCExecutor),
});
registerNatives("com/facebook/react/bridge/ProxyJavaScriptExecutor", {
makeNativeMethod(
"initialize", "(Lcom/facebook/react/bridge/JavaJSExecutor;)V",
executors::createProxyExecutor),
});
// get the current env
JNIEnv* env = Environment::current();
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(Lcom/facebook/react/bridge/ExecutorToken;IILcom/facebook/react/bridge/ReadableNativeArray;)V");
bridge::gOnBatchCompleteMethod = env->GetMethodID(callbackClass, "onBatchComplete", "()V");
bridge::gOnExecutorUnregisteredMethod = env->GetMethodID(callbackClass, "onExecutorUnregistered", "(Lcom/facebook/react/bridge/ExecutorToken;)V");
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
bridge::gLogMarkerMethod = env->GetStaticMethodID(markerClass, "logMarker", "(Ljava/lang/String;)V");
registerNatives("com/facebook/react/bridge/ReactBridge", {
makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
makeNativeMethod("destroy", bridge::destroy),
makeNativeMethod(
"loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
bridge::loadScriptFromAssets),
makeNativeMethod("loadScriptFromFile", bridge::loadScriptFromFile),
makeNativeMethod("callFunction", bridge::callFunction),
makeNativeMethod("invokeCallback", bridge::invokeCallback),
makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
makeNativeMethod("getMainExecutorToken", "()Lcom/facebook/react/bridge/ExecutorToken;", bridge::getMainExecutorToken),
makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
makeNativeMethod("startProfiler", bridge::startProfiler),
makeNativeMethod("stopProfiler", bridge::stopProfiler),
makeNativeMethod("handleMemoryPressureUiHidden", bridge::handleMemoryPressureUiHidden),
makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),
makeNativeMethod("getJavaScriptContextNativePtrExperimental", bridge::getJavaScriptContext),
});
jclass nativeRunnableClass = env->FindClass("com/facebook/react/bridge/queue/NativeRunnableDeprecated");
runnable::gNativeRunnableClass = (jclass)env->NewGlobalRef(nativeRunnableClass);
runnable::gNativeRunnableCtor = env->GetMethodID(nativeRunnableClass, "<init>", "()V");
wrap_alias(nativeRunnableClass)->registerNatives({
makeNativeMethod("run", runnable::run),
});
jclass messageQueueThreadClass =
env->FindClass("com/facebook/react/bridge/queue/MessageQueueThread");
queue::gRunOnQueueThreadMethod =
env->GetMethodID(messageQueueThreadClass, "runOnQueue", "(Ljava/lang/Runnable;)V");
});
}
} }

View File

@ -1,19 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jni.h>
#include <jni/Countable.h>
#include <react/Executor.h>
namespace facebook {
namespace react {
jmethodID getLogMarkerMethod();
struct CountableJSExecutorFactory : JSExecutorFactory, Countable {
using JSExecutorFactory::JSExecutorFactory;
};
} // namespace react
} // namespace facebook

View File

@ -1,91 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "ProxyExecutor.h"
#include <fb/assert.h>
#include <fb/Environment.h>
#include <jni/LocalReference.h>
#include <jni/LocalString.h>
#include <folly/json.h>
#include <react/Bridge.h>
namespace facebook {
namespace react {
const auto EXECUTOR_BASECLASS = "com/facebook/react/bridge/JavaJSExecutor";
static std::string executeJSCallWithProxy(
jobject executor,
const std::string& methodName,
const std::vector<folly::dynamic>& arguments) {
static auto executeJSCall =
jni::findClassStatic(EXECUTOR_BASECLASS)->getMethod<jstring(jstring, jstring)>("executeJSCall");
auto result = executeJSCall(
executor,
jni::make_jstring(methodName).get(),
jni::make_jstring(folly::toJson(arguments).c_str()).get());
return result->toString();
}
std::unique_ptr<JSExecutor> ProxyExecutorOneTimeFactory::createJSExecutor(Bridge *bridge) {
FBASSERTMSGF(
m_executor.get() != nullptr,
"Proxy instance should not be null. Did you attempt to call createJSExecutor() on this factory "
"instance more than once?");
return std::unique_ptr<JSExecutor>(new ProxyExecutor(std::move(m_executor), bridge));
}
ProxyExecutor::~ProxyExecutor() {
m_executor.reset();
}
void ProxyExecutor::loadApplicationScript(
const std::string&,
const std::string& sourceURL) {
static auto loadApplicationScript =
jni::findClassStatic(EXECUTOR_BASECLASS)->getMethod<void(jstring)>("loadApplicationScript");
// The proxy ignores the script data passed in.
loadApplicationScript(
m_executor.get(),
jni::make_jstring(sourceURL).get());
}
void ProxyExecutor::loadApplicationUnbundle(std::unique_ptr<JSModulesUnbundle>, const std::string&, const std::string&) {
jni::throwNewJavaException(
"java/lang/UnsupportedOperationException",
"Loading application unbundles is not supported for proxy executors");
}
void ProxyExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
std::vector<folly::dynamic> call{
moduleId,
methodId,
std::move(arguments),
};
std::string result = executeJSCallWithProxy(m_executor.get(), "callFunctionReturnFlushedQueue", std::move(call));
m_bridge->callNativeModules(*this, result, true);
}
void ProxyExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
std::vector<folly::dynamic> call{
(double) callbackId,
std::move(arguments)
};
std::string result = executeJSCallWithProxy(m_executor.get(), "invokeCallbackAndReturnFlushedQueue", std::move(call));
m_bridge->callNativeModules(*this, result, true);
}
void ProxyExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
static auto setGlobalVariable =
jni::findClassStatic(EXECUTOR_BASECLASS)->getMethod<void(jstring, jstring)>("setGlobalVariable");
setGlobalVariable(
m_executor.get(),
jni::make_jstring(propName).get(),
jni::make_jstring(jsonValue).get());
}
} }

View File

@ -1,57 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <react/Executor.h>
#include <fb/fbjni.h>
#include <jni.h>
#include <jni/GlobalReference.h>
#include "OnLoad.h"
namespace facebook {
namespace react {
/**
* This executor factory can only create a single executor instance because it moves
* executorInstance global reference to the executor instance it creates.
*/
class ProxyExecutorOneTimeFactory : public CountableJSExecutorFactory {
public:
ProxyExecutorOneTimeFactory(jni::global_ref<jobject>&& executorInstance) :
m_executor(std::move(executorInstance)) {}
virtual std::unique_ptr<JSExecutor> createJSExecutor(Bridge *bridge) override;
private:
jni::global_ref<jobject> m_executor;
};
class ProxyExecutor : public JSExecutor {
public:
ProxyExecutor(jni::global_ref<jobject>&& executorInstance, Bridge *bridge) :
m_executor(std::move(executorInstance)),
m_bridge(bridge) {}
virtual ~ProxyExecutor() override;
virtual void loadApplicationScript(
const std::string& script,
const std::string& sourceURL) override;
virtual void loadApplicationUnbundle(
std::unique_ptr<JSModulesUnbundle> bundle,
const std::string& startupCode,
const std::string& sourceURL) override;
virtual void callFunction(
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) override;
virtual void invokeCallback(
const double callbackId,
const folly::dynamic& arguments) override;
virtual void setGlobalVariable(
const std::string& propName,
const std::string& jsonValue) override;
private:
jni::global_ref<jobject> m_executor;
Bridge *m_bridge;
};
} }

View File

@ -1,50 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fstream>
#include <memory>
#include <string>
#include <sstream>
#include <jni.h>
#include <folly/Memory.h>
#include "JMessageQueueThread.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
class WebWorkers : public JavaClass<WebWorkers> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/webworkers/WebWorkers;";
static std::unique_ptr<JMessageQueueThread> createWebWorkerThread(int id, MessageQueueThread *ownerMessageQueueThread) {
static auto method = WebWorkers::javaClassStatic()->
getStaticMethod<JavaMessageQueueThread::javaobject(jint, JavaMessageQueueThread::javaobject)>("createWebWorkerThread");
auto res = method(WebWorkers::javaClassStatic(), id, static_cast<JMessageQueueThread*>(ownerMessageQueueThread)->jobj());
return folly::make_unique<JMessageQueueThread>(res);
}
static std::string loadScriptFromNetworkSync(const std::string& url, const std::string& tempfileName) {
static auto method = WebWorkers::javaClassStatic()->
getStaticMethod<void(jstring, jstring)>("downloadScriptToFileSync");
method(
WebWorkers::javaClassStatic(),
jni::make_jstring(url).get(),
jni::make_jstring(tempfileName).get());
std::ifstream tempFile(tempfileName);
if (!tempFile.good()) {
throw std::runtime_error("Didn't find worker script file at " + tempfileName);
}
std::stringstream buffer;
buffer << tempFile.rdbuf();
std::remove(tempfileName.c_str());
return buffer.str();
}
};
} }

View File

@ -1,12 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
namespace facebook {
namespace react {
struct noncopyable {
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
};
}}

View File

@ -1,21 +0,0 @@
include_defs('//ReactAndroid/DEFS')
cxx_library(
name = 'perftests',
srcs = [ 'OnLoad.cpp' ],
soname = 'libreactnativetests.so',
preprocessor_flags = [
'-DLOG_TAG=\"ReactPerftests\"',
],
deps = [
'//native:base',
'//native/fb:fb',
],
visibility = [
'//instrumentation_tests/com/facebook/react/...',
],
)
project_config(
src_target = ':perftests',
)

View File

@ -1,65 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <fb/log.h>
#include <fb/Environment.h>
#include <jni/LocalString.h>
#include <jni/Registration.h>
namespace facebook {
namespace react {
namespace logwatcher {
static std::string gMessageToLookFor;
static int gMessagePriorityToLookFor;
static bool gHasSeenMessage = false;
/**
* NB: Don't put JNI logic (or anything else that could trigger a log) here!
*/
static void stubLogHandler(int pri, const char *tag, const char *msg) {
if (gMessageToLookFor.empty()) {
return;
}
bool priorityMatches = pri == gMessagePriorityToLookFor;
bool substringFound = strstr(msg, gMessageToLookFor.c_str()) != NULL;
gHasSeenMessage |= priorityMatches && substringFound;
}
static jboolean hasSeenExpectedLogMessage() {
return gHasSeenMessage ? JNI_TRUE : JNI_FALSE;
}
static void stopWatchingLogMessages(JNIEnv* env, jclass loggerClass) {
gMessageToLookFor = "";
gHasSeenMessage = false;
setLogHandler(NULL);
}
static void startWatchingForLogMessage(JNIEnv* env, jclass loggerClass, jstring jmsg, jint priority) {
stopWatchingLogMessages(env, loggerClass);
gMessageToLookFor = jni::fromJString(env, jmsg);
gMessagePriorityToLookFor = priority;
setLogHandler(&stubLogHandler);
}
} // namespace logwatcher
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// get the current env
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
jni::registerNatives(env, "com/facebook/catalyst/testing/LogWatcher", {
{ "startWatchingForLogMessage", "(Ljava/lang/String;I)V", (void*) logwatcher::startWatchingForLogMessage},
{ "stopWatchingLogMessages", "()V", (void*) logwatcher::stopWatchingLogMessages },
{ "hasSeenExpectedLogMessage", "()Z", (void*) logwatcher::hasSeenExpectedLogMessage },
});
return JNI_VERSION_1_4;
}
} }

View File

@ -135,21 +135,66 @@ void tearDown(
jsModule.reset();
}
namespace logwatcher {
static std::string gMessageToLookFor;
static int gMessagePriorityToLookFor;
static bool gHasSeenMessage = false;
/**
* NB: Don't put JNI logic (or anything else that could trigger a log) here!
*/
static void stubLogHandler(int pri, const char *tag, const char *msg) {
if (gMessageToLookFor.empty()) {
return;
}
bool priorityMatches = pri == gMessagePriorityToLookFor;
bool substringFound = strstr(msg, gMessageToLookFor.c_str()) != NULL;
gHasSeenMessage |= priorityMatches && substringFound;
}
static jboolean hasSeenExpectedLogMessage(JNIEnv*, jclass) {
return gHasSeenMessage ? JNI_TRUE : JNI_FALSE;
}
static void stopWatchingLogMessages(JNIEnv*, jclass) {
gMessageToLookFor = "";
gHasSeenMessage = false;
setLogHandler(NULL);
}
static void startWatchingForLogMessage(JNIEnv* env, jclass loggerClass, jstring jmsg, jint priority) {
stopWatchingLogMessages(env, loggerClass);
gMessageToLookFor = jni::wrap_alias(jmsg)->toStdString();
gMessagePriorityToLookFor = priority;
setLogHandler(&stubLogHandler);
}
} // namespace logwatcher
} // namespace
} // namespace react
} // namespace facebook
using namespace facebook::react;
extern "C" facebook::xplat::module::CxxModule* CxxBenchmarkModule() {
return new facebook::react::CxxBenchmarkModule();
}
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
return facebook::jni::initialize(vm, [] {
facebook::jni::registerNatives(
"com/facebook/catalyst/testing/LogWatcher", {
makeNativeMethod("startWatchingForLogMessage", "(Ljava/lang/String;I)V", logwatcher::startWatchingForLogMessage),
makeNativeMethod("stopWatchingLogMessages", "()V", logwatcher::stopWatchingLogMessages),
makeNativeMethod("hasSeenExpectedLogMessage", "()Z", logwatcher::hasSeenExpectedLogMessage),
});
facebook::jni::registerNatives(
"com/facebook/react/CatalystBridgeBenchmarks", {
makeNativeMethod("runNativeBounce", facebook::react::runBounce),
makeNativeMethod("nativeSetUp", facebook::react::setUp),
makeNativeMethod("nativeTearDown", facebook::react::tearDown),
makeNativeMethod("runNativeBounce", runBounce),
makeNativeMethod("nativeSetUp", setUp),
makeNativeMethod("nativeTearDown", tearDown),
});
});
}

View File

@ -1,293 +0,0 @@
/**
* 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.bridge;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.facebook.react.common.MapBuilder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.TextNode;
import org.junit.Test;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.robolectric.RobolectricTestRunner;
import static org.fest.assertions.api.Assertions.assertThat;
/**
* Tests for {@link NativeModuleRegistry}.
*/
@RunWith(RobolectricTestRunner.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
public class NativeModuleRegistryTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
@Test
public void testModuleWithMethods() throws Exception {
NativeModuleRegistry registry = new NativeModuleRegistry.Builder()
.add(new MethodsModule())
.build();
String json = getModuleDescriptions(registry);
JsonNode node = parse(json);
JsonNode module = node.fields().next().getValue();
assertThat(module).isNotNull();
JsonNode methods = module.get("methods");
assertThat(methods).isNotNull();
ArrayList<String> expected = new ArrayList<String>();
expected.add("doSomething");
expected.add("saveData");
assertMethodsContainExactly(methods, expected);
}
@Test
public void testAsyncMethod() throws Exception {
NativeModuleRegistry registry = new NativeModuleRegistry.Builder()
.add(new MethodsModule())
.build();
String json = getModuleDescriptions(registry);
JsonNode node = parse(json);
JsonNode asyncMethodData = node.get("TestModule").get("methods").get("saveData");
assertThat(asyncMethodData.get("type")).isEqualTo(new TextNode("remoteAsync"));
JsonNode regularMethodData = node.get("TestModule").get("methods").get("doSomething");
assertThat(regularMethodData.get("type")).isNotEqualTo(new TextNode("remoteAsync"));
}
@Test
public void testModuleWithConstants() throws Exception {
ConstantsModule constantsModule = new ConstantsModule();
NativeModuleRegistry registry = new NativeModuleRegistry.Builder()
.add(constantsModule)
.build();
String json = getModuleDescriptions(registry);
JsonNode node = parse(json);
JsonNode module = node.fields().next().getValue();
assertThat(module).isNotNull();
JsonNode methods = module.get("methods");
assertThat(methods).isNotNull();
ArrayList<String> expected = new ArrayList<String>();
expected.add("runDMC");
assertMethodsContainExactly(methods, expected);
JsonNode constants = module.get("constants");
assertThat(constants.get("testInt").asInt()).isEqualTo(3);
assertThat(constants.get("testDouble").asDouble()).isEqualTo(3.14);
assertThat(constants.get("testString").asText()).isEqualTo("red panda");
JsonNode stringMap = constants.get("testStringMap");
assertThat(stringMap.get("war_room").asText()).isEqualTo("17.1");
assertThat(stringMap.get("android_corex").asText()).isEqualTo("16.1");
JsonNode intMap = constants.get("testIntMap");
assertThat(intMap.get("42").asInt()).isEqualTo(1);
assertThat(intMap.get("84").asInt()).isEqualTo(2);
JsonNode stringList = constants.get("testStringList");
assertThat(stringList.get(0).asText()).isEqualTo("vulpes vulpes");
assertThat(stringList.get(4).asText()).isEqualTo("vulpes velox");
JsonNode intList = constants.get("testIntList");
assertThat(intList.get(0).asInt()).isEqualTo(3);
assertThat(intList.get(4).asInt()).isEqualTo(5);
}
@Test
public void testModuleWithOnlyConstants() throws Exception {
OnlyConstantsModule onlyConstantsModule = new OnlyConstantsModule();
NativeModuleRegistry registry = new NativeModuleRegistry.Builder()
.add(onlyConstantsModule)
.build();
String json = getModuleDescriptions(registry);
JsonNode node = parse(json);
JsonNode module = node.fields().next().getValue();
assertThat(module).isNotNull();
JsonNode constants = module.get("constants");
assertThat(constants.get("testInt").asInt()).isEqualTo(4);
}
@Test
public void testModuleWithNestedMapConstants() throws Exception {
NestedMapConstantsModule nestedMapConstantsModule = new NestedMapConstantsModule();
NativeModuleRegistry registry = new NativeModuleRegistry.Builder()
.add(nestedMapConstantsModule)
.build();
String json = getModuleDescriptions(registry);
JsonNode node = parse(json);
JsonNode module = node.fields().next().getValue();
assertThat(module).isNotNull();
JsonNode constants = module.get("constants");
assertThat(constants).isNotNull();
JsonNode nestedMapConstant = constants.get("nestedMap");
assertThat(nestedMapConstant).isNotNull();
JsonNode firstLevel = nestedMapConstant.get("weNeedToGoDeeper");
assertThat(firstLevel).isNotNull();
JsonNode secondLevel = firstLevel.get("evenDeeper");
assertThat(secondLevel).isNotNull();
assertThat(secondLevel.get("inception").asBoolean()).isTrue();
}
private JsonNode parse(String json) throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.readTree(json);
}
private void assertMethodsContainExactly(JsonNode methodsObject, List<String> methodNames) {
ArrayList<String> actual = new ArrayList();
Iterator<Map.Entry<String,JsonNode>> fields = methodsObject.fields();
while (fields.hasNext()) {
String name = fields.next().getKey();
actual.add(name);
}
assertThat(actual)
.hasSize(methodNames.size())
.containsAll(methodNames);
}
private static String getModuleDescriptions(NativeModuleRegistry registry)
throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
registry.writeModuleDescriptions(writer);
writer.close();
return stringWriter.getBuffer().toString();
}
private static class MethodsModule extends BaseJavaModule {
@Override
public String getName() {
return "TestModule";
}
public void notACatalystMethod() {
}
@ReactMethod
public void doSomething() {
}
@ReactMethod
public void saveData(Promise promise) {
}
}
private static class ConstantsModule extends BaseJavaModule {
@Override
public String getName() {
return "ConstantsModule";
}
@Override
public Map<String, Object> getConstants() {
HashMap<String, Object> constants = new HashMap<String, Object>();
constants.put("testInt", 3);
constants.put("testDouble", 3.14);
constants.put("testString", "red panda");
constants.put(
"testStringMap",
MapBuilder.of(
"war_room",
"17.1",
"android_corex",
"16.1"));
constants.put(
"testIntMap",
MapBuilder.of(
42,
1,
84,
2));
constants.put(
"testStringList",
Arrays.asList(
new String[]{
"vulpes vulpes",
"vulpes cana",
"vulpes chama",
"vulpes fulfa",
"vulpes velox"}));
constants.put("testIntList", Arrays.asList(3, 1, 4, 1, 5));
return constants;
}
@ReactMethod
public void runDMC() {
}
}
private static class OnlyConstantsModule extends BaseJavaModule {
@Override
public String getName() {
return "OnlyConstantsModule";
}
@Override
public Map<String, Object> getConstants() {
return MapBuilder.<String, Object>of("testInt", 4);
}
}
private static class NestedMapConstantsModule extends BaseJavaModule {
@Override
public String getName() {
return "NestedMapConstantsModule";
}
@Override
public Map<String, Object> getConstants() {
return MapBuilder.<String, Object>of(
"nestedMap",
MapBuilder.of(
"weNeedToGoDeeper",
MapBuilder.of(
"evenDeeper",
MapBuilder.of("inception", true))));
}
}
}