mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-10 14:25:58 +00:00
partial port of rpc server to cpp
This commit is contained in:
parent
33edd04897
commit
418d6dfc1c
@ -7,7 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
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 */; };
|
||||
@ -156,7 +156,7 @@
|
||||
/* End PBXCopyFilesBuildPhase 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>"; };
|
||||
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>"; };
|
||||
@ -276,7 +276,7 @@
|
||||
02601F071BA0F0CD007C91FF /* realm_delegate.hpp */,
|
||||
0270BC3E1B7CFC0D00010E03 /* RealmJS.h */,
|
||||
0270BC3F1B7CFC0D00010E03 /* RealmJS.mm */,
|
||||
02258FB11BC6E2D00075F13A /* RealmRPC.h */,
|
||||
02258FB11BC6E2D00075F13A /* RealmRPC.hpp */,
|
||||
02258FB21BC6E2D00075F13A /* RealmRPC.mm */,
|
||||
0270BC401B7CFC0D00010E03 /* RJSList.cpp */,
|
||||
0270BC411B7CFC0D00010E03 /* RJSList.hpp */,
|
||||
@ -387,7 +387,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0270BC4C1B7CFC0D00010E03 /* RealmJS.h in Headers */,
|
||||
02258FB31BC6E2D00075F13A /* RealmRPC.h in Headers */,
|
||||
02258FB31BC6E2D00075F13A /* RealmRPC.hpp in Headers */,
|
||||
0270BC4F1B7CFC0D00010E03 /* RJSList.hpp in Headers */,
|
||||
0270BC541B7CFC0D00010E03 /* RJSResults.hpp in Headers */,
|
||||
0270BC581B7CFC0D00010E03 /* RJSUtil.hpp in Headers */,
|
||||
@ -706,6 +706,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
||||
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
||||
"$(SRCROOT)/vendor",
|
||||
);
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -739,6 +740,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
||||
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
||||
"$(SRCROOT)/vendor",
|
||||
);
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -772,6 +774,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/examples/ReactExample/node_modules/react-native/React/",
|
||||
"$(SRCROOT)/tests/ReactTests/node_modules/react-native/React/",
|
||||
"$(SRCROOT)/vendor",
|
||||
);
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -902,6 +905,7 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/vendor",
|
||||
);
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -935,6 +939,7 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/vendor",
|
||||
);
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -966,6 +971,7 @@
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
@ -987,6 +993,7 @@
|
||||
"$(inherited)",
|
||||
build/iOS,
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
@ -1068,6 +1075,7 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/vendor",
|
||||
);
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -1099,6 +1107,7 @@
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
|
@ -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
|
55
src/RealmRPC.hpp
Normal file
55
src/RealmRPC.hpp
Normal file
@ -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(const std::string &name, const json args);
|
||||
|
||||
private:
|
||||
JSGlobalContextRef _context;
|
||||
std::map<std::string, RPCRequest> _requests;
|
||||
std::map<RPCObjectID, JSObjectRef> _objects;
|
||||
RPCObjectID _sessionID;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
416
src/RealmRPC.mm
416
src/RealmRPC.mm
@ -16,8 +16,7 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#import "RealmRPC.h"
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
#import "RealmRPC.hpp"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <map>
|
||||
@ -33,205 +32,184 @@
|
||||
#include "shared_realm.hpp"
|
||||
#include "results.hpp"
|
||||
|
||||
using RPCObjectID = u_int64_t;
|
||||
using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
|
||||
using namespace realm_js;
|
||||
|
||||
static const char * const RealmObjectTypesFunction = "ObjectTypesFUNCTION";
|
||||
static const char * const RealmObjectTypesNotification = "ObjectTypesNOTIFICATION";
|
||||
static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS";
|
||||
|
||||
@implementation RJSRPCServer {
|
||||
JSGlobalContextRef _context;
|
||||
std::map<std::string, RPCRequest> _requests;
|
||||
std::map<RPCObjectID, JSObjectRef> _objects;
|
||||
RPCObjectID _sessionID;
|
||||
RPCServer::RPCServer() {
|
||||
_context = JSGlobalContextCreate(NULL);
|
||||
|
||||
// JavaScriptCore crashes when trying to walk up the native stack to print the stacktrace.
|
||||
// FIXME: Avoid having to do this!
|
||||
static void (*setIncludesNativeCallStack)(JSGlobalContextRef, bool) = (void (*)(JSGlobalContextRef, bool))dlsym(RTLD_DEFAULT, "JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions");
|
||||
if (setIncludesNativeCallStack) {
|
||||
setIncludesNativeCallStack(_context, false);
|
||||
}
|
||||
|
||||
_requests["/create_session"] = [=](const json dict) {
|
||||
[RealmJS initializeContext:_context];
|
||||
|
||||
JSStringRef realm_string = RJSStringForString("Realm");
|
||||
JSObjectRef realm_constructor = RJSValidatedObjectProperty(_context, JSContextGetGlobalObject(_context), realm_string);
|
||||
JSStringRelease(realm_string);
|
||||
|
||||
_sessionID = store_object(realm_constructor);
|
||||
return json({"result", _sessionID});
|
||||
};
|
||||
_requests["/create_realm"] = [=](const json dict) {
|
||||
JSObjectRef realm_constructor = _sessionID ? _objects[_sessionID] : NULL;
|
||||
if (!realm_constructor) {
|
||||
throw std::runtime_error("Realm constructor not found!");
|
||||
}
|
||||
|
||||
json::array_t args = dict["arguments"];
|
||||
size_t arg_count = args.size();
|
||||
JSValueRef arg_values[arg_count];
|
||||
|
||||
for (size_t i = 0; i < arg_count; i++) {
|
||||
arg_values[i] = deserialize_json_value(args[i]);
|
||||
}
|
||||
|
||||
JSValueRef exception = NULL;
|
||||
JSObjectRef realmObject = JSObjectCallAsConstructor(_context, realm_constructor, arg_count, arg_values, &exception);
|
||||
|
||||
if (exception) {
|
||||
return (json){"error", RJSStringForValue(_context, exception)};
|
||||
}
|
||||
|
||||
RPCObjectID realmId = store_object(realmObject);
|
||||
return (json){"result", realmId};
|
||||
};
|
||||
_requests["/begin_transaction"] = [=](const json dict) {
|
||||
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->begin_transaction();
|
||||
return nil;
|
||||
};
|
||||
_requests["/cancel_transaction"] = [=](const json dict) {
|
||||
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->cancel_transaction();
|
||||
return nil;
|
||||
};
|
||||
_requests["/commit_transaction"] = [=](const json dict) {
|
||||
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->commit_transaction();
|
||||
return nil;
|
||||
};
|
||||
_requests["/call_method"] = [=](const json dict) {
|
||||
JSObjectRef object = _objects[dict["id"].get<RPCObjectID>()];
|
||||
JSStringRef methodString = RJSStringForString(dict["name"].get<const char *>());
|
||||
JSObjectRef function = RJSValidatedObjectProperty(_context, object, methodString);
|
||||
JSStringRelease(methodString);
|
||||
|
||||
json args = dict["arguments"];
|
||||
size_t argCount = args.size();
|
||||
JSValueRef argValues[argCount];
|
||||
for (size_t i = 0; i < argCount; i++) {
|
||||
argValues[i] = deserialize_json_value(args[i]);
|
||||
}
|
||||
|
||||
JSValueRef exception = NULL;
|
||||
JSValueRef result = JSObjectCallAsFunction(_context, function, object, argCount, argValues, &exception);
|
||||
|
||||
if (exception) {
|
||||
return (json){"error", RJSStringForValue(_context, exception)};
|
||||
}
|
||||
return (json){"result", serialize_json_value(result)};
|
||||
};
|
||||
_requests["/get_property"] = [=](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(_context, _objects[oid], name.get<unsigned int>(), &exception);
|
||||
}
|
||||
else {
|
||||
JSStringRef propString = RJSStringForString(name.get<const char *>());
|
||||
value = JSObjectGetProperty(_context, _objects[oid], propString, &exception);
|
||||
JSStringRelease(propString);
|
||||
}
|
||||
|
||||
if (exception) {
|
||||
return (json){"error", RJSStringForValue(_context, exception)};
|
||||
}
|
||||
return (json){"result", serialize_json_value(value)};
|
||||
};
|
||||
_requests["/set_property"] = [=](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(_context, _objects[oid], name.get<unsigned int>(), value, &exception);
|
||||
}
|
||||
else {
|
||||
JSStringRef propString = RJSStringForString(name.get<const char *>());
|
||||
JSObjectSetProperty(_context, _objects[oid], propString, value, 0, &exception);
|
||||
JSStringRelease(propString);
|
||||
}
|
||||
|
||||
if (exception) {
|
||||
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
||||
}
|
||||
return @{};
|
||||
};
|
||||
_requests["/dispose_object"] = [=](const json dict) {
|
||||
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||
JSValueUnprotect(_context, _objects[oid]);
|
||||
_objects.erase(oid);
|
||||
return nil;
|
||||
};
|
||||
_requests["/clear_test_state"] = [=](const json 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;
|
||||
};
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
RPCServer::~RPCServer() {
|
||||
for (auto item : _objects) {
|
||||
JSValueUnprotect(_context, item.second);
|
||||
}
|
||||
|
||||
JSGlobalContextRelease(_context);
|
||||
_requests.clear();
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_context = JSGlobalContextCreate(NULL);
|
||||
|
||||
// JavaScriptCore crashes when trying to walk up the native stack to print the stacktrace.
|
||||
// FIXME: Avoid having to do this!
|
||||
static void (*setIncludesNativeCallStack)(JSGlobalContextRef, bool) = (void (*)(JSGlobalContextRef, bool))dlsym(RTLD_DEFAULT, "JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions");
|
||||
if (setIncludesNativeCallStack) {
|
||||
setIncludesNativeCallStack(_context, false);
|
||||
}
|
||||
|
||||
id _self = self;
|
||||
__weak __typeof__(self) self = _self;
|
||||
|
||||
_requests["/create_session"] = [=](NSDictionary *dict) {
|
||||
[RealmJS initializeContext:_context];
|
||||
|
||||
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 {
|
||||
json RPCServer::perform_request(const std::string &name, const json args) {
|
||||
// perform all realm ops on the main thread
|
||||
RPCRequest action = _requests[name.UTF8String];
|
||||
RPCRequest action = _requests[name];
|
||||
assert(action);
|
||||
|
||||
__block id response;
|
||||
__block json response;
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
if (_sessionID != [args[@"sessionId"] unsignedLongValue]) {
|
||||
response = @{@"error": @"Invalid session ID"};
|
||||
if (_sessionID != args["sessionId"].get<RPCObjectID>()) {
|
||||
response = {"error", "Invalid session ID"};
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
response = action(args);
|
||||
} catch (std::exception &exception) {
|
||||
response = @{@"error": [@"exception thrown: " stringByAppendingString:@(exception.what())]};
|
||||
response = {"error", (std::string)"exception thrown: " + exception.what()};
|
||||
}
|
||||
});
|
||||
return response ?: @{};
|
||||
return response ?: json();
|
||||
}
|
||||
|
||||
- (NSDictionary *)performObjectMethod:(NSString *)method args:(NSArray *)args objectId:(RPCObjectID)oid {
|
||||
JSObjectRef object = _objects[oid];
|
||||
JSStringRef methodString = RJSStringForString(method.UTF8String);
|
||||
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;
|
||||
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 {
|
||||
RPCObjectID RPCServer::store_object(JSObjectRef object) {
|
||||
static RPCObjectID s_next_id = 1;
|
||||
RPCObjectID next_id = s_next_id++;
|
||||
JSValueProtect(_context, object);
|
||||
@ -239,18 +217,18 @@ static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS";
|
||||
return next_id;
|
||||
}
|
||||
|
||||
- (NSDictionary *)resultForJSValue:(JSValueRef)value {
|
||||
json RPCServer::serialize_json_value(JSValueRef value) {
|
||||
switch (JSValueGetType(_context, value)) {
|
||||
case kJSTypeUndefined:
|
||||
return @{};
|
||||
return {};
|
||||
case kJSTypeNull:
|
||||
return @{@"value": [NSNull null]};
|
||||
return {"value", json(nullptr)};
|
||||
case kJSTypeBoolean:
|
||||
return @{@"value": @(JSValueToBoolean(_context, value))};
|
||||
return {"value", JSValueToBoolean(_context, value)};
|
||||
case kJSTypeNumber:
|
||||
return @{@"value": @(JSValueToNumber(_context, value, NULL))};
|
||||
return {"value", JSValueToNumber(_context, value, NULL)};
|
||||
case kJSTypeString:
|
||||
return @{@"value": @(RJSStringForValue(_context, value).c_str())};
|
||||
return {"value", RJSStringForValue(_context, value).c_str()};
|
||||
case kJSTypeObject:
|
||||
break;
|
||||
}
|
||||
@ -259,79 +237,71 @@ static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS";
|
||||
|
||||
if (JSValueIsObjectOfClass(_context, value, RJSObjectClass())) {
|
||||
realm::Object *object = RJSGetInternal<realm::Object *>(jsObject);
|
||||
RPCObjectID oid = [self storeObject:jsObject];
|
||||
return @{
|
||||
@"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()),
|
||||
@"id": @(oid),
|
||||
@"schema": [self objectSchemaToJSONObject:object->object_schema]
|
||||
return {
|
||||
"type", RJSTypeGet(realm::PropertyTypeObject).c_str(),
|
||||
"id", store_object(jsObject),
|
||||
"schema", serialize_object_schema(object->object_schema)
|
||||
};
|
||||
}
|
||||
else if (JSValueIsObjectOfClass(_context, value, RJSListClass())) {
|
||||
realm::List *list = RJSGetInternal<realm::List *>(jsObject);
|
||||
RPCObjectID oid = [self storeObject:jsObject];
|
||||
return @{
|
||||
@"type": @(RJSTypeGet(realm::PropertyTypeArray).c_str()),
|
||||
@"id": @(oid),
|
||||
@"size": @(list->link_view->size()),
|
||||
@"schema": [self objectSchemaToJSONObject:list->object_schema]
|
||||
return {
|
||||
"type", RJSTypeGet(realm::PropertyTypeArray),
|
||||
"id", store_object(jsObject),
|
||||
"size", list->link_view->size(),
|
||||
"schema", serialize_object_schema(list->object_schema)
|
||||
};
|
||||
}
|
||||
else if (JSValueIsObjectOfClass(_context, value, RJSResultsClass())) {
|
||||
realm::Results *results = RJSGetInternal<realm::Results *>(jsObject);
|
||||
RPCObjectID oid = [self storeObject:jsObject];
|
||||
return @{
|
||||
@"type": @(RealmObjectTypesResults),
|
||||
@"id": @(oid),
|
||||
@"size": @(results->size()),
|
||||
@"schema": [self objectSchemaToJSONObject:results->object_schema]
|
||||
return {
|
||||
"type", RealmObjectTypesResults,
|
||||
"id", store_object(jsObject),
|
||||
"size", results->size(),
|
||||
"schema", serialize_object_schema(results->object_schema)
|
||||
};
|
||||
}
|
||||
else if (JSValueIsObjectOfClass(_context, value, RJSNotificationClass())) {
|
||||
RPCObjectID oid = [self storeObject:jsObject];
|
||||
return @{
|
||||
@"type": @(RealmObjectTypesNotification),
|
||||
@"id": @(oid),
|
||||
return {
|
||||
"type", RealmObjectTypesNotification,
|
||||
"id", store_object(jsObject),
|
||||
};
|
||||
}
|
||||
else if (RJSIsValueArray(_context, value)) {
|
||||
size_t length = RJSValidatedListLength(_context, jsObject);
|
||||
NSMutableArray *array = [NSMutableArray new];
|
||||
std::vector<json> array;
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
[array addObject:[self resultForJSValue:JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)]];
|
||||
array.push_back(serialize_json_value(JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)));
|
||||
}
|
||||
return @{@"value": array};
|
||||
return {"value", array};
|
||||
}
|
||||
else if (RJSIsValueDate(_context, value)) {
|
||||
return @{
|
||||
@"type": @(RJSTypeGet(realm::PropertyTypeDate).c_str()),
|
||||
@"value": @(RJSValidatedValueToNumber(_context, value)),
|
||||
return {
|
||||
"type", RJSTypeGet(realm::PropertyTypeDate),
|
||||
"value", RJSValidatedValueToNumber(_context, value),
|
||||
};
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
- (NSDictionary *)objectSchemaToJSONObject:(realm::ObjectSchema &)objectSchema {
|
||||
NSMutableArray *properties = [[NSMutableArray alloc] init];
|
||||
|
||||
json RPCServer::serialize_object_schema(realm::ObjectSchema &objectSchema) {
|
||||
json properties({});
|
||||
for (realm::Property prop : objectSchema.properties) {
|
||||
NSDictionary *dict = @{
|
||||
@"name": @(prop.name.c_str()),
|
||||
@"type": @(RJSTypeGet(prop.type).c_str()),
|
||||
};
|
||||
|
||||
[properties addObject:dict];
|
||||
properties.push_back({
|
||||
"name", prop.name,
|
||||
"type", RJSTypeGet(prop.type),
|
||||
});
|
||||
}
|
||||
|
||||
return @{
|
||||
@"name": @(objectSchema.name.c_str()),
|
||||
@"properties": properties,
|
||||
return {
|
||||
"name", objectSchema.name,
|
||||
"properties", properties,
|
||||
};
|
||||
}
|
||||
|
||||
- (JSValueRef)deserializeDictionaryValue:(NSDictionary *)dict {
|
||||
RPCObjectID oid = [dict[@"id"] longValue];
|
||||
JSValueRef RPCServer::deserialize_json_value(const json dict)
|
||||
{
|
||||
RPCObjectID oid = dict["id"].get<long>();
|
||||
if (oid) {
|
||||
return _objects[oid];
|
||||
}
|
||||
|
7295
vendor/json.hpp
vendored
Normal file
7295
vendor/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user