From 53061e8974fe6ed415af4a399cdcbf381bdb17b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Thu, 10 Jun 2021 12:25:09 +0200 Subject: [PATCH] Initial changes to build for iOS --- RealmJS.podspec | 3 + react-native/ios/RealmReact/RealmReact.mm | 171 +++------------------- src/CMakeLists.txt | 3 +- src/hermes/CMakeLists.txt | 7 + src/hermes/hermes_class.hpp | 4 +- src/hermes/hermes_init.cpp | 5 +- src/hermes/jsi/jsi.h | 2 +- src/ios/CMakeLists.txt | 5 +- 8 files changed, 42 insertions(+), 158 deletions(-) create mode 100644 src/hermes/CMakeLists.txt diff --git a/RealmJS.podspec b/RealmJS.podspec index 00470a8f03..dd36329306 100644 --- a/RealmJS.podspec +++ b/RealmJS.podspec @@ -57,6 +57,8 @@ Pod::Spec.new do |s| # Header search paths are prefixes to the path specified in #include macros 'HEADER_SEARCH_PATHS' => [ '"$(PODS_TARGET_SRCROOT)/react-native/ios/RealmReact/"', + '"$(PODS_TARGET_SRCROOT)/src/"', + '"$(PODS_TARGET_SRCROOT)/src/hermes/"', '"$(PODS_ROOT)/Headers/Public/React-Core/"' #"'#{app_path}/ios/Pods/Headers/Public/React-Core'" # Use this line instead of 👆 while linting ].join(' ') @@ -66,6 +68,7 @@ Pod::Spec.new do |s| s.ios.vendored_frameworks = 'react-native/ios/realm-js-ios.xcframework' s.dependency 'React' + # s.dependency 'React-jsi' # TODO: Ensure the same version of GCDWebServer is used for Android s.dependency 'GCDWebServer' end diff --git a/react-native/ios/RealmReact/RealmReact.mm b/react-native/ios/RealmReact/RealmReact.mm index 7583668edb..f07775c6e7 100644 --- a/react-native/ios/RealmReact/RealmReact.mm +++ b/react-native/ios/RealmReact/RealmReact.mm @@ -19,8 +19,6 @@ #import "RealmReact.h" #import "RealmAnalytics.h" -#import - #import #import @@ -30,17 +28,12 @@ #import #import -#if DEBUG -#include -#import "GCDWebServer.h" -#import "GCDWebServerDataRequest.h" -#import "GCDWebServerDataResponse.h" -#import "GCDWebServerErrorResponse.h" - -#define WEB_SERVER_PORT 8083 +#include +#import "jsi/jsi.h" +//#import "hermes_init.hpp" -using namespace realm::rpc; -#endif +namespace jsi = facebook::jsi; +extern "C" void realm_hermes_init(jsi::Runtime& rt, jsi::Object& exports); @interface NSObject () - (instancetype)initWithJSContext:(JSContext *)context; @@ -84,11 +77,6 @@ @interface RealmReact () @implementation RealmReact { NSMutableDictionary *_eventHandlers; - -#if DEBUG - GCDWebServer *_webServer; - std::unique_ptr _rpcServer; -#endif } @synthesize bridge = _bridge; @@ -120,20 +108,7 @@ - (dispatch_queue_t)methodQueue { } - (NSDictionary *)constantsToExport { -#if DEBUG -#if TARGET_IPHONE_SIMULATOR - NSArray *hosts = @[@"localhost"]; -#else - NSArray *hosts = [self getIPAddresses]; -#endif - - return @{ - @"debugHosts": hosts, - @"debugPort": @(WEB_SERVER_PORT), - }; -#else return @{}; -#endif } - (void)addListenerForEvent:(NSString *)eventName handler:(RealmReactEventHandler)handler { @@ -155,120 +130,8 @@ - (void)removeListenerForEvent:(NSString *)eventName handler:(RealmReactEventHan } } -#if DEBUG -- (NSArray *)getIPAddresses { - static const char * const wifiInterface = "en0"; - - struct ifaddrs *ifaddrs; - if (getifaddrs(&ifaddrs)) { - NSLog(@"Failed to get interface addresses: %s", strerror(errno)); - return @[]; - } - - NSMutableArray *ipAddresses = [[NSMutableArray alloc] init]; - char host[INET6_ADDRSTRLEN]; - - for (struct ifaddrs *ifaddr = ifaddrs; ifaddr; ifaddr = ifaddr->ifa_next) { - if ((ifaddr->ifa_flags & IFF_LOOPBACK) || !(ifaddr->ifa_flags & IFF_UP)) { - // Ignore loopbacks and interfaces that aren't up. - continue; - } - - struct sockaddr *addr = ifaddr->ifa_addr; - if (addr->sa_family == AF_INET) { - // Ignore link-local ipv4 addresses. - in_addr_t sin_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; - if (IN_LOOPBACK(sin_addr) || IN_LINKLOCAL(sin_addr) || IN_ZERONET(sin_addr)) { - continue; - } - } - else if (addr->sa_family == AF_INET6) { - // Ignore link-local ipv6 addresses. - struct in6_addr *sin6_addr = &((struct sockaddr_in6 *)addr)->sin6_addr; - if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(sin6_addr)) { - continue; - } - } - else { - // Ignore addresses that are not ipv4 or ipv6. - continue; - } - - if (strcmp(ifaddr->ifa_name, wifiInterface)) { - // Ignore non-wifi addresses. - continue; - } - if (int error = getnameinfo(addr, addr->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST)) { - NSLog(@"Couldn't resolve host name for address: %s", gai_strerror(error)); - continue; - } - - [ipAddresses addObject:@(host)]; - } - - freeifaddrs(ifaddrs); - return [ipAddresses copy]; -} - -- (void)startRPC { - [GCDWebServer setLogLevel:3]; - _webServer = [[GCDWebServer alloc] init]; - _rpcServer = std::make_unique(); - __weak __typeof__(self) weakSelf = self; - - // Add a handler to respond to POST requests on any URL - [_webServer addDefaultHandlerForMethod:@"POST" - requestClass:[GCDWebServerDataRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - __typeof__(self) self = weakSelf; - RPCServer *rpcServer = self ? self->_rpcServer.get() : nullptr; - GCDWebServerResponse *response; - - try { - NSData *responseData; - - if (rpcServer) { - std::string args = [[(GCDWebServerDataRequest *)request text] UTF8String]; - std::string responseText = rpcServer->perform_request(request.path.UTF8String, args); - - responseData = [NSData dataWithBytes:responseText.c_str() length:responseText.length()]; - } - else { - // we have been deallocated - responseData = [NSData data]; - } - - response = [[GCDWebServerDataResponse alloc] initWithData:responseData contentType:@"application/json"]; - } - catch(std::exception &ex) { - NSLog(@"Invalid RPC request - %@", [(GCDWebServerDataRequest *)request text]); - response = [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnprocessableEntity - underlyingError:nil - message:@"Invalid RPC request"]; - } - - [response setValue:@"http://localhost:8081" forAdditionalHeader:@"Access-Control-Allow-Origin"]; - return response; - }]; - - [_webServer startWithPort:WEB_SERVER_PORT bonjourName:nil]; - return; -} - -- (void)shutdownRPC { - [_webServer stop]; - [_webServer removeAllHandlers]; - _webServer = nil; - _rpcServer.reset(); -} -#endif - - (void)invalidate { - RJSInvalidateCaches(); -#if DEBUG - // shutdown rpc if in chrome debug mode - [self shutdownRPC]; -#endif + // RJSInvalidateCaches(); } - (void)dealloc { @@ -284,8 +147,10 @@ void _initializeOnJSThread(JSContextRefExtractor jsContextExtractor) { [NSThread sleepForTimeInterval:0.1]; } s_currentJSThread = [NSThread currentThread]; + + jsContextExtractor(); - RJSInitializeInContext(jsContextExtractor()); + // RJSInitializeInContext(jsContextExtractor()); } - (void)setBridge:(RCTBridge *)bridge { @@ -296,17 +161,22 @@ - (void)setBridge:(RCTBridge *)bridge { s_currentModule = self; if (objc_lookUpClass("RCTWebSocketExecutor") && [bridge executorClass] == objc_lookUpClass("RCTWebSocketExecutor")) { -#if DEBUG - [self startRPC]; -#else - @throw [NSException exceptionWithName:@"Invalid Executor" reason:@"Chrome debug mode not supported in Release builds" userInfo:nil]; -#endif + @throw [NSException exceptionWithName:@"Invalid Executor" reason:@"Chrome debug mode not supported" userInfo:nil]; } else if ([bridge isKindOfClass:objc_lookUpClass("RCTCxxBridge")] || [NSStringFromClass([bridge class]) isEqual: @"RCTCxxBridge"]) { // probe for the new C++ bridge in React Native 0.45+ + + // auto& rt = *static_cast(bridge.runtime); + // auto exports = jsi::Object(rt); + // realm_hermes_init(rt, exports); __weak __typeof__(self) weakSelf = self; __weak __typeof__(bridge) weakBridge = bridge; - + + // probe for the new C++ bridge in React Native 0.45+ + auto& rt = *static_cast(bridge.runtime); + auto exports = jsi::Object(rt); + realm_hermes_init(rt, exports); + [bridge dispatchBlock:^{ __typeof__(self) self = weakSelf; __typeof__(bridge) bridge = weakBridge; @@ -315,6 +185,7 @@ - (void)setBridge:(RCTBridge *)bridge { } _initializeOnJSThread(^{ + // RN < 0.58 has a private method that returns the js context if ([bridge respondsToSelector:@selector(jsContextRef)]) { return [bridge jsContextRef]; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89a732ed80..fe29d568f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ elseif(ANDROID) add_subdirectory(jsc) add_subdirectory(android) elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) - add_subdirectory(jsc) + # add_subdirectory(jsc) + add_subdirectory(hermes) add_subdirectory(ios) endif() \ No newline at end of file diff --git a/src/hermes/CMakeLists.txt b/src/hermes/CMakeLists.txt new file mode 100644 index 0000000000..69482d0669 --- /dev/null +++ b/src/hermes/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(realm-js-hermes OBJECT + hermes_init.cpp +) + +target_include_directories(realm-js-hermes PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(realm-js-hermes PUBLIC realm-js-shared) diff --git a/src/hermes/hermes_class.hpp b/src/hermes/hermes_class.hpp index 8d3ca9fb9a..f15510fe1b 100644 --- a/src/hermes/hermes_class.hpp +++ b/src/hermes/hermes_class.hpp @@ -227,7 +227,7 @@ class ObjectWrap { "nativeFunc", util::format(R"( return function %1(...args) { - "use strict"; + //"use strict"; if (!nativeFunc) throw TypeError("%1() cannot be constructed directly from javascript"); if (!new.target) @@ -295,7 +295,7 @@ class ObjectWrap { if (typeof(property) != 'string' || !isNumber.test(property)) return Reflect.get(target, property, receiver); return getter(target, Number(property)) - } + }, set(target, property, receiver, val) { if (typeof(property) != 'string' || !isNumber.test(property)) return Reflect.set(target, property, receiver, val); diff --git a/src/hermes/hermes_init.cpp b/src/hermes/hermes_init.cpp index 55470e93b5..7c29756663 100644 --- a/src/hermes/hermes_init.cpp +++ b/src/hermes/hermes_init.cpp @@ -27,10 +27,11 @@ #include "js_realm.hpp" namespace realm::js::hermes { -void hermes_init(JsiEnv env, JsiObj& exports) { +extern "C" void realm_hermes_init(jsi::Runtime& rt, jsi::Object& exports) { + auto env = JsiEnv(rt); jsi::Function realm_constructor = js::RealmClass::create_constructor(env); auto name = realm_constructor.getProperty(env, "name").asString(env); - exports->setProperty(env, std::move(name), std::move(realm_constructor)); + exports.setProperty(env, std::move(name), std::move(realm_constructor)); // Only calling to populate static cache. Eventually this should be stored somewhere non-static. (void)js::ObjectWrap>::create_constructor(env); diff --git a/src/hermes/jsi/jsi.h b/src/hermes/jsi/jsi.h index 5308599c9b..0b29e9cf69 100644 --- a/src/hermes/jsi/jsi.h +++ b/src/hermes/jsi/jsi.h @@ -212,7 +212,7 @@ class JSI_EXPORT Runtime { /// different behaviors and limitations imposed by different VMs and APIs. By /// the time this is written, An implementation may swallow exceptions (JSC), /// may not pause (V8), and may not support bounded executions. - virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; + //virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; /// \return the global object virtual Object global() = 0; diff --git a/src/ios/CMakeLists.txt b/src/ios/CMakeLists.txt index a7d7d5aa8c..3af3dc6136 100644 --- a/src/ios/CMakeLists.txt +++ b/src/ios/CMakeLists.txt @@ -2,5 +2,6 @@ add_library(realm-js-ios STATIC platform.mm ) -target_link_libraries(realm-js-jsc PUBLIC "-framework JavaScriptCore") -target_link_libraries(realm-js-ios realm-js-jsc realm-js-shared) +# target_link_libraries(realm-js-jsc PUBLIC "-framework JavaScriptCore") +# target_link_libraries(realm-js-ios realm-js-jsc realm-js-shared) +target_link_libraries(realm-js-ios realm-js-hermes realm-js-shared)