diff --git a/ReactNative/RealmRPC.mm b/ReactNative/RealmRPC.mm index d380c1d2..09361f8e 100644 --- a/ReactNative/RealmRPC.mm +++ b/ReactNative/RealmRPC.mm @@ -24,6 +24,7 @@ #include "RealmJS.h" #include "RJSObject.hpp" #include "RJSResults.hpp" +#include "RJSArray.hpp" #include "RJSRealm.hpp" #include "RJSUtil.hpp" @@ -37,7 +38,6 @@ static std::map s_requests; static std::map s_objects; static JSGlobalContextRef s_context; -static RPCObjectID s_id_counter = 0; @implementation RJSRPCServer @@ -47,10 +47,9 @@ static RPCObjectID s_id_counter = 0; s_context = JSGlobalContextCreate(NULL); s_requests["/create_realm"] = [=](NSDictionary *dict) { - RPCObjectID realmId = s_id_counter++; JSValueRef value = [[JSValue valueWithObject:dict inContext:[JSContext contextWithJSGlobalContextRef:s_context]] JSValueRef]; - s_objects[realmId] = RealmConstructor(s_context, NULL, 1, &value, NULL); + RPCObjectID realmId = [self storeObject:RealmConstructor(s_context, NULL, 1, &value, NULL)]; return @{@"result": @(realmId)}; }; s_requests["/begin_transaction"] = [=](NSDictionary *dict) { @@ -69,15 +68,12 @@ static RPCObjectID s_id_counter = 0; return @{}; }; s_requests["/create_object"] = [=](NSDictionary *dict) { - RPCObjectID newOid = s_id_counter++; RPCObjectID realmId = [dict[@"realmId"] longValue]; JSValueRef value = [[JSValue valueWithObject:dict[@"value"] inContext:[JSContext contextWithJSGlobalContextRef:s_context]] JSValueRef]; JSValueRef exception = NULL; - JSValueRef object = RealmCreateObject(s_context, NULL, s_objects[realmId], 1, &value, &exception); - JSValueProtect(s_context, object); - s_objects[newOid] = (JSObjectRef)object; - return @{@"result": @{@"type": @"PropTypesOBJECT", @"id": @(newOid)}}; + RPCObjectID oid = [self storeObject:(JSObjectRef)RealmCreateObject(s_context, NULL, s_objects[realmId], 1, &value, &exception)]; + return @{@"result": @{@"type": @"PropTypesOBJECT", @"id": @(oid)}}; }; s_requests["/dispose_realm"] = [=](NSDictionary *dict) { RPCObjectID realmId = [dict[@"realmId"] longValue]; @@ -87,7 +83,8 @@ static RPCObjectID s_id_counter = 0; }; s_requests["/get_property"] = [=](NSDictionary *dict) { JSValueRef exception = NULL; - JSStringRef propString = RJSStringForString([dict[@"name"] UTF8String]); + NSString *name = dict[@"name"]; + JSStringRef propString = RJSStringForString(name.UTF8String); RPCObjectID objectId = [dict[@"objectId"] longValue]; JSValueRef propertyValue = ObjectGetProperty(s_context, s_objects[objectId], propString, &exception); JSStringRelease(propString); @@ -96,7 +93,8 @@ static RPCObjectID s_id_counter = 0; return @{@"error": @(RJSStringForValue(s_context, exception).c_str())}; } - return @{@"result": [self resultForJSValue:propertyValue]}; + realm::Property *prop = RJSGetInternal(s_objects[objectId])->object_schema.property_for_name(name.UTF8String); + return @{@"result": [self resultForJSValue:propertyValue type:prop->type]}; }; s_requests["/set_property"] = [=](NSDictionary *dict) { JSValueRef exception = NULL; @@ -116,7 +114,6 @@ static RPCObjectID s_id_counter = 0; return @{}; }; s_requests["/get_objects"] = [=](NSDictionary *dict) { - RPCObjectID resultsId = s_id_counter++; RPCObjectID realmId = [dict[@"realmId"] longValue]; JSValueRef arguments[2]; @@ -130,8 +127,7 @@ static RPCObjectID s_id_counter = 0; JSValueRef exception = NULL; JSValueRef results = RealmObjects(s_context, NULL, s_objects[realmId], argumentCount, arguments, &exception); - JSValueProtect(s_context, results); - s_objects[resultsId] = (JSObjectRef)results; + RPCObjectID resultsId = [self storeObject:(JSObjectRef)results]; size_t size = RJSGetInternal((JSObjectRef)results)->size(); return @{@"result": @{@"resultsId": @(resultsId), @"size": @(size)}}; }; @@ -156,7 +152,30 @@ static RPCObjectID s_id_counter = 0; return @{@"error": @(RJSStringForValue(s_context, exception).c_str())}; } - return @{@"result": [self resultForJSValue:objectValue]}; + return @{@"result": [self resultForJSValue:objectValue type:realm::PropertyTypeObject]}; + }; + s_requests["/get_list_size"] = [=](NSDictionary *dict) { + RPCObjectID listId = [dict[@"listId"] longValue]; + + JSValueRef exception = NULL; + static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length"); + JSValueRef lengthValue = ArrayGetProperty(s_context, s_objects[listId], lengthPropertyName, &exception); + return @{@"result": @(JSValueToNumber(s_context, lengthValue, &exception))}; + }; + s_requests["/get_list_item"] = [=](NSDictionary *dict) { + RPCObjectID listId = [dict[@"listId"] longValue]; + long index = [dict[@"index"] longValue]; + + JSValueRef exception = NULL; + JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str()); + JSValueRef objectValue = ArrayGetProperty(s_context, s_objects[listId], indexPropertyName, &exception); + JSStringRelease(indexPropertyName); + + if (exception) { + return @{@"error": @(RJSStringForValue(s_context, exception).c_str())}; + } + + return @{@"result": [self resultForJSValue:objectValue type:realm::PropertyTypeObject]}; }; // Add a handler to respond to GET requests on any URL @@ -169,10 +188,21 @@ static RPCObjectID s_id_counter = 0; [response setValue:@"http://localhost:8081" forAdditionalHeader:@"Access-Control-Allow-Origin"]; return response; }]; - [webServer startWithPort:8082 bonjourName:nil]; + + [webServer startWithOptions:@{GCDWebServerOption_MaxPendingConnections: @(1), + GCDWebServerOption_Port: @(8082)} + error:nil]; } -+ (NSDictionary *)resultForJSValue:(JSValueRef)value { ++ (RPCObjectID)storeObject:(JSObjectRef)object { + static RPCObjectID s_next_id = 0; + RPCObjectID next_id = s_next_id++; + JSValueProtect(s_context, object); + s_objects[next_id] = object; + return next_id; +} + ++ (NSDictionary *)resultForJSValue:(JSValueRef)value type:(realm::PropertyType)type { switch (JSValueGetType(s_context, value)) { case kJSTypeUndefined: return @{}; @@ -188,22 +218,28 @@ static RPCObjectID s_id_counter = 0; break; } - RPCObjectID newOid = s_id_counter++; - JSValueProtect(s_context, value); - s_objects[newOid] = (JSObjectRef)value; + JSObjectRef jsObject = JSValueToObject(s_context, value, NULL); + RPCObjectID oid = [self storeObject:jsObject]; - // TODO: Check for List object! - - realm::Object *object = RJSGetInternal((JSObjectRef)value); - return @{ - @"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()), - @"id": @(newOid), - @"schema": [self schemaForObject:object], - }; + if (type == realm::PropertyTypeObject) { + realm::Object *object = RJSGetInternal(jsObject); + return @{ + @"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()), + @"id": @(oid), + @"schema": [self objectSchemaToJSONObject:object->object_schema] + }; + } + else { + realm::ObjectArray *array = RJSGetInternal(jsObject); + return @{ + @"type": @(RJSTypeGet(realm::PropertyTypeArray).c_str()), + @"id": @(oid), + @"schema": [self objectSchemaToJSONObject:array->object_schema] + }; + } } -+ (NSDictionary *)schemaForObject:(realm::Object *)object { - realm::ObjectSchema &objectSchema = object->object_schema; ++ (NSDictionary *)objectSchemaToJSONObject:(realm::ObjectSchema &)objectSchema { NSMutableArray *properties = [[NSMutableArray alloc] init]; for (realm::Property prop : objectSchema.properties) { diff --git a/RealmJS.xcodeproj/project.pbxproj b/RealmJS.xcodeproj/project.pbxproj index 520e0302..1dd2431e 100644 --- a/RealmJS.xcodeproj/project.pbxproj +++ b/RealmJS.xcodeproj/project.pbxproj @@ -50,7 +50,6 @@ 0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; 0270BCD11B7D067300010E03 /* RealmReactModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0270BCD01B7D067300010E03 /* RealmReactModule.m */; }; 027799291BC3037900C96559 /* RealmRPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = 027799281BC3037900C96559 /* RealmRPC.mm */; settings = {ASSET_TAGS = (); }; }; - 02A3C7971BC4318600B1A7BE /* GCDWebServers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02A3C7941BC4317A00B1A7BE /* GCDWebServers.framework */; }; 02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; 02B58CCE1AE99D4D009B348C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02D456DA1B7E59A500EE1299 /* ArrayTests.js in Resources */ = {isa = PBXBuildFile; fileRef = 02D456D91B7E59A500EE1299 /* ArrayTests.js */; }; @@ -58,41 +57,6 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 02A3C78D1BC4317A00B1A7BE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DD76FB20486AB0100D96B5E; - remoteInfo = "GCDWebServer (Mac)"; - }; - 02A3C78F1BC4317A00B1A7BE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = E221125A1690B4DE0048D2B2; - remoteInfo = "GCDWebServer (iOS)"; - }; - 02A3C7911BC4317A00B1A7BE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CEE28CD11AE004D800F4023C; - remoteInfo = "GCDWebServers (Mac)"; - }; - 02A3C7931BC4317A00B1A7BE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CEE28CEF1AE0051F00F4023C; - remoteInfo = "GCDWebServers (iOS)"; - }; - 02A3C7951BC4317A00B1A7BE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = E24039251BA09207000B7089; - remoteInfo = "Tests (Mac)"; - }; 02B29A2F1B7CF7ED008A7E6B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 02B58CA81AE99CEB009B348C /* Project object */; @@ -169,7 +133,6 @@ 0270BCD01B7D067300010E03 /* RealmReactModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmReactModule.m; path = ReactNative/RealmReactModule.m; sourceTree = ""; }; 027799281BC3037900C96559 /* RealmRPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmRPC.mm; path = ReactNative/RealmRPC.mm; sourceTree = ""; }; 0277992A1BC3039100C96559 /* RealmRPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmRPC.h; path = ReactNative/RealmRPC.h; 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 /* libRealmReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CB11AE99CEC009B348C /* RealmJS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmJS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -183,7 +146,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 02A3C7971BC4318600B1A7BE /* GCDWebServers.framework in Frameworks */, 02B29A311B7CF86D008A7E6B /* RealmJS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -261,22 +223,9 @@ name = ReactNativeModule; sourceTree = ""; }; - 02A3C7851BC4317A00B1A7BE /* Products */ = { - isa = PBXGroup; - children = ( - 02A3C78E1BC4317A00B1A7BE /* GCDWebServer */, - 02A3C7901BC4317A00B1A7BE /* GCDWebServer.app */, - 02A3C7921BC4317A00B1A7BE /* GCDWebServers.framework */, - 02A3C7941BC4317A00B1A7BE /* GCDWebServers.framework */, - 02A3C7961BC4317A00B1A7BE /* Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; 02B58CA71AE99CEB009B348C = { isa = PBXGroup; children = ( - 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */, 02B58CCF1AE99D8C009B348C /* Frameworks */, 0270BC3D1B7CFBFD00010E03 /* RealmJS */, 02B58CC01AE99CEC009B348C /* RealmJSTests */, @@ -436,12 +385,6 @@ mainGroup = 02B58CA71AE99CEB009B348C; productRefGroup = 02B58CB21AE99CEC009B348C /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 02A3C7851BC4317A00B1A7BE /* Products */; - ProjectRef = 02A3C7841BC4317A00B1A7BE /* GCDWebServer.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 02B58CB01AE99CEC009B348C /* RealmJS */, @@ -451,44 +394,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - 02A3C78E1BC4317A00B1A7BE /* GCDWebServer */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.executable"; - path = GCDWebServer; - remoteRef = 02A3C78D1BC4317A00B1A7BE /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 02A3C7901BC4317A00B1A7BE /* GCDWebServer.app */ = { - isa = PBXReferenceProxy; - fileType = wrapper.application; - path = GCDWebServer.app; - remoteRef = 02A3C78F1BC4317A00B1A7BE /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 02A3C7921BC4317A00B1A7BE /* GCDWebServers.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = GCDWebServers.framework; - remoteRef = 02A3C7911BC4317A00B1A7BE /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 02A3C7941BC4317A00B1A7BE /* GCDWebServers.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = GCDWebServers.framework; - remoteRef = 02A3C7931BC4317A00B1A7BE /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 02A3C7961BC4317A00B1A7BE /* Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = Tests.xctest; - remoteRef = 02A3C7951BC4317A00B1A7BE /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ 02B58CAF1AE99CEC009B348C /* Resources */ = { isa = PBXResourcesBuildPhase; diff --git a/src/RJSArray.hpp b/src/RJSArray.hpp index 33c2a5c3..7c2b8f2e 100644 --- a/src/RJSArray.hpp +++ b/src/RJSArray.hpp @@ -36,3 +36,5 @@ namespace realm { JSClassRef RJSArrayClass(); JSObjectRef RJSArrayCreate(JSContextRef ctx, realm::ObjectArray *array); + +JSValueRef ArrayGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException);