@react-native-native/nativ-fabric 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NativFabric.podspec +41 -0
- package/android/build.gradle +128 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/CMakeLists.txt +59 -0
- package/android/src/main/cpp/NativBindingsInstaller.cpp +393 -0
- package/android/src/main/cpp/NativRuntime.cpp +508 -0
- package/android/src/main/java/com/nativfabric/ComposeHost.kt +26 -0
- package/android/src/main/java/com/nativfabric/NativContainerPackage.kt +35 -0
- package/android/src/main/java/com/nativfabric/NativContainerView.kt +51 -0
- package/android/src/main/java/com/nativfabric/NativContainerViewManager.kt +62 -0
- package/android/src/main/java/com/nativfabric/NativRuntime.kt +201 -0
- package/android/src/main/java/com/nativfabric/NativRuntimeModule.kt +37 -0
- package/android/src/main/java/com/nativfabric/compose/ComposeWrappers.kt +45 -0
- package/app.plugin.js +159 -0
- package/expo-module.config.json +6 -0
- package/ios/NativContainerComponentView.mm +137 -0
- package/ios/NativRuntime.h +36 -0
- package/ios/NativRuntime.mm +549 -0
- package/metro/Nativ.h +126 -0
- package/metro/compilers/android-compiler.js +339 -0
- package/metro/compilers/dylib-compiler.js +474 -0
- package/metro/compilers/kotlin-compiler.js +632 -0
- package/metro/compilers/rust-compiler.js +722 -0
- package/metro/compilers/static-compiler.js +1118 -0
- package/metro/compilers/swift-compiler.js +363 -0
- package/metro/extractors/cpp-ast-extractor.js +126 -0
- package/metro/extractors/kotlin-extractor.js +125 -0
- package/metro/extractors/rust-extractor.js +118 -0
- package/metro/index.js +236 -0
- package/metro/transformer.js +649 -0
- package/metro/utils/bridge-generator.js +50 -0
- package/metro/utils/compile-commands.js +104 -0
- package/metro/utils/cpp-daemon.js +344 -0
- package/metro/utils/dts-generator.js +32 -0
- package/metro/utils/include-resolver.js +73 -0
- package/metro/utils/kotlin-daemon.js +394 -0
- package/metro/utils/type-mapper.js +63 -0
- package/package.json +43 -0
- package/react-native.config.js +13 -0
- package/src/NativContainerNativeComponent.ts +9 -0
- package/src/NativeNativRuntime.ts +8 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Pod::Spec.new do |s|
|
|
2
|
+
s.name = 'NativFabric'
|
|
3
|
+
s.version = '0.1.0'
|
|
4
|
+
s.summary = 'React Native Native — native component rendering + JSI runtime'
|
|
5
|
+
s.author = 'Kim Brandwijk'
|
|
6
|
+
s.homepage = 'https://react-native-native.github.io'
|
|
7
|
+
s.license = { :type => 'MIT' }
|
|
8
|
+
s.platforms = { :ios => '15.1' }
|
|
9
|
+
s.source = { git: '' }
|
|
10
|
+
s.static_framework = true
|
|
11
|
+
|
|
12
|
+
s.dependency 'React-Core'
|
|
13
|
+
s.dependency 'React-RCTFabric'
|
|
14
|
+
s.dependency 'React-Fabric'
|
|
15
|
+
s.dependency 'React-jsi'
|
|
16
|
+
s.dependency 'React-utils'
|
|
17
|
+
s.dependency 'React-graphics'
|
|
18
|
+
s.dependency 'React-rendererdebug'
|
|
19
|
+
s.dependency 'ReactCommon/turbomodule/core'
|
|
20
|
+
s.dependency 'ReactCodegen'
|
|
21
|
+
s.dependency 'Yoga'
|
|
22
|
+
|
|
23
|
+
s.pod_target_xcconfig = {
|
|
24
|
+
'DEFINES_MODULE' => 'YES',
|
|
25
|
+
'OTHER_LDFLAGS' => '$(inherited) -lc++',
|
|
26
|
+
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
|
|
27
|
+
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Public/React-Fabric" ' \
|
|
28
|
+
'"$(PODS_ROOT)/Headers/Public/React-graphics" ' \
|
|
29
|
+
'"$(PODS_ROOT)/Headers/Public/ReactCodegen" ' \
|
|
30
|
+
'"$(PODS_ROOT)/Headers/Private/React-Core" ' \
|
|
31
|
+
'"$(PODS_ROOT)/Headers/Private/Yoga" ' \
|
|
32
|
+
'"${PODS_TARGET_SRCROOT}/ios/generated"',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
s.source_files = [
|
|
36
|
+
"ios/NativContainerComponentView.mm",
|
|
37
|
+
"ios/NativRuntime.h",
|
|
38
|
+
"ios/NativRuntime.mm",
|
|
39
|
+
"ios/generated/**/*.{h,cpp}",
|
|
40
|
+
]
|
|
41
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.NativFabric = [
|
|
3
|
+
kotlinVersion: "2.1.20",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36,
|
|
6
|
+
targetSdkVersion: 36
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
ext.getExtOrDefault = { prop ->
|
|
10
|
+
if (rootProject.ext.has(prop)) {
|
|
11
|
+
return rootProject.ext.get(prop)
|
|
12
|
+
}
|
|
13
|
+
return NativFabric[prop]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
repositories {
|
|
17
|
+
google()
|
|
18
|
+
mavenCentral()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
dependencies {
|
|
22
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
23
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
24
|
+
classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
apply plugin: "com.android.library"
|
|
29
|
+
apply plugin: "kotlin-android"
|
|
30
|
+
apply plugin: "org.jetbrains.kotlin.plugin.compose"
|
|
31
|
+
apply plugin: "com.facebook.react"
|
|
32
|
+
|
|
33
|
+
android {
|
|
34
|
+
namespace "com.nativfabric"
|
|
35
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
36
|
+
|
|
37
|
+
defaultConfig {
|
|
38
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
39
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion")
|
|
40
|
+
|
|
41
|
+
externalNativeBuild {
|
|
42
|
+
cmake {
|
|
43
|
+
cppFlags "-std=c++17"
|
|
44
|
+
arguments "-DANDROID_STL=c++_shared"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
externalNativeBuild {
|
|
50
|
+
cmake {
|
|
51
|
+
path "src/main/cpp/CMakeLists.txt"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
buildFeatures {
|
|
56
|
+
buildConfig true
|
|
57
|
+
prefab true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
buildTypes {
|
|
61
|
+
release {
|
|
62
|
+
minifyEnabled false
|
|
63
|
+
// Pass NATIV_RELEASE and app root to CMake
|
|
64
|
+
externalNativeBuild {
|
|
65
|
+
cmake {
|
|
66
|
+
arguments "-DNATIV_RELEASE=1", "-DNATIV_APP_ROOT=${rootProject.projectDir.parentFile.absolutePath}"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
compileOptions {
|
|
73
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
74
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
kotlinOptions {
|
|
78
|
+
jvmTarget = "17"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
sourceSets {
|
|
82
|
+
main {
|
|
83
|
+
java.srcDirs += ['src/main/java']
|
|
84
|
+
}
|
|
85
|
+
// Production: include generated Kotlin wrappers compiled by Gradle (not DexClassLoader)
|
|
86
|
+
release {
|
|
87
|
+
java.srcDirs += ["${rootProject.projectDir.parentFile.absolutePath}/.nativ/generated/kotlin-src"]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ─── Static compiler: generate bridges + compile Rust for release ─────
|
|
93
|
+
// Runs before Kotlin/CMake compilation so generated sources are available.
|
|
94
|
+
tasks.register("buildNativBridges", Exec) {
|
|
95
|
+
def projectRoot = rootProject.projectDir.parentFile.absolutePath
|
|
96
|
+
def compilerScript = file("${projectDir}/../metro/compilers/static-compiler.js").absolutePath
|
|
97
|
+
|
|
98
|
+
commandLine "node", compilerScript,
|
|
99
|
+
"--platform", "android",
|
|
100
|
+
"--root", projectRoot
|
|
101
|
+
|
|
102
|
+
// Only run for release builds
|
|
103
|
+
onlyIf { gradle.startParameter.taskNames.any { it.toLowerCase().contains("release") } }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Wire into release build: run before Kotlin compile and CMake configure
|
|
107
|
+
afterEvaluate {
|
|
108
|
+
tasks.matching { it.name == "compileReleaseKotlin" }.configureEach {
|
|
109
|
+
dependsOn "buildNativBridges"
|
|
110
|
+
}
|
|
111
|
+
tasks.matching { it.name.startsWith("configureCMake") && it.name.contains("Rel") }.configureEach {
|
|
112
|
+
dependsOn "buildNativBridges"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
repositories {
|
|
117
|
+
google()
|
|
118
|
+
mavenCentral()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
dependencies {
|
|
122
|
+
implementation "com.facebook.react:react-android"
|
|
123
|
+
implementation "com.facebook.fbjni:fbjni"
|
|
124
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:${getExtOrDefault('kotlinVersion')}"
|
|
125
|
+
implementation "androidx.compose.ui:ui:1.7.0"
|
|
126
|
+
implementation "androidx.compose.material3:material3:1.3.0"
|
|
127
|
+
implementation "androidx.compose.ui:ui-tooling-preview:1.7.0"
|
|
128
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.13)
|
|
2
|
+
project(nativruntime)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
+
|
|
6
|
+
# Map ANDROID_ABI to prefab triple
|
|
7
|
+
if(ANDROID_ABI STREQUAL "arm64-v8a")
|
|
8
|
+
set(_TRIPLE "aarch64-linux-android")
|
|
9
|
+
elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
|
|
10
|
+
set(_TRIPLE "arm-linux-androideabi")
|
|
11
|
+
elseif(ANDROID_ABI STREQUAL "x86_64")
|
|
12
|
+
set(_TRIPLE "x86_64-linux-android")
|
|
13
|
+
elseif(ANDROID_ABI STREQUAL "x86")
|
|
14
|
+
set(_TRIPLE "i686-linux-android")
|
|
15
|
+
endif()
|
|
16
|
+
|
|
17
|
+
# Include prefab Config files
|
|
18
|
+
list(GET CMAKE_FIND_ROOT_PATH 0 _PREFAB_ROOT)
|
|
19
|
+
set(_PREFAB "${_PREFAB_ROOT}/lib/${_TRIPLE}/cmake")
|
|
20
|
+
include("${_PREFAB}/fbjni/fbjniConfig.cmake")
|
|
21
|
+
include("${_PREFAB}/ReactAndroid/ReactAndroidConfig.cmake")
|
|
22
|
+
|
|
23
|
+
add_library(nativruntime SHARED
|
|
24
|
+
NativRuntime.cpp
|
|
25
|
+
NativBindingsInstaller.cpp
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
target_link_libraries(nativruntime
|
|
29
|
+
fbjni::fbjni
|
|
30
|
+
ReactAndroid::jsi
|
|
31
|
+
ReactAndroid::reactnative
|
|
32
|
+
android
|
|
33
|
+
log
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Production: statically link user native code into libnativruntime.so.
|
|
37
|
+
# NATIV_APP_ROOT is passed from build.gradle — the user's project root (parent of android/).
|
|
38
|
+
if(NATIV_RELEASE)
|
|
39
|
+
target_compile_definitions(nativruntime PRIVATE NATIV_RELEASE=1)
|
|
40
|
+
|
|
41
|
+
file(GLOB_RECURSE NATIV_USER_SOURCES
|
|
42
|
+
"${NATIV_APP_ROOT}/.nativ/generated/bridges/android/*.cpp"
|
|
43
|
+
"${NATIV_APP_ROOT}/.nativ/generated/bridges/android/*.c"
|
|
44
|
+
)
|
|
45
|
+
if(NATIV_USER_SOURCES)
|
|
46
|
+
target_sources(nativruntime PRIVATE ${NATIV_USER_SOURCES})
|
|
47
|
+
endif()
|
|
48
|
+
|
|
49
|
+
# Include paths: user project root + Nativ.h location
|
|
50
|
+
target_include_directories(nativruntime PRIVATE
|
|
51
|
+
"${NATIV_APP_ROOT}"
|
|
52
|
+
"${CMAKE_SOURCE_DIR}/../../../../metro"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
set(NATIV_RUST_LIB "${NATIV_APP_ROOT}/.nativ/generated/release/${ANDROID_ABI}/libnativ_user.a")
|
|
56
|
+
if(EXISTS ${NATIV_RUST_LIB})
|
|
57
|
+
target_link_libraries(nativruntime -Wl,--whole-archive ${NATIV_RUST_LIB} -Wl,--no-whole-archive)
|
|
58
|
+
endif()
|
|
59
|
+
endif()
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/// NativBindingsInstaller — installs global.__nativ via TurboModuleWithJSIBindings.
|
|
2
|
+
/// Self-contained in nativ-fabric. Self-contained.
|
|
3
|
+
|
|
4
|
+
#include <fbjni/fbjni.h>
|
|
5
|
+
#include <jsi/jsi.h>
|
|
6
|
+
#include <ReactCommon/BindingsInstallerHolder.h>
|
|
7
|
+
#include <ReactCommon/CallInvoker.h>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <thread>
|
|
10
|
+
#include <android/log.h>
|
|
11
|
+
#include <dlfcn.h>
|
|
12
|
+
|
|
13
|
+
using namespace facebook;
|
|
14
|
+
|
|
15
|
+
#define NATIV_LOG(...) __android_log_print(ANDROID_LOG_INFO, "NativRuntime", __VA_ARGS__)
|
|
16
|
+
|
|
17
|
+
// Defined in NativRuntime.cpp (same .so)
|
|
18
|
+
extern "C" const char* nativ_call_sync(const char*, const char*, const char*);
|
|
19
|
+
typedef void (*NativAsyncFn)(const char*, void (*)(const char*), void (*)(const char*, const char*));
|
|
20
|
+
extern "C" NativAsyncFn nativ_get_async_fn(const char*, const char*);
|
|
21
|
+
|
|
22
|
+
static jsi::Runtime* g_runtime = nullptr;
|
|
23
|
+
static std::shared_ptr<react::CallInvoker> g_callInvoker = nullptr;
|
|
24
|
+
|
|
25
|
+
static void installNativ(jsi::Runtime &rt) {
|
|
26
|
+
auto nativ = jsi::Object(rt);
|
|
27
|
+
|
|
28
|
+
// __nativ.callSync(moduleId, fnName, argsJson) → string
|
|
29
|
+
using CallSyncFn = const char* (*)(const char*, const char*, const char*);
|
|
30
|
+
nativ.setProperty(rt, "callSync", jsi::Function::createFromHostFunction(
|
|
31
|
+
rt, jsi::PropNameID::forAscii(rt, "callSync"), 3,
|
|
32
|
+
[](jsi::Runtime &rt, const jsi::Value &,
|
|
33
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
34
|
+
if (count < 3) return jsi::Value::null();
|
|
35
|
+
|
|
36
|
+
auto moduleId = args[0].getString(rt).utf8(rt);
|
|
37
|
+
auto fnName = args[1].getString(rt).utf8(rt);
|
|
38
|
+
auto argsJson = args[2].getString(rt).utf8(rt);
|
|
39
|
+
|
|
40
|
+
// Try C registry first (Rust/C++ .so modules)
|
|
41
|
+
const char* result = nativ_call_sync(moduleId.c_str(), fnName.c_str(), argsJson.c_str());
|
|
42
|
+
if (result) return jsi::Value(rt, jsi::String::createFromUtf8(rt, result));
|
|
43
|
+
|
|
44
|
+
// Fall back to Kotlin dispatch (.dex modules)
|
|
45
|
+
JNIEnv *env = jni::Environment::current();
|
|
46
|
+
if (env && env->PushLocalFrame(16) == 0) {
|
|
47
|
+
jclass rtClass = env->FindClass("com/nativfabric/NativRuntime");
|
|
48
|
+
if (rtClass) {
|
|
49
|
+
jfieldID instField = env->GetStaticFieldID(rtClass, "INSTANCE", "Lcom/nativfabric/NativRuntime;");
|
|
50
|
+
jobject rtInst = instField ? env->GetStaticObjectField(rtClass, instField) : nullptr;
|
|
51
|
+
if (rtInst) {
|
|
52
|
+
jmethodID callKt = env->GetMethodID(rtClass, "callKotlin",
|
|
53
|
+
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
|
|
54
|
+
if (callKt) {
|
|
55
|
+
jstring jMod = env->NewStringUTF(moduleId.c_str());
|
|
56
|
+
jstring jFn = env->NewStringUTF(fnName.c_str());
|
|
57
|
+
jstring jArgs = env->NewStringUTF(argsJson.c_str());
|
|
58
|
+
auto jResult = (jstring)env->CallObjectMethod(rtInst, callKt, jMod, jFn, jArgs);
|
|
59
|
+
if (jResult && !env->ExceptionCheck()) {
|
|
60
|
+
const char* chars = env->GetStringUTFChars(jResult, nullptr);
|
|
61
|
+
auto jsResult = jsi::String::createFromUtf8(rt, chars);
|
|
62
|
+
env->ReleaseStringUTFChars(jResult, chars);
|
|
63
|
+
env->PopLocalFrame(nullptr);
|
|
64
|
+
return jsi::Value(rt, jsResult);
|
|
65
|
+
}
|
|
66
|
+
if (env->ExceptionCheck()) env->ExceptionClear();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else { env->ExceptionClear(); }
|
|
70
|
+
env->PopLocalFrame(nullptr);
|
|
71
|
+
}
|
|
72
|
+
return jsi::Value::null();
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
// __nativ.callAsync(moduleId, fnName, argsJson) → Promise
|
|
76
|
+
nativ.setProperty(rt, "callAsync", jsi::Function::createFromHostFunction(
|
|
77
|
+
rt, jsi::PropNameID::forAscii(rt, "callAsync"), 3,
|
|
78
|
+
[](jsi::Runtime &rt, const jsi::Value &,
|
|
79
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
80
|
+
if (count < 3) return jsi::Value::null();
|
|
81
|
+
|
|
82
|
+
auto moduleId = args[0].getString(rt).utf8(rt);
|
|
83
|
+
auto fnName = args[1].getString(rt).utf8(rt);
|
|
84
|
+
auto argsJson = args[2].getString(rt).utf8(rt);
|
|
85
|
+
|
|
86
|
+
auto asyncFn = nativ_get_async_fn(moduleId.c_str(), fnName.c_str());
|
|
87
|
+
if (!asyncFn) {
|
|
88
|
+
throw jsi::JSError(rt, "Unknown Nativ async function: " + moduleId + "::" + fnName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
auto Promise = rt.global().getPropertyAsFunction(rt, "Promise");
|
|
92
|
+
auto executor = jsi::Function::createFromHostFunction(
|
|
93
|
+
rt, jsi::PropNameID::forAscii(rt, "executor"), 2,
|
|
94
|
+
[asyncFn, argsJson](jsi::Runtime &rt, const jsi::Value &,
|
|
95
|
+
const jsi::Value *pargs, size_t) -> jsi::Value {
|
|
96
|
+
|
|
97
|
+
auto resolve = std::make_shared<jsi::Function>(pargs[0].asObject(rt).asFunction(rt));
|
|
98
|
+
auto reject = std::make_shared<jsi::Function>(pargs[1].asObject(rt).asFunction(rt));
|
|
99
|
+
auto invoker = g_callInvoker;
|
|
100
|
+
|
|
101
|
+
// Same thread_local context pattern as iOS
|
|
102
|
+
struct AsyncCtx {
|
|
103
|
+
std::shared_ptr<jsi::Function> resolve;
|
|
104
|
+
std::shared_ptr<jsi::Function> reject;
|
|
105
|
+
std::shared_ptr<react::CallInvoker> invoker;
|
|
106
|
+
};
|
|
107
|
+
static thread_local AsyncCtx* _asyncCtx = nullptr;
|
|
108
|
+
auto ctx = new AsyncCtx{resolve, reject, invoker};
|
|
109
|
+
|
|
110
|
+
// Dispatch to background thread
|
|
111
|
+
std::thread([asyncFn, argsJson, ctx]() {
|
|
112
|
+
_asyncCtx = ctx;
|
|
113
|
+
|
|
114
|
+
asyncFn(
|
|
115
|
+
argsJson.c_str(),
|
|
116
|
+
// resolve
|
|
117
|
+
[](const char* result) {
|
|
118
|
+
auto* c = _asyncCtx;
|
|
119
|
+
if (!c) return;
|
|
120
|
+
_asyncCtx = nullptr;
|
|
121
|
+
auto resultStr = std::string(result ? result : "null");
|
|
122
|
+
auto res = c->resolve;
|
|
123
|
+
auto inv = c->invoker;
|
|
124
|
+
delete c;
|
|
125
|
+
if (inv) {
|
|
126
|
+
inv->invokeAsync([res, resultStr]() {
|
|
127
|
+
auto &rt = *g_runtime;
|
|
128
|
+
auto json = rt.global().getPropertyAsObject(rt, "JSON");
|
|
129
|
+
auto parse = json.getPropertyAsFunction(rt, "parse");
|
|
130
|
+
auto parsed = parse.call(rt, jsi::String::createFromUtf8(rt, resultStr));
|
|
131
|
+
res->call(rt, std::move(parsed));
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
// reject
|
|
136
|
+
[](const char* code, const char* msg) {
|
|
137
|
+
auto* c = _asyncCtx;
|
|
138
|
+
if (!c) return;
|
|
139
|
+
_asyncCtx = nullptr;
|
|
140
|
+
auto msgStr = std::string(msg ? msg : "Unknown error");
|
|
141
|
+
auto rej = c->reject;
|
|
142
|
+
auto inv = c->invoker;
|
|
143
|
+
delete c;
|
|
144
|
+
if (inv) {
|
|
145
|
+
inv->invokeAsync([rej, msgStr]() {
|
|
146
|
+
auto &rt = *g_runtime;
|
|
147
|
+
rej->call(rt, jsi::String::createFromUtf8(rt, msgStr));
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}).detach();
|
|
152
|
+
|
|
153
|
+
return jsi::Value::undefined();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return Promise.callAsConstructor(rt, executor);
|
|
157
|
+
}));
|
|
158
|
+
|
|
159
|
+
// __nativ.setComponentProps(componentId, props)
|
|
160
|
+
nativ.setProperty(rt, "setComponentProps", jsi::Function::createFromHostFunction(
|
|
161
|
+
rt, jsi::PropNameID::forAscii(rt, "setComponentProps"), 2,
|
|
162
|
+
[](jsi::Runtime &rt, const jsi::Value &,
|
|
163
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
164
|
+
if (count < 2 || !args[0].isString()) return jsi::Value::undefined();
|
|
165
|
+
JNIEnv *env = jni::Environment::current();
|
|
166
|
+
if (!env) return jsi::Value::undefined();
|
|
167
|
+
if (env->PushLocalFrame(128) != 0) return jsi::Value::undefined();
|
|
168
|
+
|
|
169
|
+
auto componentId = args[0].getString(rt).utf8(rt);
|
|
170
|
+
|
|
171
|
+
jclass hmClass = env->FindClass("java/util/HashMap");
|
|
172
|
+
if (!hmClass) { env->PopLocalFrame(nullptr); return jsi::Value::undefined(); }
|
|
173
|
+
jmethodID hmInit = env->GetMethodID(hmClass, "<init>", "()V");
|
|
174
|
+
jmethodID hmPut = env->GetMethodID(hmClass, "put",
|
|
175
|
+
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
176
|
+
|
|
177
|
+
jobject strings = env->NewObject(hmClass, hmInit);
|
|
178
|
+
jobject numbers = env->NewObject(hmClass, hmInit);
|
|
179
|
+
jobject bools = env->NewObject(hmClass, hmInit);
|
|
180
|
+
|
|
181
|
+
jclass dblClass = env->FindClass("java/lang/Double");
|
|
182
|
+
jmethodID dblOf = env->GetStaticMethodID(dblClass, "valueOf", "(D)Ljava/lang/Double;");
|
|
183
|
+
jclass boolClass = env->FindClass("java/lang/Boolean");
|
|
184
|
+
jmethodID boolOf = env->GetStaticMethodID(boolClass, "valueOf", "(Z)Ljava/lang/Boolean;");
|
|
185
|
+
|
|
186
|
+
if (args[1].isObject()) {
|
|
187
|
+
auto obj = args[1].asObject(rt);
|
|
188
|
+
auto names = obj.getPropertyNames(rt);
|
|
189
|
+
for (size_t i = 0; i < names.size(rt); i++) {
|
|
190
|
+
auto propName = names.getValueAtIndex(rt, i).getString(rt).utf8(rt);
|
|
191
|
+
if (propName.empty()) continue;
|
|
192
|
+
auto val = obj.getProperty(rt, propName.c_str());
|
|
193
|
+
jstring jKey = env->NewStringUTF(propName.c_str());
|
|
194
|
+
if (!jKey) continue;
|
|
195
|
+
|
|
196
|
+
if (val.isString()) {
|
|
197
|
+
auto sval = val.getString(rt).utf8(rt);
|
|
198
|
+
jstring jVal = env->NewStringUTF(sval.c_str());
|
|
199
|
+
if (jVal) env->CallObjectMethod(strings, hmPut, jKey, jVal);
|
|
200
|
+
} else if (val.isNumber()) {
|
|
201
|
+
jobject jVal = env->CallStaticObjectMethod(dblClass, dblOf, val.getNumber());
|
|
202
|
+
env->CallObjectMethod(numbers, hmPut, jKey, jVal);
|
|
203
|
+
} else if (val.isBool()) {
|
|
204
|
+
jobject jVal = env->CallStaticObjectMethod(boolClass, boolOf, (jboolean)val.getBool());
|
|
205
|
+
env->CallObjectMethod(bools, hmPut, jKey, jVal);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
jclass rtClass = env->FindClass("com/nativfabric/NativRuntime");
|
|
211
|
+
if (rtClass) {
|
|
212
|
+
jfieldID instField = env->GetStaticFieldID(rtClass, "INSTANCE",
|
|
213
|
+
"Lcom/nativfabric/NativRuntime;");
|
|
214
|
+
jobject rtInst = instField ? env->GetStaticObjectField(rtClass, instField) : nullptr;
|
|
215
|
+
|
|
216
|
+
jclass psClass = env->FindClass("com/nativfabric/NativRuntime$PropsSnapshot");
|
|
217
|
+
if (psClass && rtInst) {
|
|
218
|
+
jmethodID psInit = env->GetMethodID(psClass, "<init>",
|
|
219
|
+
"(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V");
|
|
220
|
+
jobject snapshot = env->NewObject(psClass, psInit, strings, numbers, bools);
|
|
221
|
+
|
|
222
|
+
jmethodID setProps = env->GetMethodID(rtClass, "setComponentProps",
|
|
223
|
+
"(Ljava/lang/String;Lcom/nativfabric/NativRuntime$PropsSnapshot;)V");
|
|
224
|
+
jstring jCompId = env->NewStringUTF(componentId.c_str());
|
|
225
|
+
if (jCompId && snapshot && setProps) {
|
|
226
|
+
env->CallVoidMethod(rtInst, setProps, jCompId, snapshot);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (env->ExceptionCheck()) env->ExceptionClear();
|
|
232
|
+
env->PopLocalFrame(nullptr);
|
|
233
|
+
return jsi::Value::undefined();
|
|
234
|
+
}));
|
|
235
|
+
|
|
236
|
+
#ifndef NATIV_RELEASE
|
|
237
|
+
// __nativ.loadDylib(url) — dev only, downloads .so/.dex from Metro
|
|
238
|
+
nativ.setProperty(rt, "loadDylib", jsi::Function::createFromHostFunction(
|
|
239
|
+
rt, jsi::PropNameID::forAscii(rt, "loadDylib"), 1,
|
|
240
|
+
[](jsi::Runtime &rt, const jsi::Value &,
|
|
241
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
242
|
+
if (count < 1 || !args[0].isString()) return jsi::Value(false);
|
|
243
|
+
auto url = args[0].getString(rt).utf8(rt);
|
|
244
|
+
bool isDex = url.find(".dex") != std::string::npos;
|
|
245
|
+
|
|
246
|
+
JNIEnv *env = jni::Environment::current();
|
|
247
|
+
if (!env) return jsi::Value(false);
|
|
248
|
+
if (env->PushLocalFrame(64) != 0) return jsi::Value(false);
|
|
249
|
+
|
|
250
|
+
// Download via java.net.URL
|
|
251
|
+
jclass urlClass = env->FindClass("java/net/URL");
|
|
252
|
+
jmethodID urlInit = env->GetMethodID(urlClass, "<init>", "(Ljava/lang/String;)V");
|
|
253
|
+
jmethodID openStream = env->GetMethodID(urlClass, "openStream", "()Ljava/io/InputStream;");
|
|
254
|
+
jstring jUrl = env->NewStringUTF(url.c_str());
|
|
255
|
+
jobject urlObj = env->NewObject(urlClass, urlInit, jUrl);
|
|
256
|
+
if (env->ExceptionCheck()) { env->ExceptionClear(); env->PopLocalFrame(nullptr); return jsi::Value(false); }
|
|
257
|
+
|
|
258
|
+
jobject stream = env->CallObjectMethod(urlObj, openStream);
|
|
259
|
+
if (env->ExceptionCheck() || !stream) { env->ExceptionClear(); env->PopLocalFrame(nullptr); return jsi::Value(false); }
|
|
260
|
+
|
|
261
|
+
// Read into ByteArrayOutputStream
|
|
262
|
+
jclass isClass = env->FindClass("java/io/InputStream");
|
|
263
|
+
jclass baosClass = env->FindClass("java/io/ByteArrayOutputStream");
|
|
264
|
+
jobject baos = env->NewObject(baosClass, env->GetMethodID(baosClass, "<init>", "()V"));
|
|
265
|
+
jbyteArray buf = env->NewByteArray(8192);
|
|
266
|
+
jmethodID readM = env->GetMethodID(isClass, "read", "([B)I");
|
|
267
|
+
jmethodID writeM = env->GetMethodID(baosClass, "write", "([BII)V");
|
|
268
|
+
while (true) {
|
|
269
|
+
jint n = env->CallIntMethod(stream, readM, buf);
|
|
270
|
+
if (n <= 0) break;
|
|
271
|
+
env->CallVoidMethod(baos, writeM, buf, 0, n);
|
|
272
|
+
}
|
|
273
|
+
env->CallVoidMethod(stream, env->GetMethodID(isClass, "close", "()V"));
|
|
274
|
+
auto bytes = (jbyteArray)env->CallObjectMethod(baos, env->GetMethodID(baosClass, "toByteArray", "()[B"));
|
|
275
|
+
jsize len = env->GetArrayLength(bytes);
|
|
276
|
+
|
|
277
|
+
// Write to temp file
|
|
278
|
+
jclass fileClass = env->FindClass("java/io/File");
|
|
279
|
+
jstring prefix = env->NewStringUTF("nativ_");
|
|
280
|
+
jstring suffix = env->NewStringUTF(isDex ? ".dex" : ".so");
|
|
281
|
+
jobject tmpFile = env->CallStaticObjectMethod(fileClass,
|
|
282
|
+
env->GetStaticMethodID(fileClass, "createTempFile", "(Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;"),
|
|
283
|
+
prefix, suffix);
|
|
284
|
+
if (env->ExceptionCheck()) { env->ExceptionClear(); env->PopLocalFrame(nullptr); return jsi::Value(false); }
|
|
285
|
+
|
|
286
|
+
jclass fosClass = env->FindClass("java/io/FileOutputStream");
|
|
287
|
+
jobject fos = env->NewObject(fosClass, env->GetMethodID(fosClass, "<init>", "(Ljava/io/File;)V"), tmpFile);
|
|
288
|
+
env->CallVoidMethod(fos, env->GetMethodID(fosClass, "write", "([B)V"), bytes);
|
|
289
|
+
env->CallVoidMethod(fos, env->GetMethodID(fosClass, "close", "()V"));
|
|
290
|
+
|
|
291
|
+
auto jPath = (jstring)env->CallObjectMethod(tmpFile,
|
|
292
|
+
env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"));
|
|
293
|
+
const char *pathChars = env->GetStringUTFChars(jPath, nullptr);
|
|
294
|
+
std::string filePath(pathChars);
|
|
295
|
+
env->ReleaseStringUTFChars(jPath, pathChars);
|
|
296
|
+
|
|
297
|
+
bool ok = false;
|
|
298
|
+
if (isDex) {
|
|
299
|
+
auto lastSlash = url.rfind('/');
|
|
300
|
+
auto dotDex = url.rfind(".dex");
|
|
301
|
+
std::string moduleId = (lastSlash != std::string::npos && dotDex != std::string::npos)
|
|
302
|
+
? url.substr(lastSlash + 1, dotDex - lastSlash - 1) : "";
|
|
303
|
+
if (moduleId.substr(0, 7) == "nativ_") moduleId = moduleId.substr(7);
|
|
304
|
+
auto lastUs = moduleId.rfind('_');
|
|
305
|
+
if (lastUs != std::string::npos && moduleId.length() - lastUs - 1 == 8) {
|
|
306
|
+
moduleId = moduleId.substr(0, lastUs);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
jclass rtClass = env->FindClass("com/nativfabric/NativRuntime");
|
|
310
|
+
jfieldID instField = env->GetStaticFieldID(rtClass, "INSTANCE", "Lcom/nativfabric/NativRuntime;");
|
|
311
|
+
jobject rtInst = instField ? env->GetStaticObjectField(rtClass, instField) : nullptr;
|
|
312
|
+
if (rtInst) {
|
|
313
|
+
jmethodID loadDex = env->GetMethodID(rtClass, "loadDex", "(Ljava/lang/String;Ljava/lang/String;)Z");
|
|
314
|
+
jstring jFilePath = env->NewStringUTF(filePath.c_str());
|
|
315
|
+
jstring jModuleId = env->NewStringUTF(moduleId.c_str());
|
|
316
|
+
ok = env->CallBooleanMethod(rtInst, loadDex, jFilePath, jModuleId);
|
|
317
|
+
if (env->ExceptionCheck()) { env->ExceptionClear(); ok = false; }
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
env->CallBooleanMethod(tmpFile,
|
|
321
|
+
env->GetMethodID(fileClass, "setExecutable", "(Z)Z"), (jboolean)true);
|
|
322
|
+
void *handle = dlopen(filePath.c_str(), RTLD_NOW);
|
|
323
|
+
ok = (handle != nullptr);
|
|
324
|
+
if (ok) {
|
|
325
|
+
using InitFn = void (*)(void*);
|
|
326
|
+
void *rtLib = dlopen("libnativruntime.so", RTLD_NOW | RTLD_NOLOAD);
|
|
327
|
+
if (rtLib) {
|
|
328
|
+
auto setLib = (InitFn)dlsym(handle, "nativ_set_runtime_lib");
|
|
329
|
+
if (setLib) setLib(rtLib);
|
|
330
|
+
auto initFn = (InitFn)dlsym(handle, "nativ_init");
|
|
331
|
+
if (initFn) {
|
|
332
|
+
void *regFn = dlsym(rtLib, "nativ_register_sync");
|
|
333
|
+
if (regFn) initFn(regFn);
|
|
334
|
+
}
|
|
335
|
+
auto renderInitFn = (InitFn)dlsym(handle, "nativ_init_render");
|
|
336
|
+
if (renderInitFn) {
|
|
337
|
+
void *renderRegFn = dlsym(rtLib, "nativ_register_render");
|
|
338
|
+
if (renderRegFn) renderInitFn(renderRegFn);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
NATIV_LOG("loadDylib: dlopen failed: %s", dlerror());
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
NATIV_LOG("loadDylib: %s → %s (%d bytes)", url.c_str(), ok ? "OK" : "FAIL", (int)len);
|
|
347
|
+
env->PopLocalFrame(nullptr);
|
|
348
|
+
return jsi::Value(ok);
|
|
349
|
+
}));
|
|
350
|
+
#endif // !NATIV_RELEASE
|
|
351
|
+
|
|
352
|
+
// ABI target — used by JS shim to request correct dylib from Metro
|
|
353
|
+
#if defined(__aarch64__)
|
|
354
|
+
nativ.setProperty(rt, "target", jsi::String::createFromUtf8(rt, "arm64-v8a"));
|
|
355
|
+
#elif defined(__arm__)
|
|
356
|
+
nativ.setProperty(rt, "target", jsi::String::createFromUtf8(rt, "armeabi-v7a"));
|
|
357
|
+
#elif defined(__x86_64__)
|
|
358
|
+
nativ.setProperty(rt, "target", jsi::String::createFromUtf8(rt, "x86_64"));
|
|
359
|
+
#elif defined(__i386__)
|
|
360
|
+
nativ.setProperty(rt, "target", jsi::String::createFromUtf8(rt, "x86"));
|
|
361
|
+
#endif
|
|
362
|
+
|
|
363
|
+
rt.global().setProperty(rt, "__nativ", std::move(nativ));
|
|
364
|
+
NATIV_LOG("global.__nativ installed via TurboModuleWithJSIBindings");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ─── JNI BindingsInstallerHolder for TurboModuleWithJSIBindings ────────
|
|
368
|
+
|
|
369
|
+
struct NativRuntimeJSIBindings : public jni::JavaClass<NativRuntimeJSIBindings> {
|
|
370
|
+
static constexpr const char *kJavaDescriptor = "Lcom/nativfabric/NativRuntimeModule;";
|
|
371
|
+
|
|
372
|
+
static void registerNatives() {
|
|
373
|
+
javaClassLocal()->registerNatives({
|
|
374
|
+
makeNativeMethod("getBindingsInstaller", NativRuntimeJSIBindings::getBindingsInstaller),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
static jni::local_ref<react::BindingsInstallerHolder::javaobject>
|
|
379
|
+
getBindingsInstaller(jni::alias_ref<NativRuntimeJSIBindings> /*jobj*/) {
|
|
380
|
+
return react::BindingsInstallerHolder::newObjectCxxArgs(
|
|
381
|
+
[](jsi::Runtime &runtime, const std::shared_ptr<react::CallInvoker> &callInvoker) {
|
|
382
|
+
g_callInvoker = callInvoker;
|
|
383
|
+
g_runtime = &runtime;
|
|
384
|
+
installNativ(runtime);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
|
390
|
+
return jni::initialize(vm, [] {
|
|
391
|
+
NativRuntimeJSIBindings::registerNatives();
|
|
392
|
+
});
|
|
393
|
+
}
|