Use un-patched RN for Android by installing hook into JSC
This works by installing some assembly into JSGlobalContextCreateInGroup() that will immediately jump out into our own wrapper function so we always can inject the Realm constructor into the context.
This commit is contained in:
parent
49408ad767
commit
415b45be51
|
@ -73,6 +73,6 @@ android {
|
|||
|
||||
dependencies {
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.facebook.react:react-native:0.18.0-patched'
|
||||
compile 'com.facebook.react:react-native:0.18.0'
|
||||
compile project(':realm')
|
||||
}
|
||||
|
|
|
@ -285,5 +285,5 @@ afterEvaluate { project ->
|
|||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'org.nanohttpd:nanohttpd:2.2.0'
|
||||
compile 'com.facebook.react:react-native:0.18.0-patched'
|
||||
compile 'com.facebook.react:react-native:0.18.0'
|
||||
}
|
||||
|
|
|
@ -29,5 +29,5 @@ android {
|
|||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'org.nanohttpd:nanohttpd:2.2.0'
|
||||
compile 'com.facebook.react:react-native:0.18.0-patched'
|
||||
compile 'com.facebook.react:react-native:0.18.0'
|
||||
}
|
||||
|
|
|
@ -21,19 +21,23 @@ public class RealmReactAndroid extends ReactContextBaseJavaModule {
|
|||
private static final int DEFAULT_PORT = 8082;
|
||||
|
||||
private AndroidWebServer webServer;
|
||||
private long rpcServerPtr;
|
||||
|
||||
private String filesDirPath;
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
static {
|
||||
SoLoader.loadLibrary("realmreact");
|
||||
}
|
||||
|
||||
public RealmReactAndroid(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
String fileDir;
|
||||
try {
|
||||
filesDirPath = getReactApplicationContext().getFilesDir().getCanonicalPath();
|
||||
fileDir = reactContext.getFilesDir().getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
SoLoader.loadLibrary("realmreact");
|
||||
|
||||
setDefaultRealmFileDirectory(fileDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,31 +47,18 @@ public class RealmReactAndroid extends ReactContextBaseJavaModule {
|
|||
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
long contexts = injectRealmJsContext(filesDirPath);
|
||||
|
||||
if (contexts == -1) {
|
||||
Log.e("RealmReactAndroid", "Error during initializing Realm context");
|
||||
throw new IllegalStateException("Error during initializing Realm context");
|
||||
}
|
||||
|
||||
Log.i("RealmReactAndroid", "Initialized " + contexts + " contexts");
|
||||
|
||||
if (contexts == 0) {// we're running in Chrome debug mode
|
||||
startWebServer();
|
||||
}
|
||||
// FIXME: Only start web server when in Chrome debug mode!
|
||||
startWebServer();
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCatalystInstanceDestroy() {
|
||||
if (webServer != null) {
|
||||
Log.i("RealmReactAndroid", "Stopping the debugging Webserver");
|
||||
webServer.stop();
|
||||
}
|
||||
stopWebServer();
|
||||
}
|
||||
|
||||
private void startWebServer() {
|
||||
rpcServerPtr = setupChromeDebugModeRealmJsContext();
|
||||
setupChromeDebugModeRealmJsContext();
|
||||
webServer = new AndroidWebServer(DEFAULT_PORT);
|
||||
try {
|
||||
webServer.start();
|
||||
|
@ -77,7 +68,13 @@ public class RealmReactAndroid extends ReactContextBaseJavaModule {
|
|||
}
|
||||
}
|
||||
|
||||
// WebServer
|
||||
private void stopWebServer() {
|
||||
if (webServer != null) {
|
||||
Log.i("RealmReactAndroid", "Stopping the webserver");
|
||||
webServer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidWebServer extends NanoHTTPD {
|
||||
public AndroidWebServer(int port) {
|
||||
super(port);
|
||||
|
@ -105,7 +102,7 @@ public class RealmReactAndroid extends ReactContextBaseJavaModule {
|
|||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
jsonResponse[0] = processChromeDebugCommand(rpcServerPtr, cmdUri, json);
|
||||
jsonResponse[0] = processChromeDebugCommand(cmdUri, json);
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -122,11 +119,11 @@ public class RealmReactAndroid extends ReactContextBaseJavaModule {
|
|||
}
|
||||
|
||||
// fileDir: path of the internal storage of the application
|
||||
private native long injectRealmJsContext(String fileDir);
|
||||
private native void setDefaultRealmFileDirectory(String fileDir);
|
||||
|
||||
// responsible for creating the rpcServer that will accept the chrome Websocket command
|
||||
private native long setupChromeDebugModeRealmJsContext();
|
||||
|
||||
// this receives one command from Chrome debug then return the processing we should post back
|
||||
private native String processChromeDebugCommand(long rpcServerPointer, String cmd, String args);
|
||||
private native String processChromeDebugCommand(String cmd, String args);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ LOCAL_SRC_FILES := \
|
|||
src/rpc.cpp \
|
||||
src/android/platform.cpp \
|
||||
src/android/io_realm_react_RealmReactAndroid.cpp \
|
||||
src/android/jsc_override.cpp \
|
||||
src/object-store/index_set.cpp \
|
||||
src/object-store/list.cpp \
|
||||
src/object-store/object_schema.cpp \
|
||||
|
|
|
@ -3,88 +3,42 @@
|
|||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "io_realm_react_RealmReactAndroid.h"
|
||||
#include "js_init.h"
|
||||
#include "rpc.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "shared_realm.hpp"
|
||||
#include <unordered_map>
|
||||
#include <android/log.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
class JSCExecutor;
|
||||
}
|
||||
}
|
||||
#include "io_realm_react_RealmReactAndroid.h"
|
||||
#include "rpc.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
/*
|
||||
* Class: io_realm_react_RealmReactAndroid
|
||||
* Method: injectRealmJsContext
|
||||
* Signature: ()Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactAndroid_injectRealmJsContext
|
||||
static realm_js::RPCServer *s_rpc_server;
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_realm_react_RealmReactAndroid_setDefaultRealmFileDirectory
|
||||
(JNIEnv *env, jclass, jstring fileDir)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "JSRealm", "Java_io_realm_react_RealmReactAndroid_injectRealmJsContext");
|
||||
void* handle = dlopen ("libreactnativejni.so", RTLD_LAZY);
|
||||
if (!handle) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "JSRealm", "Cannot open libreactnativejni.so");
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "JSRealm", "setDefaultRealmFileDirectory");
|
||||
|
||||
// Getting the internal storage path for the application
|
||||
const char* strFileDir = env->GetStringUTFChars(fileDir , NULL);
|
||||
// Setting the internal storage path for the application
|
||||
const char* strFileDir = env->GetStringUTFChars(fileDir, NULL);
|
||||
realm::set_default_realm_file_directory(strFileDir);
|
||||
env->ReleaseStringUTFChars(fileDir , strFileDir);
|
||||
|
||||
// load the symbol
|
||||
typedef std::unordered_map<JSContextRef, facebook::react::JSCExecutor*> (*get_jsc_context_t)();
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "JSRealm", "Absolute path: %s", realm::default_realm_file_directory().c_str());
|
||||
}
|
||||
|
||||
get_jsc_context_t get_jsc_context = (get_jsc_context_t) dlsym(handle, "get_jsc_context");
|
||||
|
||||
if (get_jsc_context != NULL) {
|
||||
// clearing previous instances
|
||||
realm::Realm::s_global_cache.clear();
|
||||
std::unordered_map<JSContextRef, facebook::react::JSCExecutor*> s_globalContextRefToJSCExecutor = get_jsc_context();
|
||||
for (auto pair : s_globalContextRefToJSCExecutor) {
|
||||
RJSInitializeInContext(pair.first);
|
||||
}
|
||||
return s_globalContextRefToJSCExecutor.size();
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "JSRealm", "Cannot find symbol get_jsc_context");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: io_realm_react_RealmReactAndroid
|
||||
* Method: setupChromeDebugModeRealmJsContext
|
||||
*/
|
||||
static realm_js::RPCServer *s_rpc_server;
|
||||
JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactAndroid_setupChromeDebugModeRealmJsContext
|
||||
JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactAndroid_setupChromeDebugModeRealmJsContext
|
||||
(JNIEnv *, jclass)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "JSRealm", "Java_com_reacttests_RealmReactAndroid_setupChromeDebugModeRealmJsContext");
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "JSRealm", "setupChromeDebugModeRealmJsContext");
|
||||
if (s_rpc_server) {
|
||||
delete s_rpc_server;
|
||||
delete s_rpc_server;
|
||||
}
|
||||
s_rpc_server = new realm_js::RPCServer();
|
||||
return (jlong)s_rpc_server;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: io_realm_react_RealmReactAndroid
|
||||
* Method: processChromeDebugCommand
|
||||
*/
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_io_realm_react_RealmReactAndroid_processChromeDebugCommand
|
||||
(JNIEnv *env, jclass, jlong rpc_server_ptr, jstring chrome_cmd, jstring chrome_args)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "JSRealm", "Java_io_realm_react_RealmReactAndroid_processChromeDebugCommand");
|
||||
JNIEXPORT jstring JNICALL Java_io_realm_react_RealmReactAndroid_processChromeDebugCommand
|
||||
(JNIEnv *env, jclass, jstring chrome_cmd, jstring chrome_args)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "JSRealm", "processChromeDebugCommand");
|
||||
const char* cmd = env->GetStringUTFChars(chrome_cmd, NULL);
|
||||
const char* args = env->GetStringUTFChars(chrome_args, NULL);
|
||||
realm_js::json json = realm_js::json::parse(args);
|
||||
|
@ -92,4 +46,4 @@ JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactAndroid_injectRealmJsConte
|
|||
env->ReleaseStringUTFChars(chrome_cmd, cmd);
|
||||
env->ReleaseStringUTFChars(chrome_args, args);
|
||||
return env->NewStringUTF(response.dump().c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Class: io_realm_react_RealmReactAndroid
|
||||
* Method: injectRealmJsContext
|
||||
* Signature: ()Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactAndroid_injectRealmJsContext
|
||||
* Class: io_realm_react_RealmReactAndroid
|
||||
* Method: setDefaultRealmFileDirectory
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_io_realm_react_RealmReactAndroid_setDefaultRealmFileDirectory
|
||||
(JNIEnv *, jclass, jstring);
|
||||
|
||||
/*
|
||||
|
@ -24,11 +24,10 @@ JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactAndroid_setupChromeDebugMo
|
|||
|
||||
/*
|
||||
* Class: io_realm_react_RealmReactAndroid
|
||||
* Method: processsetupChromeDebugCommand
|
||||
* Method: processChromeDebugCommand
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_io_realm_react_RealmReactAndroid_processChromeDebugCommand
|
||||
(JNIEnv *, jclass, jlong, jstring, jstring);
|
||||
|
||||
(JNIEnv *, jclass, jstring, jstring);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/* Copyright 2016 Realm Inc - All Rights Reserved
|
||||
* Proprietary and Confidential
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <mutex>
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
|
||||
#include "js_init.h"
|
||||
#include "shared_realm.hpp"
|
||||
|
||||
#if __arm__
|
||||
#define HOOK_SIZE 8
|
||||
#else
|
||||
#define HOOK_SIZE 5
|
||||
#endif
|
||||
|
||||
static void swap_function() __attribute__((constructor));
|
||||
|
||||
static JSGlobalContextRef create_context(JSContextGroupRef group, JSClassRef global_class)
|
||||
{
|
||||
static std::mutex s_mutex;
|
||||
std::lock_guard<std::mutex> lock(s_mutex);
|
||||
|
||||
// Replace JSGlobalContextCreateInGroup with its original implementation and call it.
|
||||
swap_function();
|
||||
JSGlobalContextRef ctx = JSGlobalContextCreateInGroup(group, global_class);
|
||||
|
||||
// Reinstall the hook.
|
||||
swap_function();
|
||||
|
||||
// Clear cache from previous instances.
|
||||
realm::Realm::s_global_cache.clear();
|
||||
|
||||
RJSInitializeInContext(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void swap_function()
|
||||
{
|
||||
static int8_t s_orig_code[HOOK_SIZE];
|
||||
static bool s_swapped = false;
|
||||
|
||||
int8_t *orig_func = (int8_t*)&JSGlobalContextCreateInGroup;
|
||||
int8_t *new_func = (int8_t*)&create_context;
|
||||
|
||||
#if __arm__
|
||||
bool orig_thumb = (uintptr_t)orig_func % 4 != 0;
|
||||
if (orig_thumb) {
|
||||
orig_func--;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
uintptr_t page_start = (uintptr_t)orig_func & ~(page_size - 1);
|
||||
uintptr_t code_end = (uintptr_t)orig_func + HOOK_SIZE;
|
||||
|
||||
// Make this memory region writable.
|
||||
mprotect((void*)page_start, code_end - page_start, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
|
||||
if (s_swapped) {
|
||||
// Copy original code back into place.
|
||||
memcpy(orig_func, s_orig_code, HOOK_SIZE);
|
||||
} else {
|
||||
// Store the original code before replacing it.
|
||||
memcpy(s_orig_code, orig_func, HOOK_SIZE);
|
||||
|
||||
#if __arm__
|
||||
if (orig_thumb) {
|
||||
// LDR PC, [PC, #0]; BX PC;
|
||||
memcpy(orig_func, "\x00\x4f\x38\x47", 4);
|
||||
memcpy(orig_func + 4, &new_func, 4);
|
||||
} else {
|
||||
// LDR PC, [PC, #0];
|
||||
memcpy(orig_func, "\x00\xf0\x9f\xe5", 4);
|
||||
memcpy(orig_func + 4, &new_func, 4);
|
||||
}
|
||||
#else
|
||||
int32_t jmp_offset = (int64_t)new_func - (int64_t)orig_func - HOOK_SIZE;
|
||||
|
||||
// Change original function to jump to our new one.
|
||||
*orig_func = 0xE9; // JMP
|
||||
*(int32_t*)(orig_func + 1) = jmp_offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
s_swapped = !s_swapped;
|
||||
|
||||
// Return this region to no longer being writable.
|
||||
mprotect((void*)page_start, code_end - page_start, PROT_READ | PROT_EXEC);
|
||||
}
|
|
@ -74,7 +74,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.facebook.react:react-native:0.18.0-patched'
|
||||
compile 'com.facebook.react:react-native:0.18.0'
|
||||
compile project(':realm')
|
||||
compile project(':react-native-fs')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue