Merge pull request #93 from realm/al-rpc-cpp
Rewrite rpc server in c++ so it can be used on android
This commit is contained in:
commit
ad47da8c09
|
@ -16,8 +16,8 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@import Foundation;
|
#import <Foundation/Foundation.h>
|
||||||
@import JavaScriptCore;
|
#import <JavaScriptCore/JavaScriptCore.h>
|
||||||
|
|
||||||
extern JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool create);
|
extern JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool create);
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
#import "RealmReact.h"
|
#import "RealmReact.h"
|
||||||
#import "Base/RCTBridge.h"
|
#import "Base/RCTBridge.h"
|
||||||
|
|
||||||
@import GCDWebServers;
|
#import <GCDWebServers/GCDWebServers.h>
|
||||||
@import RealmJS;
|
#import <RealmJS/RealmJS.h>
|
||||||
@import ObjectiveC;
|
#import <objc/runtime.h>
|
||||||
@import Darwin;
|
#import <dlfcn.h>
|
||||||
|
|
||||||
@interface NSObject (RCTJavaScriptContext)
|
@interface NSObject (RCTJavaScriptContext)
|
||||||
- (instancetype)initWithJSContext:(JSGlobalContextRef)context;
|
- (instancetype)initWithJSContext:(JSGlobalContextRef)context;
|
||||||
|
@ -50,6 +51,10 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre
|
||||||
|
|
||||||
return [rctJSContext ctx];
|
return [rctJSContext ctx];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#import <RealmJS/RealmRPC.hpp>
|
||||||
|
#import <RealmJS/RJSUtil.hpp>
|
||||||
|
|
||||||
@interface RealmReact () <RCTBridgeModule>
|
@interface RealmReact () <RCTBridgeModule>
|
||||||
@end
|
@end
|
||||||
|
@ -59,7 +64,7 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
+ (void)load {
|
+ (void)load {
|
||||||
void (*RCTRegisterModule)(Class) = dlsym(RTLD_DEFAULT, "RCTRegisterModule");
|
void (*RCTRegisterModule)(Class) = (void (*)(Class))dlsym(RTLD_DEFAULT, "RCTRegisterModule");
|
||||||
|
|
||||||
if (RCTRegisterModule) {
|
if (RCTRegisterModule) {
|
||||||
RCTRegisterModule(self);
|
RCTRegisterModule(self);
|
||||||
|
@ -77,10 +82,14 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
|
|
||||||
static GCDWebServer *s_webServer;
|
static GCDWebServer *s_webServer;
|
||||||
|
static realm_js::RPCServer *rpcServer;
|
||||||
|
|
||||||
if (s_webServer) {
|
if (s_webServer) {
|
||||||
[s_webServer stop];
|
[s_webServer stop];
|
||||||
[s_webServer removeAllHandlers];
|
[s_webServer removeAllHandlers];
|
||||||
s_webServer = nil;
|
s_webServer = nil;
|
||||||
|
|
||||||
|
delete rpcServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The executor could be a RCTWebSocketExecutor, in which case it won't have a JS context.
|
// The executor could be a RCTWebSocketExecutor, in which case it won't have a JS context.
|
||||||
|
@ -89,23 +98,28 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre
|
||||||
if ([executor isMemberOfClass:NSClassFromString(@"RCTWebSocketExecutor")]) {
|
if ([executor isMemberOfClass:NSClassFromString(@"RCTWebSocketExecutor")]) {
|
||||||
[GCDWebServer setLogLevel:3];
|
[GCDWebServer setLogLevel:3];
|
||||||
GCDWebServer *webServer = [[GCDWebServer alloc] init];
|
GCDWebServer *webServer = [[GCDWebServer alloc] init];
|
||||||
RJSRPCServer *rpcServer = [[RJSRPCServer alloc] init];
|
rpcServer = new realm_js::RPCServer();
|
||||||
|
|
||||||
// Add a handler to respond to POST requests on any URL
|
// Add a handler to respond to POST requests on any URL
|
||||||
[webServer addDefaultHandlerForMethod:@"POST"
|
[webServer addDefaultHandlerForMethod:@"POST"
|
||||||
requestClass:[GCDWebServerDataRequest class]
|
requestClass:[GCDWebServerDataRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
NSError *error;
|
|
||||||
NSData *data = [(GCDWebServerDataRequest *)request data];
|
|
||||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
|
||||||
GCDWebServerResponse *response;
|
GCDWebServerResponse *response;
|
||||||
|
try {
|
||||||
if (error || ![json isKindOfClass:[NSDictionary class]]) {
|
// perform all realm ops on the main thread
|
||||||
NSLog(@"Invalid RPC request - %@", error ?: json);
|
__block NSData *responseData;
|
||||||
response = [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnprocessableEntity underlyingError:error message:@"Invalid RPC request"];
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||||
|
realm_js::json args = realm_js::json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]);
|
||||||
|
std::string responseText = rpcServer->perform_request(request.path.UTF8String, args).dump();
|
||||||
|
responseData = [NSData dataWithBytes:responseText.c_str() length:responseText.length()];
|
||||||
|
});
|
||||||
|
response = [[GCDWebServerDataResponse alloc] initWithData:responseData contentType:@"application/json"];
|
||||||
}
|
}
|
||||||
else {
|
catch(std::exception &ex) {
|
||||||
response = [GCDWebServerDataResponse responseWithJSONObject:[rpcServer performRequest:request.path args:json]];
|
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"];
|
[response setValue:@"http://localhost:8081" forAdditionalHeader:@"Access-Control-Allow-Origin"];
|
|
@ -7,7 +7,7 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
02258FB31BC6E2D00075F13A /* RealmRPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 02258FB11BC6E2D00075F13A /* RealmRPC.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
02258FB31BC6E2D00075F13A /* RealmRPC.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 02258FB11BC6E2D00075F13A /* RealmRPC.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
02258FB41BC6E2D00075F13A /* RealmRPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = 02258FB21BC6E2D00075F13A /* RealmRPC.mm */; };
|
02258FB41BC6E2D00075F13A /* RealmRPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = 02258FB21BC6E2D00075F13A /* RealmRPC.mm */; };
|
||||||
02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; settings = {ASSET_TAGS = (); }; };
|
02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; settings = {ASSET_TAGS = (); }; };
|
||||||
02601F031BA0F0C4007C91FF /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02601F011BA0F0C4007C91FF /* external_commit_helper.cpp */; };
|
02601F031BA0F0C4007C91FF /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02601F011BA0F0C4007C91FF /* external_commit_helper.cpp */; };
|
||||||
|
@ -50,7 +50,8 @@
|
||||||
0270BC851B7D020100010E03 /* asserts.js in Resources */ = {isa = PBXBuildFile; fileRef = 0270BC7E1B7D020100010E03 /* asserts.js */; };
|
0270BC851B7D020100010E03 /* asserts.js in Resources */ = {isa = PBXBuildFile; fileRef = 0270BC7E1B7D020100010E03 /* asserts.js */; };
|
||||||
0270BC861B7D020100010E03 /* schemas.js in Resources */ = {isa = PBXBuildFile; fileRef = 0270BC7F1B7D020100010E03 /* schemas.js */; };
|
0270BC861B7D020100010E03 /* schemas.js in Resources */ = {isa = PBXBuildFile; fileRef = 0270BC7F1B7D020100010E03 /* schemas.js */; };
|
||||||
0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; };
|
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 */; };
|
02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; };
|
||||||
02B58CCE1AE99D4D009B348C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.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 = (); }; };
|
02C0864E1BCDB27000942F9C /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02C0864C1BCDB27000942F9C /* list.cpp */; settings = {ASSET_TAGS = (); }; };
|
||||||
|
@ -156,7 +157,7 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
02258FB11BC6E2D00075F13A /* RealmRPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmRPC.h; path = src/RealmRPC.h; sourceTree = "<group>"; };
|
02258FB11BC6E2D00075F13A /* RealmRPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = RealmRPC.hpp; path = src/RealmRPC.hpp; sourceTree = "<group>"; };
|
||||||
02258FB21BC6E2D00075F13A /* RealmRPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmRPC.mm; path = src/RealmRPC.mm; sourceTree = "<group>"; };
|
02258FB21BC6E2D00075F13A /* RealmRPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmRPC.mm; path = src/RealmRPC.mm; sourceTree = "<group>"; };
|
||||||
02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = tests/RealmJSCoreTests.m; sourceTree = SOURCE_ROOT; };
|
02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = tests/RealmJSCoreTests.m; sourceTree = SOURCE_ROOT; };
|
||||||
02601F011BA0F0C4007C91FF /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = external_commit_helper.cpp; path = "src/object-store/apple/external_commit_helper.cpp"; sourceTree = "<group>"; };
|
02601F011BA0F0C4007C91FF /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = external_commit_helper.cpp; path = "src/object-store/apple/external_commit_helper.cpp"; sourceTree = "<group>"; };
|
||||||
|
@ -202,7 +203,7 @@
|
||||||
0270BC7E1B7D020100010E03 /* asserts.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = asserts.js; path = tests/asserts.js; sourceTree = SOURCE_ROOT; };
|
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; };
|
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 = "<group>"; };
|
0270BCCF1B7D067300010E03 /* RealmReact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmReact.h; path = ReactNative/RealmReact.h; sourceTree = "<group>"; };
|
||||||
0270BCD01B7D067300010E03 /* RealmReact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmReact.m; path = ReactNative/RealmReact.m; sourceTree = "<group>"; };
|
0270BCD01B7D067300010E03 /* RealmReact.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmReact.mm; path = ReactNative/RealmReact.mm; sourceTree = "<group>"; };
|
||||||
02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GCDWebServer.xcodeproj; path = vendor/GCDWebServer/GCDWebServer.xcodeproj; sourceTree = "<group>"; };
|
02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GCDWebServer.xcodeproj; path = vendor/GCDWebServer/GCDWebServer.xcodeproj; sourceTree = "<group>"; };
|
||||||
02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
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; };
|
02B29A161B7CF7C9008A7E6B /* RealmReact.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmReact.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -225,6 +226,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
0291DBD21BD994F700E3852C /* JavaScriptCore.framework in Frameworks */,
|
||||||
F6B3963D1BCE2430008BDC39 /* GCDWebServers.framework in Frameworks */,
|
F6B3963D1BCE2430008BDC39 /* GCDWebServers.framework in Frameworks */,
|
||||||
02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */,
|
02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
|
@ -276,7 +278,7 @@
|
||||||
02601F071BA0F0CD007C91FF /* realm_delegate.hpp */,
|
02601F071BA0F0CD007C91FF /* realm_delegate.hpp */,
|
||||||
0270BC3E1B7CFC0D00010E03 /* RealmJS.h */,
|
0270BC3E1B7CFC0D00010E03 /* RealmJS.h */,
|
||||||
0270BC3F1B7CFC0D00010E03 /* RealmJS.mm */,
|
0270BC3F1B7CFC0D00010E03 /* RealmJS.mm */,
|
||||||
02258FB11BC6E2D00075F13A /* RealmRPC.h */,
|
02258FB11BC6E2D00075F13A /* RealmRPC.hpp */,
|
||||||
02258FB21BC6E2D00075F13A /* RealmRPC.mm */,
|
02258FB21BC6E2D00075F13A /* RealmRPC.mm */,
|
||||||
0270BC401B7CFC0D00010E03 /* RJSList.cpp */,
|
0270BC401B7CFC0D00010E03 /* RJSList.cpp */,
|
||||||
0270BC411B7CFC0D00010E03 /* RJSList.hpp */,
|
0270BC411B7CFC0D00010E03 /* RJSList.hpp */,
|
||||||
|
@ -299,7 +301,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0270BCCF1B7D067300010E03 /* RealmReact.h */,
|
0270BCCF1B7D067300010E03 /* RealmReact.h */,
|
||||||
0270BCD01B7D067300010E03 /* RealmReact.m */,
|
0270BCD01B7D067300010E03 /* RealmReact.mm */,
|
||||||
);
|
);
|
||||||
name = RealmReact;
|
name = RealmReact;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -387,7 +389,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0270BC4C1B7CFC0D00010E03 /* RealmJS.h in Headers */,
|
0270BC4C1B7CFC0D00010E03 /* RealmJS.h in Headers */,
|
||||||
02258FB31BC6E2D00075F13A /* RealmRPC.h in Headers */,
|
02258FB31BC6E2D00075F13A /* RealmRPC.hpp in Headers */,
|
||||||
0270BC4F1B7CFC0D00010E03 /* RJSList.hpp in Headers */,
|
0270BC4F1B7CFC0D00010E03 /* RJSList.hpp in Headers */,
|
||||||
0270BC541B7CFC0D00010E03 /* RJSResults.hpp in Headers */,
|
0270BC541B7CFC0D00010E03 /* RJSResults.hpp in Headers */,
|
||||||
0270BC581B7CFC0D00010E03 /* RJSUtil.hpp in Headers */,
|
0270BC581B7CFC0D00010E03 /* RJSUtil.hpp in Headers */,
|
||||||
|
@ -626,7 +628,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0270BCD11B7D067300010E03 /* RealmReact.m in Sources */,
|
0270BCD11B7D067300010E03 /* RealmReact.mm in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -706,6 +708,7 @@
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
||||||
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
||||||
|
"$(SRCROOT)/vendor",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = src/Info.plist;
|
INFOPLIST_FILE = src/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
@ -739,6 +742,7 @@
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
||||||
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
||||||
|
"$(SRCROOT)/vendor",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = src/Info.plist;
|
INFOPLIST_FILE = src/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
@ -772,6 +776,7 @@
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
||||||
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
||||||
|
"$(SRCROOT)/vendor",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = src/Info.plist;
|
INFOPLIST_FILE = src/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
@ -902,6 +907,7 @@
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/vendor",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = src/Info.plist;
|
INFOPLIST_FILE = src/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
@ -935,6 +941,7 @@
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/vendor",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = src/Info.plist;
|
INFOPLIST_FILE = src/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
@ -966,6 +973,7 @@
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "";
|
||||||
INFOPLIST_FILE = tests/Info.plist;
|
INFOPLIST_FILE = tests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
@ -987,6 +995,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
build/iOS,
|
build/iOS,
|
||||||
);
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "";
|
||||||
INFOPLIST_FILE = tests/Info.plist;
|
INFOPLIST_FILE = tests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
@ -1068,6 +1077,7 @@
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/vendor",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = src/Info.plist;
|
INFOPLIST_FILE = src/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
@ -1099,6 +1109,7 @@
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "";
|
||||||
INFOPLIST_FILE = tests/Info.plist;
|
INFOPLIST_FILE = tests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <JavaScriptCore/JavaScriptCore.h>
|
#import <JavaScriptCore/JavaScriptCore.h>
|
||||||
#import <RealmJS/RealmRPC.h>
|
|
||||||
|
|
||||||
@interface RealmJS : NSObject
|
@interface RealmJS : NSObject
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Copyright 2015 Realm Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface RJSRPCServer : NSObject
|
|
||||||
|
|
||||||
- (NSDictionary *)performRequest:(NSString *)name args:(NSDictionary *)args;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2015 Realm Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#import "json.hpp"
|
||||||
|
#import <JavaScriptCore/JavaScriptCore.h>
|
||||||
|
|
||||||
|
namespace realm {
|
||||||
|
class ObjectSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace realm_js {
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
using RPCObjectID = u_int64_t;
|
||||||
|
using RPCRequest = std::function<json(const json)>;
|
||||||
|
|
||||||
|
class RPCServer {
|
||||||
|
public:
|
||||||
|
RPCServer();
|
||||||
|
~RPCServer();
|
||||||
|
json perform_request(std::string name, json &args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
JSGlobalContextRef m_context;
|
||||||
|
std::map<std::string, RPCRequest> m_requests;
|
||||||
|
std::map<RPCObjectID, JSObjectRef> m_objects;
|
||||||
|
RPCObjectID m_session_id;
|
||||||
|
|
||||||
|
RPCObjectID store_object(JSObjectRef object);
|
||||||
|
|
||||||
|
json serialize_json_value(JSValueRef value);
|
||||||
|
JSValueRef deserialize_json_value(const json dict);
|
||||||
|
|
||||||
|
json serialize_object_schema(realm::ObjectSchema &objectSchema);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
544
src/RealmRPC.mm
544
src/RealmRPC.mm
|
@ -16,8 +16,7 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#import "RealmRPC.h"
|
#import "RealmRPC.hpp"
|
||||||
#import <JavaScriptCore/JavaScriptCore.h>
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -33,371 +32,332 @@
|
||||||
#include "shared_realm.hpp"
|
#include "shared_realm.hpp"
|
||||||
#include "results.hpp"
|
#include "results.hpp"
|
||||||
|
|
||||||
using RPCObjectID = u_int64_t;
|
using namespace realm_js;
|
||||||
using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
|
|
||||||
|
|
||||||
static const char * const RealmObjectTypesFunction = "ObjectTypesFUNCTION";
|
static const char * const RealmObjectTypesFunction = "ObjectTypesFUNCTION";
|
||||||
static const char * const RealmObjectTypesNotification = "ObjectTypesNOTIFICATION";
|
static const char * const RealmObjectTypesNotification = "ObjectTypesNOTIFICATION";
|
||||||
static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS";
|
static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS";
|
||||||
|
|
||||||
@implementation RJSRPCServer {
|
RPCServer::RPCServer() {
|
||||||
JSGlobalContextRef _context;
|
m_context = JSGlobalContextCreate(NULL);
|
||||||
std::map<std::string, RPCRequest> _requests;
|
|
||||||
std::map<RPCObjectID, JSObjectRef> _objects;
|
|
||||||
RPCObjectID _sessionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
// JavaScriptCore crashes when trying to walk up the native stack to print the stacktrace.
|
||||||
for (auto item : _objects) {
|
// FIXME: Avoid having to do this!
|
||||||
JSValueUnprotect(_context, item.second);
|
static void (*setIncludesNativeCallStack)(JSGlobalContextRef, bool) = (void (*)(JSGlobalContextRef, bool))dlsym(RTLD_DEFAULT, "JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions");
|
||||||
|
if (setIncludesNativeCallStack) {
|
||||||
|
setIncludesNativeCallStack(m_context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSGlobalContextRelease(_context);
|
m_requests["/create_session"] = [this](const json dict) {
|
||||||
_requests.clear();
|
[RealmJS initializeContext:m_context];
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init {
|
JSStringRef realm_string = RJSStringForString("Realm");
|
||||||
self = [super init];
|
JSObjectRef realm_constructor = RJSValidatedObjectProperty(m_context, JSContextGetGlobalObject(m_context), realm_string);
|
||||||
if (self) {
|
JSStringRelease(realm_string);
|
||||||
_context = JSGlobalContextCreate(NULL);
|
|
||||||
|
|
||||||
// JavaScriptCore crashes when trying to walk up the native stack to print the stacktrace.
|
m_session_id = store_object(realm_constructor);
|
||||||
// FIXME: Avoid having to do this!
|
return (json){{"result", m_session_id}};
|
||||||
static void (*setIncludesNativeCallStack)(JSGlobalContextRef, bool) = (void (*)(JSGlobalContextRef, bool))dlsym(RTLD_DEFAULT, "JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions");
|
};
|
||||||
if (setIncludesNativeCallStack) {
|
m_requests["/create_realm"] = [this](const json dict) {
|
||||||
setIncludesNativeCallStack(_context, false);
|
JSObjectRef realm_constructor = m_session_id ? m_objects[m_session_id] : NULL;
|
||||||
|
if (!realm_constructor) {
|
||||||
|
throw std::runtime_error("Realm constructor not found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
id _self = self;
|
json::array_t args = dict["arguments"];
|
||||||
__weak __typeof__(self) self = _self;
|
size_t arg_count = args.size();
|
||||||
|
JSValueRef arg_values[arg_count];
|
||||||
|
|
||||||
_requests["/create_session"] = [=](NSDictionary *dict) {
|
for (size_t i = 0; i < arg_count; i++) {
|
||||||
[RealmJS initializeContext:_context];
|
arg_values[i] = deserialize_json_value(args[i]);
|
||||||
|
|
||||||
JSStringRef realmString = RJSStringForString("Realm");
|
|
||||||
JSObjectRef realmConstructor = RJSValidatedObjectProperty(_context, JSContextGetGlobalObject(_context), realmString);
|
|
||||||
JSStringRelease(realmString);
|
|
||||||
|
|
||||||
_sessionID = [self storeObject:realmConstructor];
|
|
||||||
return @{@"result": @(_sessionID)};
|
|
||||||
};
|
|
||||||
_requests["/create_realm"] = [=](NSDictionary *dict) {
|
|
||||||
JSObjectRef realmConstructor = _sessionID ? _objects[_sessionID] : NULL;
|
|
||||||
if (!realmConstructor) {
|
|
||||||
throw std::runtime_error("Realm constructor not found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray *args = dict[@"arguments"];
|
|
||||||
NSUInteger argCount = args.count;
|
|
||||||
JSValueRef argValues[argCount];
|
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < argCount; i++) {
|
|
||||||
argValues[i] = [self deserializeDictionaryValue:args[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValueRef exception = NULL;
|
|
||||||
JSObjectRef realmObject = JSObjectCallAsConstructor(_context, realmConstructor, argCount, argValues, &exception);
|
|
||||||
|
|
||||||
if (exception) {
|
|
||||||
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
||||||
}
|
|
||||||
|
|
||||||
RPCObjectID realmId = [self storeObject:realmObject];
|
|
||||||
return @{@"result": @(realmId)};
|
|
||||||
};
|
|
||||||
_requests["/begin_transaction"] = [=](NSDictionary *dict) {
|
|
||||||
RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
|
|
||||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->begin_transaction();
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
_requests["/cancel_transaction"] = [=](NSDictionary *dict) {
|
|
||||||
RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
|
|
||||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->cancel_transaction();
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
_requests["/commit_transaction"] = [=](NSDictionary *dict) {
|
|
||||||
RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
|
|
||||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->commit_transaction();
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
_requests["/call_method"] = [=](NSDictionary *dict) {
|
|
||||||
return [self performObjectMethod:dict[@"name"]
|
|
||||||
args:dict[@"arguments"]
|
|
||||||
objectId:[dict[@"id"] unsignedLongValue]];
|
|
||||||
};
|
|
||||||
_requests["/get_property"] = [=](NSDictionary *dict) {
|
|
||||||
RPCObjectID oid = [dict[@"id"] unsignedLongValue];
|
|
||||||
id name = dict[@"name"];
|
|
||||||
JSValueRef value = NULL;
|
|
||||||
JSValueRef exception = NULL;
|
|
||||||
|
|
||||||
if ([name isKindOfClass:[NSNumber class]]) {
|
|
||||||
value = JSObjectGetPropertyAtIndex(_context, _objects[oid], [name unsignedIntValue], &exception);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JSStringRef propString = RJSStringForString([name UTF8String]);
|
|
||||||
value = JSObjectGetProperty(_context, _objects[oid], propString, &exception);
|
|
||||||
JSStringRelease(propString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception) {
|
|
||||||
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
||||||
}
|
|
||||||
return @{@"result": [self resultForJSValue:value]};
|
|
||||||
};
|
|
||||||
_requests["/set_property"] = [=](NSDictionary *dict) {
|
|
||||||
RPCObjectID oid = [dict[@"id"] unsignedLongValue];
|
|
||||||
id name = dict[@"name"];
|
|
||||||
JSValueRef value = [self deserializeDictionaryValue:dict[@"value"]];
|
|
||||||
JSValueRef exception = NULL;
|
|
||||||
|
|
||||||
if ([name isKindOfClass:[NSNumber class]]) {
|
|
||||||
JSObjectSetPropertyAtIndex(_context, _objects[oid], [name unsignedIntValue], value, &exception);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JSStringRef propString = RJSStringForString([name UTF8String]);
|
|
||||||
JSObjectSetProperty(_context, _objects[oid], propString, value, 0, &exception);
|
|
||||||
JSStringRelease(propString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception) {
|
|
||||||
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
||||||
}
|
|
||||||
return @{};
|
|
||||||
};
|
|
||||||
_requests["/dispose_object"] = [=](NSDictionary *dict) {
|
|
||||||
RPCObjectID oid = [dict[@"id"] unsignedLongValue];
|
|
||||||
JSValueUnprotect(_context, _objects[oid]);
|
|
||||||
_objects.erase(oid);
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
_requests["/clear_test_state"] = [=](NSDictionary *dict) {
|
|
||||||
for (auto object : _objects) {
|
|
||||||
// The session ID points to the Realm constructor object, which should remain.
|
|
||||||
if (object.first == _sessionID) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValueUnprotect(_context, object.second);
|
|
||||||
_objects.erase(object.first);
|
|
||||||
}
|
|
||||||
JSGarbageCollect(_context);
|
|
||||||
[RealmJS clearTestState];
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)performRequest:(NSString *)name args:(NSDictionary *)args {
|
|
||||||
// perform all realm ops on the main thread
|
|
||||||
RPCRequest action = _requests[name.UTF8String];
|
|
||||||
assert(action);
|
|
||||||
|
|
||||||
__block id response;
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
if (_sessionID != [args[@"sessionId"] unsignedLongValue]) {
|
|
||||||
response = @{@"error": @"Invalid session ID"};
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
JSValueRef exception = NULL;
|
||||||
response = action(args);
|
JSObjectRef realm_object = JSObjectCallAsConstructor(m_context, realm_constructor, arg_count, arg_values, &exception);
|
||||||
} catch (std::exception &exception) {
|
if (exception) {
|
||||||
response = @{@"error": [@"exception thrown: " stringByAppendingString:@(exception.what())]};
|
return (json){{"error", RJSStringForValue(m_context, exception)}};
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return response ?: @{};
|
RPCObjectID realm_id = store_object(realm_object);
|
||||||
|
return (json){{"result", realm_id}};
|
||||||
|
};
|
||||||
|
m_requests["/begin_transaction"] = [this](const json dict) {
|
||||||
|
RPCObjectID realm_id = dict["realmId"].get<RPCObjectID>();
|
||||||
|
RJSGetInternal<realm::SharedRealm *>(m_objects[realm_id])->get()->begin_transaction();
|
||||||
|
return json::object();
|
||||||
|
};
|
||||||
|
m_requests["/cancel_transaction"] = [this](const json dict) {
|
||||||
|
RPCObjectID realm_id = dict["realmId"].get<RPCObjectID>();
|
||||||
|
RJSGetInternal<realm::SharedRealm *>(m_objects[realm_id])->get()->cancel_transaction();
|
||||||
|
return json::object();
|
||||||
|
};
|
||||||
|
m_requests["/commit_transaction"] = [this](const json dict) {
|
||||||
|
RPCObjectID realm_id = dict["realmId"].get<RPCObjectID>();
|
||||||
|
RJSGetInternal<realm::SharedRealm *>(m_objects[realm_id])->get()->commit_transaction();
|
||||||
|
return json::object();
|
||||||
|
};
|
||||||
|
m_requests["/call_method"] = [this](const json dict) {
|
||||||
|
JSObjectRef object = m_objects[dict["id"].get<RPCObjectID>()];
|
||||||
|
JSStringRef method_string = RJSStringForString(dict["name"].get<std::string>());
|
||||||
|
JSObjectRef function = RJSValidatedObjectProperty(m_context, object, method_string);
|
||||||
|
JSStringRelease(method_string);
|
||||||
|
|
||||||
|
json args = dict["arguments"];
|
||||||
|
size_t count = args.size();
|
||||||
|
JSValueRef arg_values[count];
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
arg_values[i] = deserialize_json_value(args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValueRef exception = NULL;
|
||||||
|
JSValueRef result = JSObjectCallAsFunction(m_context, function, object, count, arg_values, &exception);
|
||||||
|
if (exception) {
|
||||||
|
return (json){{"error", RJSStringForValue(m_context, exception)}};
|
||||||
|
}
|
||||||
|
return (json){{"result", serialize_json_value(result)}};
|
||||||
|
};
|
||||||
|
m_requests["/get_property"] = [this](const json dict) {
|
||||||
|
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||||
|
json name = dict["name"];
|
||||||
|
JSValueRef value = NULL;
|
||||||
|
JSValueRef exception = NULL;
|
||||||
|
|
||||||
|
if (name.is_number()) {
|
||||||
|
value = JSObjectGetPropertyAtIndex(m_context, m_objects[oid], name.get<unsigned int>(), &exception);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JSStringRef prop_string = RJSStringForString(name.get<std::string>());
|
||||||
|
value = JSObjectGetProperty(m_context, m_objects[oid], prop_string, &exception);
|
||||||
|
JSStringRelease(prop_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception) {
|
||||||
|
return (json){{"error", RJSStringForValue(m_context, exception)}};
|
||||||
|
}
|
||||||
|
return (json){{"result", serialize_json_value(value)}};
|
||||||
|
};
|
||||||
|
m_requests["/set_property"] = [this](const json dict) {
|
||||||
|
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||||
|
json name = dict["name"];
|
||||||
|
JSValueRef value = deserialize_json_value(dict["value"]);
|
||||||
|
JSValueRef exception = NULL;
|
||||||
|
|
||||||
|
if (name.is_number()) {
|
||||||
|
JSObjectSetPropertyAtIndex(m_context, m_objects[oid], name.get<unsigned int>(), value, &exception);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JSStringRef prop_string = RJSStringForString(name.get<std::string>());
|
||||||
|
JSObjectSetProperty(m_context, m_objects[oid], prop_string, value, 0, &exception);
|
||||||
|
JSStringRelease(prop_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception) {
|
||||||
|
return (json){{"error", RJSStringForValue(m_context, exception)}};
|
||||||
|
}
|
||||||
|
return json::object();
|
||||||
|
};
|
||||||
|
m_requests["/dispose_object"] = [this](const json dict) {
|
||||||
|
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||||
|
JSValueUnprotect(m_context, m_objects[oid]);
|
||||||
|
m_objects.erase(oid);
|
||||||
|
return json::object();
|
||||||
|
};
|
||||||
|
m_requests["/clear_test_state"] = [this](const json dict) {
|
||||||
|
for (auto object : m_objects) {
|
||||||
|
// The session ID points to the Realm constructor object, which should remain.
|
||||||
|
if (object.first == m_session_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValueUnprotect(m_context, object.second);
|
||||||
|
m_objects.erase(object.first);
|
||||||
|
}
|
||||||
|
JSGarbageCollect(m_context);
|
||||||
|
[RealmJS clearTestState];
|
||||||
|
return json::object();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)performObjectMethod:(NSString *)method args:(NSArray *)args objectId:(RPCObjectID)oid {
|
RPCServer::~RPCServer() {
|
||||||
JSObjectRef object = _objects[oid];
|
for (auto item : m_objects) {
|
||||||
JSStringRef methodString = RJSStringForString(method.UTF8String);
|
JSValueUnprotect(m_context, item.second);
|
||||||
JSObjectRef function = RJSValidatedObjectProperty(_context, object, methodString);
|
|
||||||
JSStringRelease(methodString);
|
|
||||||
|
|
||||||
NSUInteger argCount = args.count;
|
|
||||||
JSValueRef argValues[argCount];
|
|
||||||
for (NSUInteger i = 0; i < argCount; i++) {
|
|
||||||
argValues[i] = [self deserializeDictionaryValue:args[i]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValueRef exception = NULL;
|
JSGlobalContextRelease(m_context);
|
||||||
JSValueRef result = JSObjectCallAsFunction(_context, function, object, argCount, argValues, &exception);
|
|
||||||
|
|
||||||
if (exception) {
|
|
||||||
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
||||||
}
|
|
||||||
return @{@"result": [self resultForJSValue:result]};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RPCObjectID)storeObject:(JSObjectRef)object {
|
json RPCServer::perform_request(std::string name, json &args) {
|
||||||
|
try {
|
||||||
|
RPCRequest action = m_requests[name];
|
||||||
|
assert(action);
|
||||||
|
|
||||||
|
if (name == "/create_session" || m_session_id == args["sessionId"].get<RPCObjectID>()) {
|
||||||
|
return action(args);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {{"error", "Invalid session ID"}};
|
||||||
|
}
|
||||||
|
} catch (std::exception &exception) {
|
||||||
|
return {{"error", (std::string)"exception thrown: " + exception.what()}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCObjectID RPCServer::store_object(JSObjectRef object) {
|
||||||
static RPCObjectID s_next_id = 1;
|
static RPCObjectID s_next_id = 1;
|
||||||
RPCObjectID next_id = s_next_id++;
|
RPCObjectID next_id = s_next_id++;
|
||||||
JSValueProtect(_context, object);
|
JSValueProtect(m_context, object);
|
||||||
_objects[next_id] = object;
|
m_objects[next_id] = object;
|
||||||
return next_id;
|
return next_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)resultForJSValue:(JSValueRef)value {
|
json RPCServer::serialize_json_value(JSValueRef value) {
|
||||||
switch (JSValueGetType(_context, value)) {
|
switch (JSValueGetType(m_context, value)) {
|
||||||
case kJSTypeUndefined:
|
case kJSTypeUndefined:
|
||||||
return @{};
|
return json::object();
|
||||||
case kJSTypeNull:
|
case kJSTypeNull:
|
||||||
return @{@"value": [NSNull null]};
|
return {{"value", json(nullptr)}};
|
||||||
case kJSTypeBoolean:
|
case kJSTypeBoolean:
|
||||||
return @{@"value": @(JSValueToBoolean(_context, value))};
|
return {{"value", JSValueToBoolean(m_context, value)}};
|
||||||
case kJSTypeNumber:
|
case kJSTypeNumber:
|
||||||
return @{@"value": @(JSValueToNumber(_context, value, NULL))};
|
return {{"value", JSValueToNumber(m_context, value, NULL)}};
|
||||||
case kJSTypeString:
|
case kJSTypeString:
|
||||||
return @{@"value": @(RJSStringForValue(_context, value).c_str())};
|
return {{"value", RJSStringForValue(m_context, value)}};
|
||||||
case kJSTypeObject:
|
case kJSTypeObject:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObjectRef jsObject = JSValueToObject(_context, value, NULL);
|
JSObjectRef js_object = JSValueToObject(m_context, value, NULL);
|
||||||
|
|
||||||
if (JSValueIsObjectOfClass(_context, value, RJSObjectClass())) {
|
if (JSValueIsObjectOfClass(m_context, value, RJSObjectClass())) {
|
||||||
realm::Object *object = RJSGetInternal<realm::Object *>(jsObject);
|
realm::Object *object = RJSGetInternal<realm::Object *>(js_object);
|
||||||
RPCObjectID oid = [self storeObject:jsObject];
|
return {
|
||||||
return @{
|
{"type", RJSTypeGet(realm::PropertyTypeObject)},
|
||||||
@"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()),
|
{"id", store_object(js_object)},
|
||||||
@"id": @(oid),
|
{"schema", serialize_object_schema(object->object_schema)}
|
||||||
@"schema": [self objectSchemaToJSONObject:object->object_schema]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (JSValueIsObjectOfClass(_context, value, RJSListClass())) {
|
else if (JSValueIsObjectOfClass(m_context, value, RJSListClass())) {
|
||||||
realm::List *list = RJSGetInternal<realm::List *>(jsObject);
|
realm::List *list = RJSGetInternal<realm::List *>(js_object);
|
||||||
RPCObjectID oid = [self storeObject:jsObject];
|
return {
|
||||||
return @{
|
{"type", RJSTypeGet(realm::PropertyTypeArray)},
|
||||||
@"type": @(RJSTypeGet(realm::PropertyTypeArray).c_str()),
|
{"id", store_object(js_object)},
|
||||||
@"id": @(oid),
|
{"size", list->link_view->size()},
|
||||||
@"size": @(list->link_view->size()),
|
{"schema", serialize_object_schema(list->object_schema)}
|
||||||
@"schema": [self objectSchemaToJSONObject:list->object_schema]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (JSValueIsObjectOfClass(_context, value, RJSResultsClass())) {
|
else if (JSValueIsObjectOfClass(m_context, value, RJSResultsClass())) {
|
||||||
realm::Results *results = RJSGetInternal<realm::Results *>(jsObject);
|
realm::Results *results = RJSGetInternal<realm::Results *>(js_object);
|
||||||
RPCObjectID oid = [self storeObject:jsObject];
|
return {
|
||||||
return @{
|
{"type", RealmObjectTypesResults},
|
||||||
@"type": @(RealmObjectTypesResults),
|
{"id", store_object(js_object)},
|
||||||
@"id": @(oid),
|
{"size", results->size()},
|
||||||
@"size": @(results->size()),
|
{"schema", serialize_object_schema(results->object_schema)}
|
||||||
@"schema": [self objectSchemaToJSONObject:results->object_schema]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (JSValueIsObjectOfClass(_context, value, RJSNotificationClass())) {
|
else if (JSValueIsObjectOfClass(m_context, value, RJSNotificationClass())) {
|
||||||
RPCObjectID oid = [self storeObject:jsObject];
|
return {
|
||||||
return @{
|
{"type", RealmObjectTypesNotification},
|
||||||
@"type": @(RealmObjectTypesNotification),
|
{"id", store_object(js_object)},
|
||||||
@"id": @(oid),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (RJSIsValueArray(_context, value)) {
|
else if (RJSIsValueArray(m_context, value)) {
|
||||||
size_t length = RJSValidatedListLength(_context, jsObject);
|
size_t length = RJSValidatedListLength(m_context, js_object);
|
||||||
NSMutableArray *array = [NSMutableArray new];
|
std::vector<json> array;
|
||||||
for (unsigned int i = 0; i < length; i++) {
|
for (unsigned int i = 0; i < length; i++) {
|
||||||
[array addObject:[self resultForJSValue:JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)]];
|
array.push_back(serialize_json_value(JSObjectGetPropertyAtIndex(m_context, js_object, i, NULL)));
|
||||||
}
|
}
|
||||||
return @{@"value": array};
|
return {{"value", array}};
|
||||||
}
|
}
|
||||||
else if (RJSIsValueDate(_context, value)) {
|
else if (RJSIsValueDate(m_context, value)) {
|
||||||
return @{
|
return {
|
||||||
@"type": @(RJSTypeGet(realm::PropertyTypeDate).c_str()),
|
{"type", RJSTypeGet(realm::PropertyTypeDate)},
|
||||||
@"value": @(RJSValidatedValueToNumber(_context, value)),
|
{"value", RJSValidatedValueToNumber(m_context, value)},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
assert(0);
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)objectSchemaToJSONObject:(realm::ObjectSchema &)objectSchema {
|
json RPCServer::serialize_object_schema(realm::ObjectSchema &object_schema) {
|
||||||
NSMutableArray *properties = [[NSMutableArray alloc] init];
|
json properties = json::array();
|
||||||
|
for (realm::Property prop : object_schema.properties) {
|
||||||
for (realm::Property prop : objectSchema.properties) {
|
properties.push_back({
|
||||||
NSDictionary *dict = @{
|
{"name", prop.name},
|
||||||
@"name": @(prop.name.c_str()),
|
{"type", RJSTypeGet(prop.type)},
|
||||||
@"type": @(RJSTypeGet(prop.type).c_str()),
|
});
|
||||||
};
|
|
||||||
|
|
||||||
[properties addObject:dict];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return @{
|
return {
|
||||||
@"name": @(objectSchema.name.c_str()),
|
{"name", object_schema.name},
|
||||||
@"properties": properties,
|
{"properties", properties},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
- (JSValueRef)deserializeDictionaryValue:(NSDictionary *)dict {
|
JSValueRef RPCServer::deserialize_json_value(const json dict)
|
||||||
RPCObjectID oid = [dict[@"id"] longValue];
|
{
|
||||||
if (oid) {
|
json oid = dict["id"];
|
||||||
return _objects[oid];
|
if (oid.is_number()) {
|
||||||
|
return m_objects[oid.get<RPCObjectID>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *type = dict[@"type"];
|
json value = dict["value"];
|
||||||
id value = dict[@"value"];
|
json type = dict["type"];
|
||||||
|
if (type.is_string()) {
|
||||||
|
std::string type_string = type.get<std::string>();
|
||||||
|
if (type_string == RealmObjectTypesFunction) {
|
||||||
|
// FIXME: Make this actually call the function by its id once we need it to.
|
||||||
|
JSStringRef js_body = JSStringCreateWithUTF8CString("");
|
||||||
|
JSObjectRef js_function = JSObjectMakeFunction(m_context, NULL, 0, NULL, js_body, NULL, 1, NULL);
|
||||||
|
JSStringRelease(js_body);
|
||||||
|
|
||||||
if ([type isEqualToString:@(RealmObjectTypesFunction)]) {
|
return js_function;
|
||||||
// FIXME: Make this actually call the function by its id once we need it to.
|
}
|
||||||
JSStringRef jsBody = JSStringCreateWithUTF8CString("");
|
else if (type_string == RJSTypeGet(realm::PropertyTypeDate)) {
|
||||||
JSObjectRef jsFunction = JSObjectMakeFunction(_context, NULL, 0, NULL, jsBody, NULL, 1, NULL);
|
JSValueRef exception = NULL;
|
||||||
JSStringRelease(jsBody);
|
JSValueRef time = JSValueMakeNumber(m_context, value.get<double>());
|
||||||
|
JSObjectRef date = JSObjectMakeDate(m_context, 1, &time, &exception);
|
||||||
return jsFunction;
|
|
||||||
}
|
if (exception) {
|
||||||
else if ([type isEqualToString:@(RJSTypeGet(realm::PropertyTypeDate).c_str())]) {
|
throw RJSException(m_context, exception);
|
||||||
JSValueRef exception = NULL;
|
}
|
||||||
JSValueRef time = JSValueMakeNumber(_context, [value doubleValue]);
|
return date;
|
||||||
JSObjectRef date = JSObjectMakeDate(_context, 1, &time, &exception);
|
|
||||||
|
|
||||||
if (exception) {
|
|
||||||
throw RJSException(_context, exception);
|
|
||||||
}
|
}
|
||||||
return date;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value) {
|
if (value.is_null()) {
|
||||||
return JSValueMakeUndefined(_context);
|
return JSValueMakeNull(m_context);
|
||||||
}
|
}
|
||||||
else if ([value isKindOfClass:[NSNull class]]) {
|
else if (value.is_boolean()) {
|
||||||
return JSValueMakeNull(_context);
|
return JSValueMakeBoolean(m_context, value.get<bool>());
|
||||||
}
|
}
|
||||||
else if ([value isKindOfClass:[@YES class]]) {
|
else if (value.is_number()) {
|
||||||
return JSValueMakeBoolean(_context, [value boolValue]);
|
return JSValueMakeNumber(m_context, value.get<double>());
|
||||||
}
|
}
|
||||||
else if ([value isKindOfClass:[NSNumber class]]) {
|
else if (value.is_string()) {
|
||||||
return JSValueMakeNumber(_context, [value doubleValue]);
|
return RJSValueForString(m_context, value.get<std::string>());
|
||||||
}
|
}
|
||||||
else if ([value isKindOfClass:[NSString class]]) {
|
else if (value.is_array()) {
|
||||||
return RJSValueForString(_context, std::string([value UTF8String]));
|
size_t count = value.size();
|
||||||
}
|
JSValueRef js_values[count];
|
||||||
else if ([value isKindOfClass:[NSArray class]]) {
|
|
||||||
NSUInteger count = [value count];
|
|
||||||
JSValueRef jsValues[count];
|
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
jsValues[i] = [self deserializeDictionaryValue:value[i]];
|
js_values[i] = deserialize_json_value(value.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSObjectMakeArray(_context, count, jsValues, NULL);
|
return JSObjectMakeArray(m_context, count, js_values, NULL);
|
||||||
}
|
}
|
||||||
else if ([value isKindOfClass:[NSDictionary class]]) {
|
else if (value.is_object()) {
|
||||||
JSObjectRef jsObject = JSObjectMake(_context, NULL, NULL);
|
JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL);
|
||||||
|
|
||||||
for (NSString *key in value) {
|
for (json::iterator it = value.begin(); it != value.end(); ++it) {
|
||||||
JSValueRef jsValue = [self deserializeDictionaryValue:value[key]];
|
JSValueRef js_value = deserialize_json_value(it.value());
|
||||||
JSStringRef jsKey = JSStringCreateWithCFString((__bridge CFStringRef)key);
|
JSStringRef js_key = JSStringCreateWithUTF8CString(it.key().c_str());
|
||||||
|
|
||||||
JSObjectSetProperty(_context, jsObject, jsKey, jsValue, 0, NULL);
|
JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL);
|
||||||
JSStringRelease(jsKey);
|
JSStringRelease(js_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsObject;
|
return js_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSValueMakeUndefined(_context);
|
return JSValueMakeUndefined(m_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ static id<RCTJavaScriptExecutor> s_currentJavaScriptExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_currentJavaScriptExecutor = [bridge valueForKey:@"javaScriptExecutor"];
|
s_currentJavaScriptExecutor = [bridge valueForKey:@"javaScriptExecutor"];
|
||||||
|
assert(s_currentJavaScriptExecutor);
|
||||||
|
|
||||||
// FIXME: Remove this nonsense once the crashes go away when a test fails!
|
// FIXME: Remove this nonsense once the crashes go away when a test fails!
|
||||||
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(s_currentJavaScriptExecutor, false);
|
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(s_currentJavaScriptExecutor, false);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue