diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactTestHelper.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactTestHelper.java index fbe1c0db2..20afcb234 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactTestHelper.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactTestHelper.java @@ -12,6 +12,7 @@ import javax.annotation.Nullable; import android.app.Instrumentation; import android.content.Context; +import android.support.test.InstrumentationRegistry; import android.view.View; import android.view.ViewGroup; @@ -24,9 +25,9 @@ import com.facebook.react.bridge.JavaScriptModulesConfig; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.NativeModuleCallExceptionHandler; import com.facebook.react.bridge.NativeModuleRegistry; +import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; -import android.support.test.InstrumentationRegistry; import com.android.internal.util.Predicate; public class ReactTestHelper { @@ -60,7 +61,7 @@ public class ReactTestHelper { public CatalystInstance build() { return new CatalystInstanceImpl.Builder() .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) - .setJSExecutor(new JSCJavaScriptExecutor()) + .setJSExecutor(new JSCJavaScriptExecutor(new WritableNativeMap())) .setRegistry(mNativeModuleRegistryBuilder.build()) .setJSModulesConfig(mJSModulesConfigBuilder.build()) .setJSBundleLoader(JSBundleLoader.createFileLoader( diff --git a/ReactAndroid/src/main/java/com/facebook/react/JSCConfig.java b/ReactAndroid/src/main/java/com/facebook/react/JSCConfig.java new file mode 100644 index 000000000..959a6f8d3 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/JSCConfig.java @@ -0,0 +1,12 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +package com.facebook.react; + +import com.facebook.react.bridge.WritableNativeMap; + +/** + * Interface for the configuration object that is passed to JSC. + */ +public interface JSCConfig { + public WritableNativeMap getConfigMap(); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 9ef96f5b8..990202ebe 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -181,6 +181,7 @@ public abstract class ReactInstanceManager { protected @Nullable LifecycleState mInitialLifecycleState; protected @Nullable UIImplementationProvider mUIImplementationProvider; protected @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; + protected @Nullable JSCConfig mJSCConfig; protected Builder() { } @@ -274,6 +275,11 @@ public abstract class ReactInstanceManager { return this; } + public Builder setJSCConfig(JSCConfig jscConfig) { + mJSCConfig = jscConfig; + return this; + } + /** * Instantiates a new {@link ReactInstanceManagerImpl}. * Before calling {@code build}, the following must be called: @@ -307,7 +313,8 @@ public abstract class ReactInstanceManager { mBridgeIdleDebugListener, Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"), mUIImplementationProvider, - mNativeModuleCallExceptionHandler); + mNativeModuleCallExceptionHandler, + mJSCConfig); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java index 63380ecb0..faba27baf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java @@ -127,6 +127,7 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START private final UIImplementationProvider mUIImplementationProvider; private final MemoryPressureRouter mMemoryPressureRouter; private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; + private final @Nullable JSCConfig mJSCConfig; private final ReactInstanceDevCommandsHandler mDevInterface = new ReactInstanceDevCommandsHandler() { @@ -193,7 +194,9 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START protected Result doInBackground(ReactContextInitParams... params) { Assertions.assertCondition(params != null && params.length > 0 && params[0] != null); try { - JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create(); + 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 @@ -274,7 +277,8 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener, LifecycleState initialLifecycleState, UIImplementationProvider uiImplementationProvider, - NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) { + NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler, + @Nullable JSCConfig jscConfig) { initializeSoLoaderIfNecessary(applicationContext); // TODO(9577825): remove this @@ -296,6 +300,7 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START mUIImplementationProvider = uiImplementationProvider; mMemoryPressureRouter = new MemoryPressureRouter(applicationContext); mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; + mJSCConfig = jscConfig; } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSCJavaScriptExecutor.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSCJavaScriptExecutor.java index 32b261930..b4431c0d6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSCJavaScriptExecutor.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSCJavaScriptExecutor.java @@ -16,8 +16,8 @@ import com.facebook.soloader.SoLoader; public class JSCJavaScriptExecutor extends JavaScriptExecutor { public static class Factory implements JavaScriptExecutor.Factory { @Override - public JavaScriptExecutor create() throws Exception { - return new JSCJavaScriptExecutor(); + public JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception { + return new JSCJavaScriptExecutor(jscConfig); } } @@ -25,10 +25,10 @@ public class JSCJavaScriptExecutor extends JavaScriptExecutor { SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB); } - public JSCJavaScriptExecutor() { - initialize(); + public JSCJavaScriptExecutor(WritableNativeMap jscConfig) { + initialize(jscConfig); } - private native void initialize(); + private native void initialize(WritableNativeMap jscConfig); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptExecutor.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptExecutor.java index bdf7e4fc6..92499d314 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptExecutor.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptExecutor.java @@ -15,7 +15,7 @@ import com.facebook.proguard.annotations.DoNotStrip; @DoNotStrip public abstract class JavaScriptExecutor extends Countable { public interface Factory { - JavaScriptExecutor create() throws Exception; + JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception; } /** diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ProxyJavaScriptExecutor.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ProxyJavaScriptExecutor.java index 80cd6f4b3..37d83b338 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ProxyJavaScriptExecutor.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ProxyJavaScriptExecutor.java @@ -32,7 +32,7 @@ public class ProxyJavaScriptExecutor extends JavaScriptExecutor { } @Override - public JavaScriptExecutor create() throws Exception { + public JavaScriptExecutor create(WritableNativeMap jscConfig) throws Exception { return new ProxyJavaScriptExecutor(mJavaJSExecutorFactory.create()); } } diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp index 38dd9e237..ad9f3c05a 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp @@ -7,7 +7,7 @@ #include #include #include - +#include #include #include #include @@ -83,13 +83,14 @@ static std::string executeJSCallWithJSC( } std::unique_ptr JSCExecutorFactory::createJSExecutor(Bridge *bridge) { - return std::unique_ptr(new JSCExecutor(bridge, cacheDir_)); + return std::unique_ptr(new JSCExecutor(bridge, cacheDir_, m_jscConfig)); } -JSCExecutor::JSCExecutor(Bridge *bridge, const std::string& cacheDir) : +JSCExecutor::JSCExecutor(Bridge *bridge, const std::string& cacheDir, const folly::dynamic& jscConfig) : m_bridge(bridge), m_deviceCacheDir(cacheDir), - m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()) { + m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()), + m_jscConfig(jscConfig) { initOnJSVMThread(); } @@ -98,12 +99,14 @@ JSCExecutor::JSCExecutor( int workerId, JSCExecutor *owner, const std::string& script, - const std::unordered_map& globalObjAsJSON) : + const std::unordered_map& globalObjAsJSON, + const folly::dynamic& jscConfig) : m_bridge(bridge), m_workerId(workerId), m_owner(owner), m_deviceCacheDir(owner->m_deviceCacheDir), - m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()) { + 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] () { @@ -145,6 +148,8 @@ void JSCExecutor::initOnJSVMThread() { installGlobalFunction(m_context, "nativeLoggingHook", JSLogging::nativeHook); + // TODO (t10136849): Pass the config options from map to JSC + #ifdef WITH_FB_JSC_TUNING configureJSCForAndroid(); #endif @@ -317,7 +322,7 @@ int JSCExecutor::addWebWorker( auto workerMQT = WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get()); std::unique_ptr worker; workerMQT->runOnQueueSync([this, &worker, &script, &globalObj, workerId] () { - worker.reset(new JSCExecutor(m_bridge, workerId, this, script, globalObj.toJSONMap())); + worker.reset(new JSCExecutor(m_bridge, workerId, this, script, globalObj.toJSONMap(), m_jscConfig)); }); Object workerObj = Value(m_context, workerRef).asObject(); diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.h b/ReactAndroid/src/main/jni/react/JSCExecutor.h index 6b2669dbe..336220144 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.h +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "Executor.h" @@ -18,10 +19,13 @@ class MessageQueueThread; class JSCExecutorFactory : public JSExecutorFactory { public: - JSCExecutorFactory(const std::string& cacheDir) : cacheDir_(cacheDir) {} + JSCExecutorFactory(const std::string& cacheDir, const folly::dynamic& jscConfig) : + cacheDir_(cacheDir), + m_jscConfig(jscConfig) {} virtual std::unique_ptr createJSExecutor(Bridge *bridge) override; private: std::string cacheDir_; + folly::dynamic m_jscConfig; }; class JSCExecutor; @@ -45,7 +49,7 @@ public: /** * Must be invoked from thread this Executor will run on. */ - explicit JSCExecutor(Bridge *bridge, const std::string& cacheDir); + explicit JSCExecutor(Bridge *bridge, const std::string& cacheDir, const folly::dynamic& jscConfig); ~JSCExecutor() override; virtual void loadApplicationScript( @@ -84,6 +88,7 @@ private: std::string m_deviceCacheDir; std::shared_ptr m_messageQueueThread; std::unique_ptr m_unbundle; + folly::dynamic m_jscConfig; /** * WebWorker constructor. Must be invoked from thread this Executor will run on. @@ -93,7 +98,8 @@ private: int workerId, JSCExecutor *owner, const std::string& script, - const std::unordered_map& globalObjAsJSON); + const std::unordered_map& globalObjAsJSON, + const folly::dynamic& jscConfig); void initOnJSVMThread(); void terminateOnJSVMThread(); diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index 1a4dbe3c1..5081bd67f 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -819,13 +819,21 @@ std::string getDeviceCacheDir() { } struct CountableJSCExecutorFactory : CountableJSExecutorFactory { +public: + CountableJSCExecutorFactory(folly::dynamic jscConfig) : m_jscConfig(jscConfig) {} virtual std::unique_ptr createJSExecutor(Bridge *bridge) override { - return JSCExecutorFactory(getDeviceCacheDir()).createJSExecutor(bridge); + return JSCExecutorFactory(getDeviceCacheDir(), m_jscConfig).createJSExecutor(bridge); } + +private: + folly::dynamic m_jscConfig; }; -static void createJSCExecutor(JNIEnv *env, jobject obj) { - auto executor = createNew(); +static void createJSCExecutor(JNIEnv *env, jobject obj, jobject jscConfig) { + auto nativeMap = extractRefPtr(env, jscConfig); + exceptions::throwIfObjectAlreadyConsumed(nativeMap, "Map to push already consumed"); + auto executor = createNew(std::move(nativeMap->map)); + nativeMap->isConsumed = true; setCountableForJava(env, obj, std::move(executor)); } @@ -920,7 +928,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { }); registerNatives("com/facebook/react/bridge/JSCJavaScriptExecutor", { - makeNativeMethod("initialize", executors::createJSCExecutor), + makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/WritableNativeMap;)V", + executors::createJSCExecutor), }); registerNatives("com/facebook/react/bridge/ProxyJavaScriptExecutor", {