Expose way for native modules to modify JSC context

Reviewed By: svcscm

Differential Revision: D2933197

fb-gh-sync-id: 32eb943ab341804343bbcadd29f0377fccf75de6
shipit-source-id: 32eb943ab341804343bbcadd29f0377fccf75de6
This commit is contained in:
Scott Kyle
2016-02-15 12:57:21 -08:00
committed by facebook-github-bot-1
parent 46a8f1d8e0
commit e5ba46c30d
13 changed files with 77 additions and 2 deletions

View File

@@ -16,6 +16,15 @@
*/ */
RCT_EXTERN NSString *const RCTJSCThreadName; RCT_EXTERN NSString *const RCTJSCThreadName;
/**
* This notification fires on the JS thread immediately after a `JSContext`
* is fully initialized, but before the JS bundle has been loaded. The object
* of this notification is the `JSContext`. Native modules should listen for
* notification only if they need to install custom functionality into the
* context. Note that this notification won't fire when debugging in Chrome.
*/
RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
/** /**
* Uses a JavaScriptCore context as the execution engine. * Uses a JavaScriptCore context as the execution engine.
*/ */

View File

@@ -28,6 +28,8 @@
NSString *const RCTJSCThreadName = @"com.facebook.React.JavaScript"; NSString *const RCTJSCThreadName = @"com.facebook.React.JavaScript";
NSString *const RCTJavaScriptContextCreatedNotification = @"RCTJavaScriptContextCreatedNotification";
static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled"; static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled";
@interface RCTJavaScriptContext : NSObject <RCTInvalidating> @interface RCTJavaScriptContext : NSObject <RCTInvalidating>
@@ -332,7 +334,16 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
}]; }];
[self executeBlockOnJavaScriptQueue:^{ [self executeBlockOnJavaScriptQueue:^{
RCTInstallJSCProfiler(_bridge, self.context.ctx); RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf.valid) {
return;
}
JSContext *context = strongSelf.context.context;
RCTInstallJSCProfiler(_bridge, context.JSGlobalContextRef);
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification
object:context];
}]; }];
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) { for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {

View File

@@ -332,6 +332,11 @@ public abstract class BaseJavaModule implements NativeModule {
return false; return false;
} }
@Override
public void onReactBridgeInitialized(ReactBridge bridge) {
// do nothing
}
@Override @Override
public void onCatalystInstanceDestroy() { public void onCatalystInstanceDestroy() {
// do nothing // do nothing

View File

@@ -127,6 +127,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
} }
mJavaRegistry.notifyReactBridgeInitialized(bridge);
return bridge; return bridge;
} }

View File

@@ -59,6 +59,15 @@ public interface NativeModule {
*/ */
boolean canOverrideExistingModule(); boolean canOverrideExistingModule();
/**
* Called on the JS thread after a ReactBridge has been created. This is useful for native modules
* that need to do any setup before the JS bundle has been loaded. An example of this would be
* installing custom functionality into the JavaScriptCore context.
*
* @param bridge the ReactBridge instance that has just been created
*/
void onReactBridgeInitialized(ReactBridge bridge);
/** /**
* Called before {CatalystInstance#onHostDestroy} * Called before {CatalystInstance#onHostDestroy}
*/ */

View File

@@ -114,6 +114,19 @@ public class NativeModuleRegistry {
} }
} }
/* 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() { public void onBatchComplete() {
for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) { for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) {
mBatchCompleteListenerModules.get(i).onBatchComplete(); mBatchCompleteListenerModules.get(i).onBatchComplete();

View File

@@ -87,4 +87,13 @@ public class ReactBridge extends Countable {
public native void stopProfiler(String title, String filename); public native void stopProfiler(String title, String filename);
private native void handleMemoryPressureModerate(); private native void handleMemoryPressureModerate();
private native void handleMemoryPressureCritical(); private native void handleMemoryPressureCritical();
/**
* This method will return a long representing the underlying JSGlobalContextRef pointer or
* 0 (representing NULL) when in Chrome debug mode, and is only useful if passed back through
* the JNI to native code that will use it with the JavaScriptCore C API.
* **WARNING:** This method is *experimental* and should only be used when no other option is
* available. It will likely change in a future release!
*/
public native long getJavaScriptContextNativePtrExperimental();
} }

View File

@@ -57,6 +57,10 @@ void Bridge::setGlobalVariable(const std::string& propName, const std::string& j
m_mainExecutor->setGlobalVariable(propName, jsonValue); m_mainExecutor->setGlobalVariable(propName, jsonValue);
} }
void* Bridge::getJavaScriptContext() {
return m_mainExecutor->getJavaScriptContext();
}
bool Bridge::supportsProfiling() { bool Bridge::supportsProfiling() {
return m_mainExecutor->supportsProfiling(); return m_mainExecutor->supportsProfiling();
} }

View File

@@ -54,6 +54,7 @@ public:
const std::string& startupCode, const std::string& startupCode,
const std::string& sourceURL); const std::string& sourceURL);
void setGlobalVariable(const std::string& propName, const std::string& jsonValue); void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
void* getJavaScriptContext();
bool supportsProfiling(); bool supportsProfiling();
void startProfiler(const std::string& title); void startProfiler(const std::string& title);
void stopProfiler(const std::string& title, const std::string& filename); void stopProfiler(const std::string& title, const std::string& filename);

View File

@@ -60,6 +60,9 @@ public:
virtual void setGlobalVariable( virtual void setGlobalVariable(
const std::string& propName, const std::string& propName,
const std::string& jsonValue) = 0; const std::string& jsonValue) = 0;
virtual void* getJavaScriptContext() {
return nullptr;
};
virtual bool supportsProfiling() { virtual bool supportsProfiling() {
return false; return false;
}; };

View File

@@ -198,6 +198,10 @@ void JSCExecutor::setGlobalVariable(const std::string& propName, const std::stri
JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL); JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
} }
void* JSCExecutor::getJavaScriptContext() {
return m_context;
}
bool JSCExecutor::supportsProfiling() { bool JSCExecutor::supportsProfiling() {
#ifdef WITH_FBSYSTRACE #ifdef WITH_FBSYSTRACE
return true; return true;

View File

@@ -48,6 +48,7 @@ public:
virtual void setGlobalVariable( virtual void setGlobalVariable(
const std::string& propName, const std::string& propName,
const std::string& jsonValue) override; const std::string& jsonValue) override;
virtual void* getJavaScriptContext() override;
virtual bool supportsProfiling() override; virtual bool supportsProfiling() override;
virtual void startProfiler(const std::string &titleString) override; virtual void startProfiler(const std::string &titleString) override;
virtual void stopProfiler(const std::string &titleString, const std::string &filename) override; virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;

View File

@@ -760,6 +760,11 @@ static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstrin
bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue)); 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 jboolean supportsProfiling(JNIEnv* env, jobject obj) { static jboolean supportsProfiling(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<CountableBridge>(env, obj); auto bridge = extractRefPtr<CountableBridge>(env, obj);
return bridge->supportsProfiling() ? JNI_TRUE : JNI_FALSE; return bridge->supportsProfiling() ? JNI_TRUE : JNI_FALSE;
@@ -944,7 +949,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
makeNativeMethod("stopProfiler", bridge::stopProfiler), makeNativeMethod("stopProfiler", bridge::stopProfiler),
makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate), makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical), makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),
makeNativeMethod("getJavaScriptContextNativePtrExperimental", bridge::getJavaScriptContext),
}); });
jclass nativeRunnableClass = env->FindClass("com/facebook/react/bridge/queue/NativeRunnableDeprecated"); jclass nativeRunnableClass = env->FindClass("com/facebook/react/bridge/queue/NativeRunnableDeprecated");