mirror of
https://github.com/status-im/react-native.git
synced 2025-02-26 08:05:34 +00:00
Delete old bridge
Reviewed By: astreet Differential Revision: D3510660 fbshipit-source-id: 031b9dcf19dd4e6677a6c9417917930bcbbe3219
This commit is contained in:
parent
5617d41327
commit
1a690d5674
@ -13,6 +13,7 @@ android_library(
|
|||||||
react_native_dep('third-party/java/testing-support-lib:runner'),
|
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/bridge:bridge'),
|
||||||
react_native_target('java/com/facebook/react/common:common'),
|
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/devsupport:devsupport'),
|
||||||
react_native_target('java/com/facebook/react/modules/core:core'),
|
react_native_target('java/com/facebook/react/modules/core:core'),
|
||||||
react_native_target('java/com/facebook/react/modules/debug:debug'),
|
react_native_target('java/com/facebook/react/modules/debug:debug'),
|
||||||
|
@ -25,7 +25,6 @@ import com.facebook.react.bridge.ReactContext;
|
|||||||
import com.facebook.react.LifecycleState;
|
import com.facebook.react.LifecycleState;
|
||||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
import com.facebook.react.ReactPackage;
|
|
||||||
import com.facebook.react.ReactRootView;
|
import com.facebook.react.ReactRootView;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
import com.facebook.react.uimanager.UIImplementationProvider;
|
import com.facebook.react.uimanager.UIImplementationProvider;
|
||||||
|
@ -20,13 +20,14 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
import com.facebook.react.bridge.CatalystInstance;
|
import com.facebook.react.bridge.CatalystInstance;
|
||||||
import com.facebook.react.bridge.CatalystInstanceImpl;
|
import com.facebook.react.cxxbridge.CatalystInstanceImpl;
|
||||||
import com.facebook.react.bridge.JSBundleLoader;
|
import com.facebook.react.cxxbridge.JSBundleLoader;
|
||||||
import com.facebook.react.bridge.JSCJavaScriptExecutor;
|
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.JavaScriptModuleRegistry;
|
||||||
import com.facebook.react.bridge.NativeModule;
|
import com.facebook.react.bridge.NativeModule;
|
||||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||||
import com.facebook.react.bridge.NativeModuleRegistry;
|
|
||||||
import com.facebook.react.bridge.WritableNativeMap;
|
import com.facebook.react.bridge.WritableNativeMap;
|
||||||
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
||||||
|
|
||||||
@ -61,9 +62,15 @@ public class ReactTestHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CatalystInstance build() {
|
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()
|
return new CatalystInstanceImpl.Builder()
|
||||||
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
|
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
|
||||||
.setJSExecutor(new JSCJavaScriptExecutor(new WritableNativeMap()))
|
.setJSExecutor(executor)
|
||||||
.setRegistry(mNativeModuleRegistryBuilder.build())
|
.setRegistry(mNativeModuleRegistryBuilder.build())
|
||||||
.setJSModuleRegistry(mJSModuleRegistryBuilder.build())
|
.setJSModuleRegistry(mJSModuleRegistryBuilder.build())
|
||||||
.setJSBundleLoader(JSBundleLoader.createFileLoader(
|
.setJSBundleLoader(JSBundleLoader.createFileLoader(
|
||||||
|
@ -202,7 +202,6 @@ public abstract class ReactInstanceManager {
|
|||||||
protected @Nullable Activity mCurrentActivity;
|
protected @Nullable Activity mCurrentActivity;
|
||||||
protected @Nullable DefaultHardwareBackBtnHandler mDefaultHardwareBackBtnHandler;
|
protected @Nullable DefaultHardwareBackBtnHandler mDefaultHardwareBackBtnHandler;
|
||||||
protected @Nullable RedBoxHandler mRedBoxHandler;
|
protected @Nullable RedBoxHandler mRedBoxHandler;
|
||||||
protected boolean mUseOldBridge;
|
|
||||||
|
|
||||||
protected Builder() {
|
protected Builder() {
|
||||||
}
|
}
|
||||||
@ -330,11 +329,6 @@ public abstract class ReactInstanceManager {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setUseOldBridge(boolean enable) {
|
|
||||||
mUseOldBridge = enable;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new {@link ReactInstanceManagerImpl}.
|
* Instantiates a new {@link ReactInstanceManagerImpl}.
|
||||||
* Before calling {@code build}, the following must be called:
|
* Before calling {@code build}, the following must be called:
|
||||||
@ -350,10 +344,6 @@ public abstract class ReactInstanceManager {
|
|||||||
mApplication,
|
mApplication,
|
||||||
"Application property has not been set with this builder");
|
"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(
|
Assertions.assertCondition(
|
||||||
mUseDeveloperSupport || mJSBundleFile != null || mJSBundleLoader != null,
|
mUseDeveloperSupport || mJSBundleFile != null || mJSBundleLoader != null,
|
||||||
"JS Bundle File has to be provided when dev support is disabled");
|
"JS Bundle File has to be provided when dev support is disabled");
|
||||||
@ -367,38 +357,21 @@ public abstract class ReactInstanceManager {
|
|||||||
mUIImplementationProvider = new UIImplementationProvider();
|
mUIImplementationProvider = new UIImplementationProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUseOldBridge) {
|
return new XReactInstanceManagerImpl(
|
||||||
return new ReactInstanceManagerImpl(
|
mApplication,
|
||||||
mApplication,
|
mCurrentActivity,
|
||||||
mCurrentActivity,
|
mDefaultHardwareBackBtnHandler,
|
||||||
mDefaultHardwareBackBtnHandler,
|
(mJSBundleLoader == null && mJSBundleFile != null) ?
|
||||||
mJSBundleFile,
|
JSBundleLoader.createFileLoader(mApplication, mJSBundleFile) : mJSBundleLoader,
|
||||||
mJSMainModuleName,
|
mJSMainModuleName,
|
||||||
mPackages,
|
mPackages,
|
||||||
mUseDeveloperSupport,
|
mUseDeveloperSupport,
|
||||||
mBridgeIdleDebugListener,
|
mBridgeIdleDebugListener,
|
||||||
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
||||||
mUIImplementationProvider,
|
mUIImplementationProvider,
|
||||||
mNativeModuleCallExceptionHandler,
|
mNativeModuleCallExceptionHandler,
|
||||||
mJSCConfig,
|
mJSCConfig,
|
||||||
mRedBoxHandler);
|
mRedBoxHandler);
|
||||||
} else {
|
|
||||||
return new XReactInstanceManagerImpl(
|
|
||||||
mApplication,
|
|
||||||
mCurrentActivity,
|
|
||||||
mDefaultHardwareBackBtnHandler,
|
|
||||||
(mJSBundleLoader == null && mJSBundleFile != null) ?
|
|
||||||
JSBundleLoader.createFileLoader(mApplication, mJSBundleFile) : mJSBundleLoader,
|
|
||||||
mJSMainModuleName,
|
|
||||||
mPackages,
|
|
||||||
mUseDeveloperSupport,
|
|
||||||
mBridgeIdleDebugListener,
|
|
||||||
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
|
|
||||||
mUIImplementationProvider,
|
|
||||||
mNativeModuleCallExceptionHandler,
|
|
||||||
mJSCConfig,
|
|
||||||
mRedBoxHandler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
@ -9,20 +9,9 @@
|
|||||||
|
|
||||||
package com.facebook.react.bridge;
|
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;
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
/**
|
public class ReactBridge {
|
||||||
* Interface to the JS execution environment and means of transport for messages Java<->JS.
|
|
||||||
*/
|
|
||||||
@DoNotStrip
|
|
||||||
public class ReactBridge extends Countable {
|
|
||||||
|
|
||||||
private static final String REACT_NATIVE_LIB = "reactnativejni";
|
private static final String REACT_NATIVE_LIB = "reactnativejni";
|
||||||
private static final String XREACT_NATIVE_LIB = "reactnativejnifb";
|
private static final String XREACT_NATIVE_LIB = "reactnativejnifb";
|
||||||
@ -31,81 +20,8 @@ public class ReactBridge extends Countable {
|
|||||||
staticInit();
|
staticInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ReactCallback mCallback;
|
|
||||||
private final JavaScriptExecutor mJSExecutor;
|
|
||||||
private final MessageQueueThread mNativeModulesQueueThread;
|
|
||||||
|
|
||||||
public static void staticInit() {
|
public static void staticInit() {
|
||||||
SoLoader.loadLibrary(REACT_NATIVE_LIB);
|
SoLoader.loadLibrary(REACT_NATIVE_LIB);
|
||||||
SoLoader.loadLibrary(XREACT_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();
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
@ -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',
|
|
||||||
],
|
|
||||||
)
|
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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() {};
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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; });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -5,14 +5,7 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_MODULE := libreactnativejni
|
LOCAL_MODULE := libreactnativejni
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
JExecutorToken.cpp \
|
Dummy.cpp \
|
||||||
JMessageQueueThread.cpp \
|
|
||||||
JSCPerfLogging.cpp \
|
|
||||||
JSLoader.cpp \
|
|
||||||
JSLogging.cpp \
|
|
||||||
JniJSModulesUnbundle.cpp \
|
|
||||||
OnLoad.cpp \
|
|
||||||
ProxyExecutor.cpp \
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../.. $(LOCAL_PATH)/..
|
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../.. $(LOCAL_PATH)/..
|
||||||
@ -23,15 +16,9 @@ LOCAL_CFLAGS += $(CXX11_FLAGS)
|
|||||||
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
|
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
|
||||||
|
|
||||||
LOCAL_LDLIBS += -landroid
|
LOCAL_LDLIBS += -landroid
|
||||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libjsc libglog_init libreactnativejnifb
|
LOCAL_SHARED_LIBRARIES := libreactnativejnifb
|
||||||
LOCAL_STATIC_LIBRARIES := libreactnative
|
LOCAL_STATIC_LIBRARIES :=
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
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)
|
$(call import-module,xreact/jni)
|
||||||
|
@ -3,66 +3,20 @@ include_defs('//ReactAndroid/DEFS')
|
|||||||
# We depend on JSC, support the same platforms
|
# We depend on JSC, support the same platforms
|
||||||
SUPPORTED_PLATFORMS = '^android-(armv7|x86)$'
|
SUPPORTED_PLATFORMS = '^android-(armv7|x86)$'
|
||||||
|
|
||||||
DEPS = [
|
# TODO(cjhopman): Remove this target (or move the xreact target to this directory).
|
||||||
FBGLOGINIT_TARGET,
|
cxx_library(
|
||||||
'//native/fb:fb',
|
name='jni',
|
||||||
'//native/third-party/android-ndk:android',
|
|
||||||
'//xplat/folly:molly',
|
|
||||||
]
|
|
||||||
|
|
||||||
def jni_library(**kwargs):
|
|
||||||
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',
|
soname = 'libreactnativejni.so',
|
||||||
header_namespace = 'react/jni',
|
header_namespace = 'react/jni',
|
||||||
supported_platforms_regex = SUPPORTED_PLATFORMS,
|
supported_platforms_regex = SUPPORTED_PLATFORMS,
|
||||||
srcs = [
|
srcs = [
|
||||||
'JExecutorToken.cpp',
|
'Dummy.cpp',
|
||||||
'JMessageQueueThread.cpp',
|
|
||||||
'JSCPerfLogging.cpp',
|
|
||||||
'JSLoader.cpp',
|
|
||||||
'JSLogging.cpp',
|
|
||||||
'JniJSModulesUnbundle.cpp',
|
|
||||||
'OnLoad.cpp',
|
|
||||||
'ProxyExecutor.cpp',
|
|
||||||
],
|
],
|
||||||
headers = [
|
deps = [
|
||||||
'JSLoader.h',
|
react_native_target('jni/xreact/jni:jni'),
|
||||||
'JExecutorToken.h',
|
|
||||||
'JExecutorTokenFactory.h',
|
|
||||||
'JMessageQueueThread.h',
|
|
||||||
'JNativeRunnable.h',
|
|
||||||
'JniJSModulesUnbundle.h',
|
|
||||||
'JSCPerfLogging.h',
|
|
||||||
'JSLogging.h',
|
|
||||||
'ProxyExecutor.h',
|
|
||||||
'WebWorkers.h',
|
|
||||||
],
|
],
|
||||||
exported_headers = [
|
visibility = [
|
||||||
],
|
'PUBLIC',
|
||||||
preprocessor_flags = [
|
|
||||||
'-DLOG_TAG="ReactNativeJNI"',
|
|
||||||
'-DWITH_FBSYSTRACE=1',
|
|
||||||
'-DWITH_FBJSCEXTENSIONS=1',
|
|
||||||
],
|
|
||||||
compiler_flags = [
|
|
||||||
'-Wall',
|
|
||||||
'-Werror',
|
|
||||||
'-fexceptions',
|
|
||||||
'-std=c++11',
|
|
||||||
'-fvisibility=hidden',
|
|
||||||
'-frtti',
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
7
ReactAndroid/src/main/jni/react/jni/Dummy.cpp
Normal file
7
ReactAndroid/src/main/jni/react/jni/Dummy.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
void ThisWhatsLeftOfTheOldBridge() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
@ -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;";
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
|
|
||||||
} }
|
|
@ -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;");
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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();
|
|
||||||
|
|
||||||
} }
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}};
|
|
@ -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);
|
|
||||||
}}
|
|
@ -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()))};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
@ -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;
|
|
||||||
};
|
|
||||||
}}
|
|
@ -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',
|
|
||||||
)
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@ -135,21 +135,66 @@ void tearDown(
|
|||||||
jsModule.reset();
|
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() {
|
extern "C" facebook::xplat::module::CxxModule* CxxBenchmarkModule() {
|
||||||
return new facebook::react::CxxBenchmarkModule();
|
return new facebook::react::CxxBenchmarkModule();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
|
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
|
||||||
return facebook::jni::initialize(vm, [] {
|
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(
|
facebook::jni::registerNatives(
|
||||||
"com/facebook/react/CatalystBridgeBenchmarks", {
|
"com/facebook/react/CatalystBridgeBenchmarks", {
|
||||||
makeNativeMethod("runNativeBounce", facebook::react::runBounce),
|
makeNativeMethod("runNativeBounce", runBounce),
|
||||||
makeNativeMethod("nativeSetUp", facebook::react::setUp),
|
makeNativeMethod("nativeSetUp", setUp),
|
||||||
makeNativeMethod("nativeTearDown", facebook::react::tearDown),
|
makeNativeMethod("nativeTearDown", tearDown),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user