Skip to content

Commit

Permalink
add debugging settings (#34489)
Browse files Browse the repository at this point in the history
Summary:
For brownfield apps, it is possible to have multiple hermes runtimes serving different JS bundles. Hermes inspector currently only supports single JS bundle. The latest loaded JS bundle will overwrite previous JS bundle. This is because we always use the ["Hermes React Native" as the inspector page name](https://github.com/facebook/react-native/blob/de75a7a22eebbe6b7106377bdd697a2d779b91b0/ReactCommon/hermes/executor/HermesExecutorFactory.cpp#L157) and [the latest page name will overwrite previous one](https://github.com/facebook/react-native/blob/de75a7a22eebbe6b7106377bdd697a2d779b91b0/ReactCommon/hermes/inspector/chrome/ConnectionDemux.cpp#L77-L86).

This PR adds more customization for HermesExecutorFactory:
- `setEnableDebugger`: provide a way to disable debugging features for the hermes runtime
- `setDebuggerName`: provide a way to customize inspector page name other than the default "Hermes React Native"

## Changelog

[General] [Added] - Add more debugging settings for *HermesExecutorFactory*

Pull Request resolved: #34489

Test Plan:
Verify the features by RNTester.

1. `setEnableDebugger`

```diff
 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java
+++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java
@@ -10,10 +10,12 @@ package com.facebook.react.uiapp;
 import android.app.Application;
 import androidx.annotation.NonNull;
 import com.facebook.fbreact.specs.SampleTurboModule;
+import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
 import com.facebook.react.ReactApplication;
 import com.facebook.react.ReactNativeHost;
 import com.facebook.react.ReactPackage;
 import com.facebook.react.TurboReactPackage;
+import com.facebook.react.bridge.JavaScriptExecutorFactory;
 import com.facebook.react.bridge.NativeModule;
 import com.facebook.react.bridge.ReactApplicationContext;
 import com.facebook.react.config.ReactFeatureFlags;
@@ -50,6 +52,13 @@ public class RNTesterApplication extends Application implements ReactApplication
           return BuildConfig.DEBUG;
         }

+        Override
+        protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
+          HermesExecutorFactory factory = new HermesExecutorFactory();
+          factory.setEnableDebugger(false);
+          return factory;
+        }
+
         Override
         public List<ReactPackage> getPackages() {
           return Arrays.<ReactPackage>asList(
```

after app launched, the metro inspector should return empty array.
Run `curl http://localhost:8081/json` and returns `[]`

2. `setDebuggerName`

```diff
 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java
+++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java
@@ -10,10 +10,12 @@ package com.facebook.react.uiapp;
 import android.app.Application;
 import androidx.annotation.NonNull;
 import com.facebook.fbreact.specs.SampleTurboModule;
+import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
 import com.facebook.react.ReactApplication;
 import com.facebook.react.ReactNativeHost;
 import com.facebook.react.ReactPackage;
 import com.facebook.react.TurboReactPackage;
+import com.facebook.react.bridge.JavaScriptExecutorFactory;
 import com.facebook.react.bridge.NativeModule;
 import com.facebook.react.bridge.ReactApplicationContext;
 import com.facebook.react.config.ReactFeatureFlags;
@@ -50,6 +52,13 @@ public class RNTesterApplication extends Application implements ReactApplication
           return BuildConfig.DEBUG;
         }

+        Override
+        protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
+          HermesExecutorFactory factory = new HermesExecutorFactory();
+          factory.setDebuggerName("Custom Hermes Debugger");
+          return factory;
+        }
+
         Override
         public List<ReactPackage> getPackages() {
           return Arrays.<ReactPackage>asList(
```

after app launched, the metro inspector should return an entry with *Custom Hermes Debugger*
Run `curl http://localhost:8081/json` and returns

```json
[
  {
    "id": "2-1",
    "description": "com.facebook.react.uiapp",
    "title": "Custom Hermes Debugger",
    "faviconUrl": "https://reactjs.org/favicon.ico",
    "devtoolsFrontendUrl": "devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=%5B%3A%3A1%5D%3A8081%2Finspector%2Fdebug%3Fdevice%3D2 (e5c5dcd9e26e9443f59864d9763b049e0bda98e7)&page=1 (ea93151)",
    "type": "node",
    "webSocketDebuggerUrl": "ws://[::1]:8081/inspector/debug?device=2&page=1",
    "vm": "Hermes"
  }
]
```

Reviewed By: mdvacca

Differential Revision: D38982104

Pulled By: cipolleschi

fbshipit-source-id: 78003c173db55448a751145986985b3e1d1c71bb
  • Loading branch information
Kudo authored and facebook-github-bot committed Sep 7, 2022
1 parent ac1fe3b commit 32d12e8
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 17 deletions.
Expand Up @@ -33,8 +33,11 @@ public static void loadLibrary() throws UnsatisfiedLinkError {
}
}

HermesExecutor(@Nullable RuntimeConfig config) {
super(config == null ? initHybridDefaultConfig() : initHybrid(config.heapSizeMB));
HermesExecutor(@Nullable RuntimeConfig config, boolean enableDebugger, String debuggerName) {
super(
config == null
? initHybridDefaultConfig(enableDebugger, debuggerName)
: initHybrid(enableDebugger, debuggerName, config.heapSizeMB));
}

@Override
Expand All @@ -51,7 +54,9 @@ public String getName() {
*/
public static native boolean canLoadFile(String path);

private static native HybridData initHybridDefaultConfig();
private static native HybridData initHybridDefaultConfig(
boolean enableDebugger, String debuggerName);

private static native HybridData initHybrid(long heapSizeMB);
private static native HybridData initHybrid(
boolean enableDebugger, String debuggerName, long heapSizeMB);
}
Expand Up @@ -15,6 +15,8 @@ public class HermesExecutorFactory implements JavaScriptExecutorFactory {
private static final String TAG = "Hermes";

private final RuntimeConfig mConfig;
private boolean mEnableDebugger = true;
private String mDebuggerName = "";

public HermesExecutorFactory() {
this(null);
Expand All @@ -24,9 +26,17 @@ public HermesExecutorFactory(RuntimeConfig config) {
mConfig = config;
}

public void setEnableDebugger(boolean enableDebugger) {
mEnableDebugger = enableDebugger;
}

public void setDebuggerName(String debuggerName) {
mDebuggerName = debuggerName;
}

@Override
public JavaScriptExecutor create() {
return new HermesExecutor(mConfig);
return new HermesExecutor(mConfig, mEnableDebugger, mDebuggerName);
}

@Override
Expand Down
23 changes: 18 additions & 5 deletions ReactAndroid/src/main/jni/react/hermes/reactexecutor/OnLoad.cpp
Expand Up @@ -69,26 +69,39 @@ class HermesExecutorHolder
"Lcom/facebook/hermes/reactexecutor/HermesExecutor;";

static jni::local_ref<jhybriddata> initHybridDefaultConfig(
jni::alias_ref<jclass>) {
jni::alias_ref<jclass>,
bool enableDebugger,
std::string debuggerName) {
JReactMarker::setLogPerfMarkerIfNeeded();

std::call_once(flag, []() {
facebook::hermes::HermesRuntime::setFatalHandler(hermesFatalHandler);
});
return makeCxxInstance(
std::make_unique<HermesExecutorFactory>(installBindings));
auto factory = std::make_unique<HermesExecutorFactory>(installBindings);
factory->setEnableDebugger(enableDebugger);
if (!debuggerName.empty()) {
factory->setDebuggerName(debuggerName);
}
return makeCxxInstance(std::move(factory));
}

static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
bool enableDebugger,
std::string debuggerName,
jlong heapSizeMB) {
JReactMarker::setLogPerfMarkerIfNeeded();
auto runtimeConfig = makeRuntimeConfig(heapSizeMB);
std::call_once(flag, []() {
facebook::hermes::HermesRuntime::setFatalHandler(hermesFatalHandler);
});
return makeCxxInstance(std::make_unique<HermesExecutorFactory>(
installBindings, JSIExecutor::defaultTimeoutInvoker, runtimeConfig));
auto factory = std::make_unique<HermesExecutorFactory>(
installBindings, JSIExecutor::defaultTimeoutInvoker, runtimeConfig);
factory->setEnableDebugger(enableDebugger);
if (!debuggerName.empty()) {
factory->setDebuggerName(debuggerName);
}
return makeCxxInstance(std::move(factory));
}

static bool canLoadFile(jni::alias_ref<jclass>, const std::string &path) {
Expand Down
35 changes: 28 additions & 7 deletions ReactCommon/hermes/executor/HermesExecutorFactory.cpp
Expand Up @@ -147,20 +147,28 @@ class DecoratedRuntime : public jsi::WithRuntimeDecorator<ReentrancyCheck> {
DecoratedRuntime(
std::unique_ptr<Runtime> runtime,
HermesRuntime &hermesRuntime,
std::shared_ptr<MessageQueueThread> jsQueue)
std::shared_ptr<MessageQueueThread> jsQueue,
bool enableDebugger,
const std::string &debuggerName)
: jsi::WithRuntimeDecorator<ReentrancyCheck>(*runtime, reentrancyCheck_),
runtime_(std::move(runtime)) {
#ifdef HERMES_ENABLE_DEBUGGER
std::shared_ptr<HermesRuntime> rt(runtime_, &hermesRuntime);
auto adapter = std::make_unique<HermesExecutorRuntimeAdapter>(rt, jsQueue);
debugToken_ = facebook::hermes::inspector::chrome::enableDebugging(
std::move(adapter), "Hermes React Native");
enableDebugger_ = enableDebugger;
if (enableDebugger_) {
std::shared_ptr<HermesRuntime> rt(runtime_, &hermesRuntime);
auto adapter =
std::make_unique<HermesExecutorRuntimeAdapter>(rt, jsQueue);
debugToken_ = facebook::hermes::inspector::chrome::enableDebugging(
std::move(adapter), debuggerName);
}
#endif
}

~DecoratedRuntime() {
#ifdef HERMES_ENABLE_DEBUGGER
facebook::hermes::inspector::chrome::disableDebugging(debugToken_);
if (enableDebugger_) {
facebook::hermes::inspector::chrome::disableDebugging(debugToken_);
}
#endif
}

Expand All @@ -175,20 +183,33 @@ class DecoratedRuntime : public jsi::WithRuntimeDecorator<ReentrancyCheck> {
std::shared_ptr<Runtime> runtime_;
ReentrancyCheck reentrancyCheck_;
#ifdef HERMES_ENABLE_DEBUGGER
bool enableDebugger_;
facebook::hermes::inspector::chrome::DebugSessionToken debugToken_;
#endif
};

} // namespace

void HermesExecutorFactory::setEnableDebugger(bool enableDebugger) {
enableDebugger_ = enableDebugger;
}

void HermesExecutorFactory::setDebuggerName(const std::string &debuggerName) {
debuggerName_ = debuggerName;
}

std::unique_ptr<JSExecutor> HermesExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) {
std::unique_ptr<HermesRuntime> hermesRuntime =
makeHermesRuntimeSystraced(runtimeConfig_);
HermesRuntime &hermesRuntimeRef = *hermesRuntime;
auto decoratedRuntime = std::make_shared<DecoratedRuntime>(
std::move(hermesRuntime), hermesRuntimeRef, jsQueue);
std::move(hermesRuntime),
hermesRuntimeRef,
jsQueue,
enableDebugger_,
debuggerName_);

// So what do we have now?
// DecoratedRuntime -> HermesRuntime
Expand Down
6 changes: 6 additions & 0 deletions ReactCommon/hermes/executor/HermesExecutorFactory.h
Expand Up @@ -28,6 +28,10 @@ class HermesExecutorFactory : public JSExecutorFactory {
assert(timeoutInvoker_ && "Should not have empty timeoutInvoker");
}

void setEnableDebugger(bool enableDebugger);

void setDebuggerName(const std::string &debuggerName);

std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
Expand All @@ -38,6 +42,8 @@ class HermesExecutorFactory : public JSExecutorFactory {
JSIExecutor::RuntimeInstaller runtimeInstaller_;
JSIScopedTimeoutInvoker timeoutInvoker_;
::hermes::vm::RuntimeConfig runtimeConfig_;
bool enableDebugger_ = true;
std::string debuggerName_ = "Hermes React Native";
};

class HermesExecutor : public JSIExecutor {
Expand Down

0 comments on commit 32d12e8

Please sign in to comment.