Skip to content

Commit

Permalink
Add Hermes support to React Native on Android (#25613)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
cpojer authored and facebook-github-bot committed Jul 26, 2019
1 parent fee7f06 commit d7f5153
Show file tree
Hide file tree
Showing 106 changed files with 17,050 additions and 56 deletions.
25 changes: 24 additions & 1 deletion RNTester/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ project.ext.react = [
bundleAssetName: "RNTesterApp.android.bundle",
entryFile: file("../../js/RNTesterApp.android.js"),
root: "$rootDir",
inputExcludes: ["android/**", "./**", ".gradle/**"]
inputExcludes: ["android/**", "./**", ".gradle/**"],
composeSourceMapsPath: "$rootDir/scripts/compose-source-maps.js",
hermesCommand: "../../../node_modules/hermesvm/%OS-BIN%/hermes",
enableHermesForVariant: { def v -> v.name.contains("hermes") }
]

apply from: "../../../react.gradle"
Expand Down Expand Up @@ -105,6 +108,16 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

flavorDimensions "vm"
productFlavors {
hermes {
dimension "vm"
}
jsc {
dimension "vm"
}
}

defaultConfig {
applicationId "com.facebook.react.uiapp"
minSdkVersion 16
Expand Down Expand Up @@ -138,6 +151,12 @@ android {
signingConfig signingConfigs.release
}
}
packagingOptions {
pickFirst '**/armeabi-v7a/libc++_shared.so'
pickFirst '**/x86/libc++_shared.so'
pickFirst '**/arm64-v8a/libc++_shared.so'
pickFirst '**/x86_64/libc++_shared.so'
}
}

dependencies {
Expand All @@ -146,6 +165,10 @@ dependencies {
// Build React Native from source
implementation project(':ReactAndroid')

def hermesPath = '$projectDir/../../../../node_modules/hermesvm/android/'
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")

if (useIntlJsc) {
implementation 'org.webkit:android-jsc-intl:+'
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.views.text.ReactFontManager;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;

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

@Override
Expand Down
24 changes: 23 additions & 1 deletion ReactAndroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,27 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy
into("$thirdPartyNdkDir/folly")
}

task prepareHermes() {
def hermesAAR = file("$projectDir/../node_modules/hermesvm/android/hermes-debug.aar")
if (!hermesAAR.exists()) {
// For an app to build from RN source, hermesvm is located at /path/to/app/node_modules
// and $projectDir is located at /path/to/app/node_modules/react-native/ReactAndroid
hermesAAR = file("$projectDir/../../hermesvm/android/hermes-debug.aar")

if (!hermesAAR.exists()) {
// At Facebook, this file is in a different folder
hermesAAR = file("$projectDir/../../node_modules/hermesvm/android/hermes-debug.aar")
}
}
def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" })

copy {
from soFiles
from "src/main/jni/first-party/hermes/Android.mk"
into "$thirdPartyNdkDir/hermes"
}
}

task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
onlyIfNewer(true)
Expand Down Expand Up @@ -232,7 +253,7 @@ def getNdkBuildFullPath() {
return ndkBuildFullPath
}

task buildReactNdkLib(dependsOn: [prepareJSC, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
task buildReactNdkLib(dependsOn: [prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
inputs.dir("$projectDir/../ReactCommon")
inputs.dir("src/main/jni")
outputs.dir("$buildDir/react-ndk/all")
Expand Down Expand Up @@ -269,6 +290,7 @@ task packageReactNdkLibs(dependsOn: buildReactNdkLib, type: Copy) {
from("$buildDir/react-ndk/all")
into("$buildDir/react-ndk/exported")
exclude("**/libjsc.so")
exclude("**/libhermes.so")
}

task packageReactNdkLibsForBuck(dependsOn: packageReactNdkLibs, type: Copy) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library")

rn_android_library(
name = "instrumentation",
srcs = glob(["**/*.java"]),
visibility = [
"PUBLIC",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#include <fb/fbjni.h>
#include <string>

namespace facebook {
namespace jsi {
namespace jni {

namespace jni = ::facebook::jni;

class HermesMemoryDumper : public jni::JavaClass<HermesMemoryDumper> {
public:
constexpr static auto kJavaDescriptor =
"Lcom/facebook/hermes/instrumentation/HermesMemoryDumper;";

bool shouldSaveSnapshot() {
static auto shouldSaveSnapshotMethod =
javaClassStatic()->getMethod<jboolean()>("shouldSaveSnapshot");
return shouldSaveSnapshotMethod(self());
}

std::string getInternalStorage() {
static auto getInternalStorageMethod =
javaClassStatic()->getMethod<jstring()>("getInternalStorage");
return getInternalStorageMethod(self())->toStdString();
}

std::string getId() {
static auto getInternalStorageMethod =
javaClassStatic()->getMethod<jstring()>("getId");
return getInternalStorageMethod(self())->toStdString();
}

void setMetaData(std::string crashId) {
static auto getIdMethod =
javaClassStatic()->getMethod<void(std::string)>("setMetaData");
getIdMethod(self(), crashId);
}
};

} // namespace jni
} // namespace jsi
} // namespace facebook
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.facebook.hermes.instrumentation;

public interface HermesMemoryDumper {
boolean shouldSaveSnapshot();

String getInternalStorage();

String getId();

void setMetaData(String crashId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
REACT_NATIVE := $(LOCAL_PATH)/../../../../../../../..

LOCAL_MODULE := hermes-executor-release

LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)

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

LOCAL_CPP_FEATURES := exceptions

LOCAL_STATIC_LIBRARIES := libjsireact libjsi
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes

include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
REACT_NATIVE := $(LOCAL_PATH)/../../../../../../../..

LOCAL_MODULE := hermes-executor-debug
LOCAL_CFLAGS := -DHERMES_ENABLE_DEBUGGER=1

LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)

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

LOCAL_CPP_FEATURES := exceptions

LOCAL_STATIC_LIBRARIES := libjsireact libjsi libhermes-inspector
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes

include $(BUILD_SHARED_LIBRARY)
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.facebook.hermes.reactexecutor;

import com.facebook.hermes.instrumentation.HermesMemoryDumper;
import com.facebook.jni.HybridData;
import com.facebook.react.bridge.JavaScriptExecutor;
import com.facebook.soloader.SoLoader;
import javax.annotation.Nullable;

public class HermesExecutor extends JavaScriptExecutor {
private static String mode_;

static {
// libhermes must be loaded explicitly to invoke its JNI_OnLoad.
SoLoader.loadLibrary("hermes");
try {
SoLoader.loadLibrary("hermes-executor-release");
mode_ = "Release";
} catch (UnsatisfiedLinkError e) {
SoLoader.loadLibrary("hermes-executor-debug");
mode_ = "Debug";
}
}

HermesExecutor(@Nullable RuntimeConfig config) {
super(
config == null
? initHybridDefaultConfig()
: initHybrid(
config.heapSizeMB,
config.es6Symbol,
config.bytecodeWarmupPercent,
config.tripWireEnabled,
config.heapDumper,
config.tripWireCooldownMS,
config.tripWireLimitBytes));
}

@Override
public String getName() {
return "HermesExecutor" + mode_;
}

/**
* Return whether this class can load a file at the given path, based on a binary compatibility
* check between the contents of the file and the Hermes VM.
*
* @param path the path containing the file to inspect.
* @return whether the given file is compatible with the Hermes VM.
*/
public static native boolean canLoadFile(String path);

private static native HybridData initHybridDefaultConfig();

private static native HybridData initHybrid(
long heapSizeMB,
boolean es6Symbol,
int bytecodeWarmupPercent,
boolean tripWireEnabled,
@Nullable HermesMemoryDumper heapDumper,
long tripWireCooldownMS,
long tripWireLimitBytes);
}
Loading

0 comments on commit d7f5153

Please sign in to comment.