Skip to content

Commit d7f5153

Browse files
cpojerfacebook-github-bot
authored andcommitted
Add Hermes support to React Native on Android (#25613)
Summary: Yesterday we shipped hermesengine.dev as part of the current 0.60 release. This PR brings those changes to master. ## Changelog [General] [Added] - Added support for Hermes Pull Request resolved: #25613 Test Plan: * CI is green both on GitHub and at FB * Creating a new app from source can use Hermes on Android Reviewed By: cpojer Differential Revision: D16221777 Pulled By: willholen fbshipit-source-id: aa6be10537863039cb666292465ba2e1d44b64ef
1 parent fee7f06 commit d7f5153

File tree

106 files changed

+17050
-56
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+17050
-56
lines changed

RNTester/android/app/build.gradle

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ project.ext.react = [
6868
bundleAssetName: "RNTesterApp.android.bundle",
6969
entryFile: file("../../js/RNTesterApp.android.js"),
7070
root: "$rootDir",
71-
inputExcludes: ["android/**", "./**", ".gradle/**"]
71+
inputExcludes: ["android/**", "./**", ".gradle/**"],
72+
composeSourceMapsPath: "$rootDir/scripts/compose-source-maps.js",
73+
hermesCommand: "../../../node_modules/hermesvm/%OS-BIN%/hermes",
74+
enableHermesForVariant: { def v -> v.name.contains("hermes") }
7275
]
7376

7477
apply from: "../../../react.gradle"
@@ -105,6 +108,16 @@ android {
105108
targetCompatibility JavaVersion.VERSION_1_8
106109
}
107110

111+
flavorDimensions "vm"
112+
productFlavors {
113+
hermes {
114+
dimension "vm"
115+
}
116+
jsc {
117+
dimension "vm"
118+
}
119+
}
120+
108121
defaultConfig {
109122
applicationId "com.facebook.react.uiapp"
110123
minSdkVersion 16
@@ -138,6 +151,12 @@ android {
138151
signingConfig signingConfigs.release
139152
}
140153
}
154+
packagingOptions {
155+
pickFirst '**/armeabi-v7a/libc++_shared.so'
156+
pickFirst '**/x86/libc++_shared.so'
157+
pickFirst '**/arm64-v8a/libc++_shared.so'
158+
pickFirst '**/x86_64/libc++_shared.so'
159+
}
141160
}
142161

143162
dependencies {
@@ -146,6 +165,10 @@ dependencies {
146165
// Build React Native from source
147166
implementation project(':ReactAndroid')
148167

168+
def hermesPath = '$projectDir/../../../../node_modules/hermesvm/android/'
169+
debugImplementation files(hermesPath + "hermes-debug.aar")
170+
releaseImplementation files(hermesPath + "hermes-release.aar")
171+
149172
if (useIntlJsc) {
150173
implementation 'org.webkit:android-jsc-intl:+'
151174
} else {

RNTester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.facebook.react.ReactPackage;
1414
import com.facebook.react.shell.MainReactPackage;
1515
import com.facebook.react.views.text.ReactFontManager;
16+
import com.facebook.soloader.SoLoader;
1617
import java.util.Arrays;
1718
import java.util.List;
1819

@@ -44,6 +45,7 @@ public List<ReactPackage> getPackages() {
4445
public void onCreate() {
4546
ReactFontManager.getInstance().addCustomFont(this, "Rubik", R.font.rubik);
4647
super.onCreate();
48+
SoLoader.init(this, /* native exopackage */ false);
4749
}
4850

4951
@Override

ReactAndroid/build.gradle

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,27 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy
9191
into("$thirdPartyNdkDir/folly")
9292
}
9393

94+
task prepareHermes() {
95+
def hermesAAR = file("$projectDir/../node_modules/hermesvm/android/hermes-debug.aar")
96+
if (!hermesAAR.exists()) {
97+
// For an app to build from RN source, hermesvm is located at /path/to/app/node_modules
98+
// and $projectDir is located at /path/to/app/node_modules/react-native/ReactAndroid
99+
hermesAAR = file("$projectDir/../../hermesvm/android/hermes-debug.aar")
100+
101+
if (!hermesAAR.exists()) {
102+
// At Facebook, this file is in a different folder
103+
hermesAAR = file("$projectDir/../../node_modules/hermesvm/android/hermes-debug.aar")
104+
}
105+
}
106+
def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" })
107+
108+
copy {
109+
from soFiles
110+
from "src/main/jni/first-party/hermes/Android.mk"
111+
into "$thirdPartyNdkDir/hermes"
112+
}
113+
}
114+
94115
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
95116
src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
96117
onlyIfNewer(true)
@@ -232,7 +253,7 @@ def getNdkBuildFullPath() {
232253
return ndkBuildFullPath
233254
}
234255

235-
task buildReactNdkLib(dependsOn: [prepareJSC, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
256+
task buildReactNdkLib(dependsOn: [prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
236257
inputs.dir("$projectDir/../ReactCommon")
237258
inputs.dir("src/main/jni")
238259
outputs.dir("$buildDir/react-ndk/all")
@@ -269,6 +290,7 @@ task packageReactNdkLibs(dependsOn: buildReactNdkLib, type: Copy) {
269290
from("$buildDir/react-ndk/all")
270291
into("$buildDir/react-ndk/exported")
271292
exclude("**/libjsc.so")
293+
exclude("**/libhermes.so")
272294
}
273295

274296
task packageReactNdkLibsForBuck(dependsOn: packageReactNdkLibs, type: Copy) {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library")
2+
3+
rn_android_library(
4+
name = "instrumentation",
5+
srcs = glob(["**/*.java"]),
6+
visibility = [
7+
"PUBLIC",
8+
],
9+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the LICENSE
5+
* file in the root directory of this source tree.
6+
*/
7+
#include <fb/fbjni.h>
8+
#include <string>
9+
10+
namespace facebook {
11+
namespace jsi {
12+
namespace jni {
13+
14+
namespace jni = ::facebook::jni;
15+
16+
class HermesMemoryDumper : public jni::JavaClass<HermesMemoryDumper> {
17+
public:
18+
constexpr static auto kJavaDescriptor =
19+
"Lcom/facebook/hermes/instrumentation/HermesMemoryDumper;";
20+
21+
bool shouldSaveSnapshot() {
22+
static auto shouldSaveSnapshotMethod =
23+
javaClassStatic()->getMethod<jboolean()>("shouldSaveSnapshot");
24+
return shouldSaveSnapshotMethod(self());
25+
}
26+
27+
std::string getInternalStorage() {
28+
static auto getInternalStorageMethod =
29+
javaClassStatic()->getMethod<jstring()>("getInternalStorage");
30+
return getInternalStorageMethod(self())->toStdString();
31+
}
32+
33+
std::string getId() {
34+
static auto getInternalStorageMethod =
35+
javaClassStatic()->getMethod<jstring()>("getId");
36+
return getInternalStorageMethod(self())->toStdString();
37+
}
38+
39+
void setMetaData(std::string crashId) {
40+
static auto getIdMethod =
41+
javaClassStatic()->getMethod<void(std::string)>("setMetaData");
42+
getIdMethod(self(), crashId);
43+
}
44+
};
45+
46+
} // namespace jni
47+
} // namespace jsi
48+
} // namespace facebook
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
5+
* directory of this source tree.
6+
*/
7+
package com.facebook.hermes.instrumentation;
8+
9+
public interface HermesMemoryDumper {
10+
boolean shouldSaveSnapshot();
11+
12+
String getInternalStorage();
13+
14+
String getId();
15+
16+
void setMetaData(String crashId);
17+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) Facebook, Inc. and its affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
LOCAL_PATH := $(call my-dir)
7+
8+
include $(CLEAR_VARS)
9+
REACT_NATIVE := $(LOCAL_PATH)/../../../../../../../..
10+
11+
LOCAL_MODULE := hermes-executor-release
12+
13+
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
14+
15+
LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(REACT_NATIVE)/node_modules/hermesvm/android/include $(REACT_NATIVE)/../hermesvm/android/include $(REACT_NATIVE)/../node_modules/hermesvm/include
16+
17+
LOCAL_CPP_FEATURES := exceptions
18+
19+
LOCAL_STATIC_LIBRARIES := libjsireact libjsi
20+
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes
21+
22+
include $(BUILD_SHARED_LIBRARY)
23+
24+
25+
include $(CLEAR_VARS)
26+
REACT_NATIVE := $(LOCAL_PATH)/../../../../../../../..
27+
28+
LOCAL_MODULE := hermes-executor-debug
29+
LOCAL_CFLAGS := -DHERMES_ENABLE_DEBUGGER=1
30+
31+
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
32+
33+
LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(REACT_NATIVE)/node_modules/hermesvm/android/include $(REACT_NATIVE)/../hermesvm/android/include $(REACT_NATIVE)/../node_modules/hermesvm/include
34+
35+
LOCAL_CPP_FEATURES := exceptions
36+
37+
LOCAL_STATIC_LIBRARIES := libjsireact libjsi libhermes-inspector
38+
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes
39+
40+
include $(BUILD_SHARED_LIBRARY)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
5+
* directory of this source tree.
6+
*/
7+
package com.facebook.hermes.reactexecutor;
8+
9+
import com.facebook.hermes.instrumentation.HermesMemoryDumper;
10+
import com.facebook.jni.HybridData;
11+
import com.facebook.react.bridge.JavaScriptExecutor;
12+
import com.facebook.soloader.SoLoader;
13+
import javax.annotation.Nullable;
14+
15+
public class HermesExecutor extends JavaScriptExecutor {
16+
private static String mode_;
17+
18+
static {
19+
// libhermes must be loaded explicitly to invoke its JNI_OnLoad.
20+
SoLoader.loadLibrary("hermes");
21+
try {
22+
SoLoader.loadLibrary("hermes-executor-release");
23+
mode_ = "Release";
24+
} catch (UnsatisfiedLinkError e) {
25+
SoLoader.loadLibrary("hermes-executor-debug");
26+
mode_ = "Debug";
27+
}
28+
}
29+
30+
HermesExecutor(@Nullable RuntimeConfig config) {
31+
super(
32+
config == null
33+
? initHybridDefaultConfig()
34+
: initHybrid(
35+
config.heapSizeMB,
36+
config.es6Symbol,
37+
config.bytecodeWarmupPercent,
38+
config.tripWireEnabled,
39+
config.heapDumper,
40+
config.tripWireCooldownMS,
41+
config.tripWireLimitBytes));
42+
}
43+
44+
@Override
45+
public String getName() {
46+
return "HermesExecutor" + mode_;
47+
}
48+
49+
/**
50+
* Return whether this class can load a file at the given path, based on a binary compatibility
51+
* check between the contents of the file and the Hermes VM.
52+
*
53+
* @param path the path containing the file to inspect.
54+
* @return whether the given file is compatible with the Hermes VM.
55+
*/
56+
public static native boolean canLoadFile(String path);
57+
58+
private static native HybridData initHybridDefaultConfig();
59+
60+
private static native HybridData initHybrid(
61+
long heapSizeMB,
62+
boolean es6Symbol,
63+
int bytecodeWarmupPercent,
64+
boolean tripWireEnabled,
65+
@Nullable HermesMemoryDumper heapDumper,
66+
long tripWireCooldownMS,
67+
long tripWireLimitBytes);
68+
}

0 commit comments

Comments
 (0)