diff --git a/ReactNative/RealmReact.h b/ReactNative/RealmReact.h index 6a0b086c..42b83f6b 100644 --- a/ReactNative/RealmReact.h +++ b/ReactNative/RealmReact.h @@ -16,8 +16,8 @@ // //////////////////////////////////////////////////////////////////////////// -@import Foundation; -@import JavaScriptCore; +#import +#import extern JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool create); diff --git a/ReactNative/RealmReact.m b/ReactNative/RealmReact.mm similarity index 73% rename from ReactNative/RealmReact.m rename to ReactNative/RealmReact.mm index b780b73e..d942f124 100644 --- a/ReactNative/RealmReact.m +++ b/ReactNative/RealmReact.mm @@ -16,17 +16,18 @@ // //////////////////////////////////////////////////////////////////////////// +extern "C" { #import "RealmReact.h" #import "Base/RCTBridge.h" -@import GCDWebServers; -@import RealmJS; -@import ObjectiveC; -@import Darwin; +#import +#import +#import +#import @interface NSObject (RCTJavaScriptContext) -- (instancetype)initWithJSContext:(JSGlobalContextRef)context; -- (JSGlobalContextRef)ctx; + - (instancetype)initWithJSContext:(JSGlobalContextRef)context; + - (JSGlobalContextRef)ctx; @end JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool create) { @@ -47,9 +48,13 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre NSLog(@"Failed to load RCTJavaScriptContext class"); } } - + return [rctJSContext ctx]; } +} + +#import +#import @interface RealmReact () @end @@ -59,7 +64,7 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre @synthesize bridge = _bridge; + (void)load { - void (*RCTRegisterModule)(Class) = dlsym(RTLD_DEFAULT, "RCTRegisterModule"); + void (*RCTRegisterModule)(Class) = (void (*)(Class))dlsym(RTLD_DEFAULT, "RCTRegisterModule"); if (RCTRegisterModule) { RCTRegisterModule(self); @@ -89,25 +94,27 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre if ([executor isMemberOfClass:NSClassFromString(@"RCTWebSocketExecutor")]) { [GCDWebServer setLogLevel:3]; GCDWebServer *webServer = [[GCDWebServer alloc] init]; - RJSRPCServer *rpcServer = [[RJSRPCServer alloc] init]; + __block realm_js::RPCServer rpcServer; // Add a handler to respond to POST requests on any URL [webServer addDefaultHandlerForMethod:@"POST" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - NSError *error; - NSData *data = [(GCDWebServerDataRequest *)request data]; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - GCDWebServerResponse *response; - if (error || ![json isKindOfClass:[NSDictionary class]]) { - NSLog(@"Invalid RPC request - %@", error ?: json); - response = [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnprocessableEntity underlyingError:error message:@"Invalid RPC request"]; + GCDWebServerResponse *response; + try { + realm_js::json args = realm_js::json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]); + std::string response_text = rpcServer.perform_request(request.path.UTF8String, args).dump(); + response = [[GCDWebServerDataResponse alloc] initWithData:[NSData dataWithBytes:response_text.c_str() length:response_text.length()] contentType:@"application/json"]; } - else { - response = [GCDWebServerDataResponse responseWithJSONObject:[rpcServer performRequest:request.path args: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; }]; diff --git a/RealmJS.xcodeproj/project.pbxproj b/RealmJS.xcodeproj/project.pbxproj index ac350c7b..c0a97443 100644 --- a/RealmJS.xcodeproj/project.pbxproj +++ b/RealmJS.xcodeproj/project.pbxproj @@ -50,7 +50,8 @@ 0270BC851B7D020100010E03 /* asserts.js in Resources */ = {isa = PBXBuildFile; fileRef = 0270BC7E1B7D020100010E03 /* asserts.js */; }; 0270BC861B7D020100010E03 /* schemas.js in Resources */ = {isa = PBXBuildFile; fileRef = 0270BC7F1B7D020100010E03 /* schemas.js */; }; 0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; - 0270BCD11B7D067300010E03 /* RealmReact.m in Sources */ = {isa = PBXBuildFile; fileRef = 0270BCD01B7D067300010E03 /* RealmReact.m */; }; + 0270BCD11B7D067300010E03 /* RealmReact.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0270BCD01B7D067300010E03 /* RealmReact.mm */; }; + 0291DBD21BD994F700E3852C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; 02B58CCE1AE99D4D009B348C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02C0864E1BCDB27000942F9C /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02C0864C1BCDB27000942F9C /* list.cpp */; settings = {ASSET_TAGS = (); }; }; @@ -202,7 +203,7 @@ 0270BC7E1B7D020100010E03 /* asserts.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = asserts.js; path = tests/asserts.js; sourceTree = SOURCE_ROOT; }; 0270BC7F1B7D020100010E03 /* schemas.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = schemas.js; path = tests/schemas.js; sourceTree = SOURCE_ROOT; }; 0270BCCF1B7D067300010E03 /* RealmReact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmReact.h; path = ReactNative/RealmReact.h; sourceTree = ""; }; - 0270BCD01B7D067300010E03 /* RealmReact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmReact.m; path = ReactNative/RealmReact.m; sourceTree = ""; }; + 0270BCD01B7D067300010E03 /* RealmReact.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmReact.mm; path = ReactNative/RealmReact.mm; sourceTree = ""; }; 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GCDWebServer.xcodeproj; path = vendor/GCDWebServer/GCDWebServer.xcodeproj; sourceTree = ""; }; 02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; 02B29A161B7CF7C9008A7E6B /* RealmReact.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmReact.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -225,6 +226,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0291DBD21BD994F700E3852C /* JavaScriptCore.framework in Frameworks */, F6B3963D1BCE2430008BDC39 /* GCDWebServers.framework in Frameworks */, 02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */, ); @@ -299,7 +301,7 @@ isa = PBXGroup; children = ( 0270BCCF1B7D067300010E03 /* RealmReact.h */, - 0270BCD01B7D067300010E03 /* RealmReact.m */, + 0270BCD01B7D067300010E03 /* RealmReact.mm */, ); name = RealmReact; sourceTree = ""; @@ -626,7 +628,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0270BCD11B7D067300010E03 /* RealmReact.m in Sources */, + 0270BCD11B7D067300010E03 /* RealmReact.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/RealmJS.h b/src/RealmJS.h index daf34de6..880d478e 100644 --- a/src/RealmJS.h +++ b/src/RealmJS.h @@ -18,7 +18,6 @@ #import #import -#import @interface RealmJS : NSObject diff --git a/src/RealmRPC.hpp b/src/RealmRPC.hpp index 7033d0dc..1b3c9663 100644 --- a/src/RealmRPC.hpp +++ b/src/RealmRPC.hpp @@ -35,7 +35,7 @@ class RPCServer { public: RPCServer(); ~RPCServer(); - json perform_request(const std::string &name, const json args); + json perform_request(std::string name, const json args); private: JSGlobalContextRef _context; diff --git a/src/RealmRPC.mm b/src/RealmRPC.mm index 67ddb85e..bc66e59e 100644 --- a/src/RealmRPC.mm +++ b/src/RealmRPC.mm @@ -99,7 +99,7 @@ RPCServer::RPCServer() { }; _requests["/call_method"] = [=](const json dict) { JSObjectRef object = _objects[dict["id"].get()]; - JSStringRef methodString = RJSStringForString(dict["name"].get()); + JSStringRef methodString = RJSStringForString(dict["name"].get()); JSObjectRef function = RJSValidatedObjectProperty(_context, object, methodString); JSStringRelease(methodString); @@ -128,7 +128,7 @@ RPCServer::RPCServer() { value = JSObjectGetPropertyAtIndex(_context, _objects[oid], name.get(), &exception); } else { - JSStringRef propString = RJSStringForString(name.get()); + JSStringRef propString = RJSStringForString(name.get()); value = JSObjectGetProperty(_context, _objects[oid], propString, &exception); JSStringRelease(propString); } @@ -148,15 +148,15 @@ RPCServer::RPCServer() { JSObjectSetPropertyAtIndex(_context, _objects[oid], name.get(), value, &exception); } else { - JSStringRef propString = RJSStringForString(name.get()); + JSStringRef propString = RJSStringForString(name.get()); JSObjectSetProperty(_context, _objects[oid], propString, value, 0, &exception); JSStringRelease(propString); } if (exception) { - return @{@"error": @(RJSStringForValue(_context, exception).c_str())}; + return json({"error", RJSStringForValue(_context, exception)}); } - return @{}; + return json({}); }; _requests["/dispose_object"] = [=](const json dict) { RPCObjectID oid = dict["id"].get(); @@ -188,7 +188,7 @@ RPCServer::~RPCServer() { JSGlobalContextRelease(_context); } -json RPCServer::perform_request(const std::string &name, const json args) { +json RPCServer::perform_request(std::string name, const json args) { // perform all realm ops on the main thread RPCRequest action = _requests[name]; assert(action); @@ -306,10 +306,10 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) return _objects[oid]; } - NSString *type = dict[@"type"]; - id value = dict[@"value"]; + std::string type = dict["type"].get(); + json value = dict["value"]; - if ([type isEqualToString:@(RealmObjectTypesFunction)]) { + if (type == RealmObjectTypesFunction) { // FIXME: Make this actually call the function by its id once we need it to. JSStringRef jsBody = JSStringCreateWithUTF8CString(""); JSObjectRef jsFunction = JSObjectMakeFunction(_context, NULL, 0, NULL, jsBody, NULL, 1, NULL); @@ -317,9 +317,9 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) return jsFunction; } - else if ([type isEqualToString:@(RJSTypeGet(realm::PropertyTypeDate).c_str())]) { + else if (type == RJSTypeGet(realm::PropertyTypeDate)) { JSValueRef exception = NULL; - JSValueRef time = JSValueMakeNumber(_context, [value doubleValue]); + JSValueRef time = JSValueMakeNumber(_context, value.get()); JSObjectRef date = JSObjectMakeDate(_context, 1, &time, &exception); if (exception) { @@ -331,34 +331,34 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) if (!value) { return JSValueMakeUndefined(_context); } - else if ([value isKindOfClass:[NSNull class]]) { + else if (value.is_null()) { return JSValueMakeNull(_context); } - else if ([value isKindOfClass:[@YES class]]) { - return JSValueMakeBoolean(_context, [value boolValue]); + else if (value.is_boolean()) { + return JSValueMakeBoolean(_context, value.get()); } - else if ([value isKindOfClass:[NSNumber class]]) { - return JSValueMakeNumber(_context, [value doubleValue]); + else if (value.is_number()) { + return JSValueMakeNumber(_context, value.get()); } - else if ([value isKindOfClass:[NSString class]]) { - return RJSValueForString(_context, std::string([value UTF8String])); + else if (value.is_string()) { + return RJSValueForString(_context, value.get()); } - else if ([value isKindOfClass:[NSArray class]]) { - NSUInteger count = [value count]; + else if (value.is_array()) { + size_t count = value.size(); JSValueRef jsValues[count]; - for (NSUInteger i = 0; i < count; i++) { - jsValues[i] = [self deserializeDictionaryValue:value[i]]; + for (size_t i = 0; i < count; i++) { + jsValues[i] = deserialize_json_value(value.at(i)); } return JSObjectMakeArray(_context, count, jsValues, NULL); } - else if ([value isKindOfClass:[NSDictionary class]]) { + else if (value.is_object()) { JSObjectRef jsObject = JSObjectMake(_context, NULL, NULL); - for (NSString *key in value) { - JSValueRef jsValue = [self deserializeDictionaryValue:value[key]]; - JSStringRef jsKey = JSStringCreateWithCFString((__bridge CFStringRef)key); + for (json::iterator it = value.begin(); it != value.end(); ++it) { + JSValueRef jsValue = deserialize_json_value(it.value()); + JSStringRef jsKey = JSStringCreateWithUTF8CString(it.key().c_str()); JSObjectSetProperty(_context, jsObject, jsKey, jsValue, 0, NULL); JSStringRelease(jsKey); @@ -369,5 +369,3 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) return JSValueMakeUndefined(_context); } - -@end diff --git a/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m b/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m index 58adc9f8..3b712f24 100644 --- a/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m +++ b/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m @@ -41,7 +41,8 @@ static id s_currentJavaScriptExecutor; } s_currentJavaScriptExecutor = [bridge valueForKey:@"javaScriptExecutor"]; - + assert(s_currentJavaScriptExecutor); + // FIXME: Remove this nonsense once the crashes go away when a test fails! JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(s_currentJavaScriptExecutor, false); if (ctx) {