Skip to content

Commit c9e5f64

Browse files
Kudocortinico
andauthored
chore: add android implementations (#2)
- copy jsc related code from react-native core into this repo - the so file name is libjscruntimefactory.so and basically merge JSCExecutorFactory and JSCInstance together without using so merging. --------- Co-authored-by: Nicola Corti <corti.nico@gmail.com>
1 parent 1e4c54e commit c9e5f64

File tree

12 files changed

+492
-0
lines changed

12 files changed

+492
-0
lines changed
File renamed without changes.

android/.gitkeep

Whitespace-only changes.

android/CMakeLists.txt

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#
2+
# Copyright (c) react-native-community
3+
#
4+
# This source code is licensed under the MIT license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
#
7+
8+
cmake_minimum_required(VERSION 3.13)
9+
project(jscruntimefactory)
10+
11+
set(CMAKE_VERBOSE_MAKEFILE ON)
12+
set (CMAKE_CXX_STANDARD 20)
13+
14+
string(APPEND CMAKE_CXX_FLAGS
15+
" -fexceptions"
16+
" -frtti"
17+
" -O3"
18+
" -Wno-unused-lambda-capture"
19+
" -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}"
20+
" -DREACT_NATIVE_PATCH_VERSION=${REACT_NATIVE_PATCH_VERSION}"
21+
)
22+
23+
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
24+
add_compile_options(${folly_FLAGS})
25+
26+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
27+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
28+
29+
set(PACKAGE_NAME "jscruntimefactory")
30+
set(SRC_DIR "${CMAKE_SOURCE_DIR}/src/main/cpp")
31+
32+
set(COMMON_DIR "${CMAKE_SOURCE_DIR}/../common")
33+
file(GLOB COMMON_SOURCES "${COMMON_DIR}/*.cpp")
34+
35+
add_library(
36+
${PACKAGE_NAME}
37+
SHARED
38+
${COMMON_SOURCES}
39+
"${SRC_DIR}/JSCExecutorFactory.cpp"
40+
"${SRC_DIR}/OnLoad.cpp"
41+
)
42+
43+
# includes
44+
45+
target_include_directories(
46+
${PACKAGE_NAME}
47+
PRIVATE
48+
"${COMMON_DIR}"
49+
"${REACT_NATIVE_DIR}/ReactCommon"
50+
"${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor"
51+
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni"
52+
)
53+
54+
# find libraries
55+
56+
find_library(
57+
LOG_LIB
58+
log
59+
)
60+
find_package(fbjni REQUIRED CONFIG)
61+
find_package(jsc-android REQUIRED CONFIG)
62+
find_package(ReactAndroid REQUIRED CONFIG)
63+
64+
# link to shared libraries
65+
66+
target_link_libraries(
67+
${PACKAGE_NAME}
68+
${LOG_LIB}
69+
android
70+
fbjni::fbjni
71+
jsc-android::jsc
72+
ReactAndroid::jsi
73+
ReactAndroid::reactnative
74+
)

android/build.gradle.kts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright (c) react-native-community
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import com.android.build.gradle.internal.tasks.factory.dependsOn
9+
import groovy.json.JsonSlurper
10+
import org.apache.tools.ant.taskdefs.condition.Os
11+
import java.util.Properties
12+
13+
val reactNativeDir = findNodePackageDir("react-native")
14+
val reactNativeManifest = file("${reactNativeDir}/package.json")
15+
val reactNativeManifestAsJson = JsonSlurper().parseText(reactNativeManifest.readText()) as Map<*, *>
16+
val reactNativeVersion = reactNativeManifestAsJson["version"] as String
17+
val (major, minor, patch) = reactNativeVersion.split(".")
18+
val rnMinorVersion = minor.toInt()
19+
val rnPatchVersion = patch.toInt()
20+
val prefabHeadersDir = file("${layout.buildDirectory.get()}/prefab-headers")
21+
22+
if (findProperty("hermesEnabled") == "true") {
23+
throw GradleException("Please disable Hermes because Hermes will transform the JavaScript bundle as bytecode bundle.\n")
24+
}
25+
26+
val reactProperties = Properties().apply {
27+
file("${reactNativeDir}/ReactAndroid/gradle.properties").inputStream().use { load(it) }
28+
}
29+
30+
plugins {
31+
id("com.android.library")
32+
id("org.jetbrains.kotlin.android")
33+
}
34+
35+
android {
36+
namespace = "io.github.reactnativecommunity.javascriptcore"
37+
compileSdk = safeExtGet("compileSdkVersion", 35)
38+
39+
if (rootProject.hasProperty("ndkPath")) {
40+
ndkPath = rootProject.extra["ndkPath"].toString()
41+
}
42+
if (rootProject.hasProperty("ndkVersion")) {
43+
ndkVersion = rootProject.extra["ndkVersion"].toString()
44+
}
45+
46+
defaultConfig {
47+
minSdk = safeExtGet("minSdkVersion", 24)
48+
49+
@Suppress("UnstableApiUsage")
50+
externalNativeBuild.cmake {
51+
arguments += listOf(
52+
"-DANDROID_STL=c++_shared",
53+
"-DREACT_NATIVE_DIR=${reactNativeDir.toPlatformString()}",
54+
"-DREACT_NATIVE_MINOR_VERSION=${rnMinorVersion}",
55+
"-DREACT_NATIVE_PATCH_VERSION=${rnPatchVersion}",
56+
)
57+
targets("jscruntimefactory")
58+
abiFilters(*reactNativeArchitectures().toTypedArray())
59+
}
60+
}
61+
62+
externalNativeBuild {
63+
cmake {
64+
path("CMakeLists.txt")
65+
}
66+
}
67+
68+
lint {
69+
abortOnError = false
70+
targetSdk = safeExtGet("targetSdkVersion", 35)
71+
}
72+
73+
packaging {
74+
// Uncomment to keep debug symbols
75+
// doNotStrip.add("**/*.so")
76+
77+
jniLibs {
78+
excludes.add("**/libc++_shared.so")
79+
excludes.add("**/libfbjni.so")
80+
excludes.add("**/libjsi.so")
81+
excludes.add("**/libreactnative.so")
82+
pickFirsts.add("**/libjscruntimefactory.so")
83+
}
84+
}
85+
86+
buildFeatures {
87+
prefab = true
88+
prefabPublishing = true
89+
}
90+
91+
prefab {
92+
register("jscruntimefactory") {
93+
headers = file(prefabHeadersDir).absolutePath
94+
}
95+
}
96+
}
97+
98+
dependencies {
99+
implementation("com.facebook.yoga:proguard-annotations:1.19.0")
100+
compileOnly("com.facebook.fbjni:fbjni:0.7.0")
101+
implementation("com.facebook.react:react-android")
102+
103+
//noinspection GradleDynamicVersion
104+
implementation("io.github.react-native-community:jsc-android:2026004.+")
105+
}
106+
107+
val createPrefabHeadersDir by
108+
tasks.registering {
109+
prefabHeadersDir.mkdirs()
110+
}
111+
112+
tasks.named("preBuild").dependsOn(createPrefabHeadersDir)
113+
114+
private fun findNodePackageDir(packageName: String, absolute: Boolean = true): File {
115+
val nodeCommand = listOf("node", "--print", "require.resolve('${packageName}/package.json')")
116+
val proc = ProcessBuilder(nodeCommand)
117+
.directory(rootDir)
118+
.start()
119+
val error = proc.errorStream.bufferedReader().readText()
120+
if (error.isNotEmpty()) {
121+
val nodeCommandString = nodeCommand.joinToString(" ")
122+
throw GradleException(
123+
"findNodePackageDir() execution failed - nodeCommand[$nodeCommandString]\n$error"
124+
)
125+
}
126+
val dir = File(proc.inputStream.bufferedReader().readText().trim()).parentFile
127+
return if (absolute) dir.absoluteFile else dir
128+
}
129+
130+
private fun File.toPlatformString(): String {
131+
var result = path.toString()
132+
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
133+
result = result.replace(File.separatorChar, '/')
134+
}
135+
return result
136+
}
137+
138+
private fun reactNativeArchitectures(): List<String> {
139+
val value = findProperty("reactNativeArchitectures")
140+
return value?.toString()?.split(",") ?: listOf("armeabi-v7a", "x86", "x86_64", "arm64-v8a")
141+
}
142+
143+
private fun <T> safeExtGet(prop: String, fallback: T): T {
144+
@Suppress("UNCHECKED_CAST")
145+
return rootProject.extra[prop] as? T ?: fallback
146+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "JSCExecutorFactory.h"
9+
10+
#include <fbjni/fbjni.h>
11+
#include <react/jni/JReactMarker.h>
12+
#include <react/jni/JSLogging.h>
13+
14+
namespace facebook::react {
15+
16+
namespace {
17+
18+
class JSCExecutorFactory : public JSExecutorFactory {
19+
public:
20+
std::unique_ptr<JSExecutor>
21+
createJSExecutor(std::shared_ptr<ExecutorDelegate> delegate,
22+
std::shared_ptr<MessageQueueThread> jsQueue) override {
23+
auto installBindings = [](jsi::Runtime &runtime) {
24+
react::Logger androidLogger =
25+
static_cast<void (*)(const std::string &, unsigned int)>(
26+
&reactAndroidLoggingHook);
27+
react::bindNativeLogger(runtime, androidLogger);
28+
};
29+
return std::make_unique<JSIExecutor>(jsc::makeJSCRuntime(), delegate,
30+
JSIExecutor::defaultTimeoutInvoker,
31+
installBindings);
32+
}
33+
};
34+
35+
} // namespace
36+
37+
// static
38+
jni::local_ref<JSCExecutorHolder::jhybriddata>
39+
JSCExecutorHolder::initHybrid(jni::alias_ref<jclass>, ReadableNativeMap *) {
40+
// This is kind of a weird place for stuff, but there's no other
41+
// good place for initialization which is specific to JSC on
42+
// Android.
43+
JReactMarker::setLogPerfMarkerIfNeeded();
44+
// TODO mhorowitz T28461666 fill in some missing nice to have glue
45+
return makeCxxInstance(std::make_unique<JSCExecutorFactory>());
46+
}
47+
48+
} // namespace facebook::react
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <fbjni/fbjni.h>
11+
#include <jsireact/JSIExecutor.h>
12+
#include <react/jni/JavaScriptExecutorHolder.h>
13+
#include <react/jni/ReadableNativeMap.h>
14+
15+
#include "JSCRuntime.h"
16+
17+
namespace facebook::react {
18+
19+
// This is not like JSCJavaScriptExecutor, which calls JSC directly. This uses
20+
// JSIExecutor with JSCRuntime.
21+
class JSCExecutorHolder
22+
: public jni::HybridClass<JSCExecutorHolder, JavaScriptExecutorHolder> {
23+
public:
24+
static constexpr auto kJavaDescriptor =
25+
"Lio/github/reactnativecommunity/javascriptcore/JSCExecutor;";
26+
27+
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>,
28+
ReadableNativeMap *);
29+
30+
static void registerNatives() {
31+
registerHybrid({
32+
makeNativeMethod("initHybrid", JSCExecutorHolder::initHybrid),
33+
});
34+
}
35+
36+
private:
37+
friend HybridBase;
38+
using HybridBase::HybridBase;
39+
};
40+
41+
} // namespace facebook::react
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <fbjni/fbjni.h>
11+
#include <jsi/jsi.h>
12+
#include <react/runtime/JSRuntimeFactory.h>
13+
#include <react/runtime/jni/JJSRuntimeFactory.h>
14+
15+
#include "JSCRuntime.h"
16+
17+
namespace facebook::react {
18+
19+
class JSCRuntimeFactory
20+
: public jni::HybridClass<JSCRuntimeFactory, JJSRuntimeFactory> {
21+
public:
22+
static constexpr auto kJavaDescriptor =
23+
"Lio/github/reactnativecommunity/javascriptcore/JSCRuntimeFactory;";
24+
25+
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>) {
26+
return makeCxxInstance();
27+
}
28+
29+
static void registerNatives() {
30+
registerHybrid({
31+
makeNativeMethod("initHybrid", JSCRuntimeFactory::initHybrid),
32+
});
33+
}
34+
35+
std::unique_ptr<JSRuntime>
36+
createJSRuntime(std::shared_ptr<MessageQueueThread> msgQueueThread) noexcept {
37+
return std::make_unique<JSIRuntimeHolder>(jsc::makeJSCRuntime());
38+
}
39+
40+
private:
41+
friend HybridBase;
42+
using HybridBase::HybridBase;
43+
};
44+
45+
} // namespace facebook::react

android/src/main/cpp/OnLoad.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <fbjni/fbjni.h>
9+
10+
#include "JSCExecutorFactory.h"
11+
#include "JSCRuntimeFactory.h"
12+
13+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
14+
return facebook::jni::initialize(vm, [] {
15+
facebook::react::JSCExecutorHolder::registerNatives();
16+
facebook::react::JSCRuntimeFactory::registerNatives();
17+
});
18+
}

0 commit comments

Comments
 (0)