Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose way for native modules to modify JSC context #5540

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions React/Executors/RCTJSCExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@

#import "RCTJavaScriptExecutor.h"

/**
* 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.
*/
Expand Down
13 changes: 12 additions & 1 deletion React/Executors/RCTJSCExecutor.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#import "RCTRedBox.h"
#import "RCTSourceCode.h"

NSString *const RCTJavaScriptContextCreatedNotification = @"RCTJavaScriptContextCreatedNotification";

static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled";

@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
Expand Down Expand Up @@ -314,7 +316,16 @@ - (void)setUp
}];

[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
Copy link
Member

@javache javache Apr 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is under an #if RCT_DEV so notifications will never fire in production.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that was pretty major mistake on my part. I realized that a little while after this was merged. 😞

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put up a fix 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks! 👯

object:context];
}];

for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ public boolean canOverrideExistingModule() {
return false;
}

@Override
public void onReactBridgeInitialized(ReactBridge bridge) {
// do nothing
}

@Override
public void onCatalystInstanceDestroy() {
// do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ private ReactBridge initializeBridge(
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}

mJavaRegistry.notifyReactBridgeInitialized(bridge);
return bridge;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ interface NativeMethod {
*/
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}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ private 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() {
for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) {
mBatchCompleteListenerModules.get(i).onBatchComplete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ private native void initialize(
public native void callFunction(int moduleId, int methodId, NativeArray arguments);
public native void invokeCallback(int callbackID, NativeArray arguments);
public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
public native long getJavaScriptContext();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name something convincingly scary like getJavaScriptContextNativePtrExperimental and document that this API will go away soon.

public native boolean supportsProfiling();
public native void startProfiler(String title);
public native void stopProfiler(String title, String filename);
Expand Down
4 changes: 4 additions & 0 deletions ReactAndroid/src/main/jni/react/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ void Bridge::setGlobalVariable(const std::string& propName, const std::string& j
m_jsExecutor->setGlobalVariable(propName, jsonValue);
}

void* Bridge::getJavaScriptContext() {
return m_jsExecutor->getJavaScriptContext();
}

bool Bridge::supportsProfiling() {
return m_jsExecutor->supportsProfiling();
}
Expand Down
1 change: 1 addition & 0 deletions ReactAndroid/src/main/jni/react/Bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class Bridge : public Countable {
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);
Expand Down
3 changes: 3 additions & 0 deletions ReactAndroid/src/main/jni/react/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class JSExecutor {
virtual void setGlobalVariable(
const std::string& propName,
const std::string& jsonValue) = 0;
virtual void* getJavaScriptContext() {
return nullptr;
};
virtual bool supportsProfiling() {
return false;
};
Expand Down
4 changes: 4 additions & 0 deletions ReactAndroid/src/main/jni/react/JSCExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ void JSCExecutor::setGlobalVariable(const std::string& propName, const std::stri
JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}

void* JSCExecutor::getJavaScriptContext() {
return m_context;
}

bool JSCExecutor::supportsProfiling() {
#ifdef WITH_FBSYSTRACE
return true;
Expand Down
1 change: 1 addition & 0 deletions ReactAndroid/src/main/jni/react/JSCExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class JSCExecutor : public JSExecutor, public JSCWebWorkerOwner {
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;
Expand Down
7 changes: 6 additions & 1 deletion ReactAndroid/src/main/jni/react/jni/OnLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,11 @@ static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstrin
bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
}

static jlong getJavaScriptContext(JNIEnv *env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
return (uintptr_t) bridge->getJavaScriptContext();
}

static jboolean supportsProfiling(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
return bridge->supportsProfiling() ? JNI_TRUE : JNI_FALSE;
Expand Down Expand Up @@ -880,12 +885,12 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
makeNativeMethod("callFunction", bridge::callFunction),
makeNativeMethod("invokeCallback", bridge::invokeCallback),
makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
makeNativeMethod("getJavaScriptContext", bridge::getJavaScriptContext),
makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
makeNativeMethod("startProfiler", bridge::startProfiler),
makeNativeMethod("stopProfiler", bridge::stopProfiler),
makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),

});

jclass nativeRunnableClass = env->FindClass("com/facebook/react/bridge/queue/NativeRunnableDeprecated");
Expand Down