2015-10-08 17:57:07 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#import "RealmRPC.h"
|
|
|
|
#import <JavaScriptCore/JavaScriptCore.h>
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include "RealmJS.h"
|
|
|
|
#include "RJSObject.hpp"
|
|
|
|
#include "RJSResults.hpp"
|
2015-10-13 22:27:24 +00:00
|
|
|
#include "RJSList.hpp"
|
2015-10-08 17:57:07 +00:00
|
|
|
#include "RJSRealm.hpp"
|
|
|
|
#include "RJSUtil.hpp"
|
|
|
|
|
|
|
|
#include "object_accessor.hpp"
|
|
|
|
#include "shared_realm.hpp"
|
|
|
|
#include "results.hpp"
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
using RPCObjectID = u_int64_t;
|
2015-10-08 17:57:07 +00:00
|
|
|
using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
|
|
|
|
|
|
|
|
@implementation RJSRPCServer {
|
2015-10-08 18:23:42 +00:00
|
|
|
JSGlobalContextRef _context;
|
|
|
|
std::map<std::string, RPCRequest> _requests;
|
|
|
|
std::map<RPCObjectID, JSObjectRef> _objects;
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 01:50:20 +00:00
|
|
|
- (void)dealloc {
|
|
|
|
for (auto item : _objects) {
|
|
|
|
JSValueUnprotect(_context, item.second);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSGlobalContextRelease(_context);
|
|
|
|
_requests.clear();
|
|
|
|
}
|
|
|
|
|
2015-10-08 17:57:07 +00:00
|
|
|
- (instancetype)init {
|
|
|
|
self = [super init];
|
|
|
|
if (self) {
|
2015-10-08 18:23:42 +00:00
|
|
|
_context = JSGlobalContextCreate(NULL);
|
2015-10-15 10:00:13 +00:00
|
|
|
|
2015-10-16 01:50:20 +00:00
|
|
|
id _self = self;
|
|
|
|
__weak __typeof__(self) self = _self;
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/create_realm"] = [=](NSDictionary *dict) {
|
2015-10-15 10:00:13 +00:00
|
|
|
NSArray *args = dict[@"arguments"];
|
|
|
|
NSUInteger argCount = args.count;
|
|
|
|
JSValueRef argValues[argCount];
|
|
|
|
|
|
|
|
for (NSUInteger i = 0; i < argCount; i++) {
|
2015-10-16 00:07:10 +00:00
|
|
|
argValues[i] = [self deserializeDictionaryValue:args[i]];
|
2015-10-15 10:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSObjectRef realmObject = RealmConstructor(_context, NULL, argCount, argValues, &exception);
|
|
|
|
|
|
|
|
if (exception) {
|
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
2015-10-08 18:23:42 +00:00
|
|
|
}
|
2015-10-15 10:00:13 +00:00
|
|
|
|
|
|
|
RPCObjectID realmId = [self storeObject:realmObject];
|
2015-10-08 17:57:07 +00:00
|
|
|
return @{@"result": @(realmId)};
|
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/begin_transaction"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
|
|
|
|
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->begin_transaction();
|
2015-10-15 10:00:13 +00:00
|
|
|
return nil;
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/cancel_transaction"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
|
|
|
|
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->cancel_transaction();
|
2015-10-15 10:00:13 +00:00
|
|
|
return nil;
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/commit_transaction"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
|
|
|
|
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->commit_transaction();
|
2015-10-15 10:00:13 +00:00
|
|
|
return nil;
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/call_realm_method"] = [=](NSDictionary *dict) {
|
2015-10-08 17:57:07 +00:00
|
|
|
NSString *name = dict[@"name"];
|
|
|
|
return [self performObjectMethod:name.UTF8String
|
|
|
|
classMethods:RJSRealmFuncs
|
|
|
|
args:dict[@"arguments"]
|
2015-10-08 18:23:42 +00:00
|
|
|
objectId:[dict[@"realmId"] unsignedLongValue]];
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/get_property"] = [=](NSDictionary *dict) {
|
2015-10-08 17:57:07 +00:00
|
|
|
JSValueRef exception = NULL;
|
|
|
|
NSString *name = dict[@"name"];
|
|
|
|
JSStringRef propString = RJSStringForString(name.UTF8String);
|
2015-10-08 18:23:42 +00:00
|
|
|
RPCObjectID objectId = [dict[@"objectId"] unsignedLongValue];
|
|
|
|
JSValueRef propertyValue = ObjectGetProperty(_context, _objects[objectId], propString, &exception);
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRelease(propString);
|
|
|
|
|
|
|
|
if (exception) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
return @{@"result": [self resultForJSValue:propertyValue]};
|
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/set_property"] = [=](NSDictionary *dict) {
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRef propString = RJSStringForString([dict[@"name"] UTF8String]);
|
2015-10-08 18:23:42 +00:00
|
|
|
RPCObjectID realmId = [dict[@"objectId"] unsignedLongValue];
|
2015-10-16 00:07:10 +00:00
|
|
|
JSValueRef value = [self deserializeDictionaryValue:dict[@"value"]];
|
2015-10-08 17:57:07 +00:00
|
|
|
JSValueRef exception = NULL;
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
ObjectSetProperty(_context, _objects[realmId], propString, value, &exception);
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRelease(propString);
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
return exception ? @{@"error": @(RJSStringForValue(_context, exception).c_str())} : @{};
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/dispose_object"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID oid = [dict[@"id"] unsignedLongValue];
|
|
|
|
JSValueUnprotect(_context, _objects[oid]);
|
|
|
|
_objects.erase(oid);
|
2015-10-15 10:00:13 +00:00
|
|
|
return nil;
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/get_results_size"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID resultsId = [dict[@"resultsId"] unsignedLongValue];
|
2015-10-08 17:57:07 +00:00
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length");
|
2015-10-08 18:23:42 +00:00
|
|
|
JSValueRef lengthValue = ResultsGetProperty(_context, _objects[resultsId], lengthPropertyName, &exception);
|
2015-10-08 22:30:29 +00:00
|
|
|
size_t length = JSValueToNumber(_context, lengthValue, &exception);
|
|
|
|
|
|
|
|
if (exception) {
|
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
|
|
}
|
|
|
|
return @{@"result": @(length)};
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/get_results_item"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID resultsId = [dict[@"resultsId"] unsignedLongValue];
|
2015-10-08 17:57:07 +00:00
|
|
|
long index = [dict[@"index"] longValue];
|
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str());
|
2015-10-08 18:23:42 +00:00
|
|
|
JSValueRef objectValue = ResultsGetProperty(_context, _objects[resultsId], indexPropertyName, &exception);
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRelease(indexPropertyName);
|
|
|
|
|
|
|
|
if (exception) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
return @{@"result": [self resultForJSValue:objectValue]};
|
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/get_list_size"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID listId = [dict[@"listId"] unsignedLongValue];
|
2015-10-08 17:57:07 +00:00
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length");
|
2015-10-13 22:56:43 +00:00
|
|
|
JSValueRef lengthValue = ListGetProperty(_context, _objects[listId], lengthPropertyName, &exception);
|
2015-10-08 22:30:29 +00:00
|
|
|
size_t length = JSValueToNumber(_context, lengthValue, &exception);
|
|
|
|
|
|
|
|
if (exception) {
|
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
|
|
}
|
|
|
|
return @{@"result": @(length)};
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/get_list_item"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID listId = [dict[@"listId"] unsignedLongValue];
|
2015-10-08 17:57:07 +00:00
|
|
|
long index = [dict[@"index"] longValue];
|
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str());
|
2015-10-13 22:56:43 +00:00
|
|
|
JSValueRef objectValue = ListGetProperty(_context, _objects[listId], indexPropertyName, &exception);
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRelease(indexPropertyName);
|
|
|
|
|
|
|
|
if (exception) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return @{@"result": [self resultForJSValue:objectValue]};
|
|
|
|
};
|
2015-10-16 00:07:10 +00:00
|
|
|
_requests["/set_list_item"] = [=](NSDictionary *dict) {
|
|
|
|
RPCObjectID listId = [dict[@"listId"] unsignedLongValue];
|
|
|
|
long index = [dict[@"index"] longValue];
|
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str());
|
|
|
|
JSValueRef value = [self deserializeDictionaryValue:dict[@"value"]];
|
|
|
|
ListSetProperty(_context, _objects[listId], indexPropertyName, value, &exception);
|
|
|
|
JSStringRelease(indexPropertyName);
|
|
|
|
|
|
|
|
if (exception) {
|
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
|
|
|
}
|
|
|
|
|
|
|
|
return @{};
|
|
|
|
};
|
2015-10-08 18:23:42 +00:00
|
|
|
_requests["/call_list_method"] = [=](NSDictionary *dict) {
|
2015-10-08 17:57:07 +00:00
|
|
|
NSString *name = dict[@"name"];
|
|
|
|
return [self performObjectMethod:name.UTF8String
|
2015-10-13 22:27:24 +00:00
|
|
|
classMethods:RJSListFuncs
|
2015-10-08 17:57:07 +00:00
|
|
|
args:dict[@"arguments"]
|
2015-10-08 18:23:42 +00:00
|
|
|
objectId:[dict[@"listId"] unsignedLongValue]];
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
2015-10-16 01:48:13 +00:00
|
|
|
_requests["/clear_test_state"] = [=](NSDictionary *dict) {
|
|
|
|
for (auto object : _objects) {
|
|
|
|
JSValueUnprotect(_context, object.second);
|
|
|
|
}
|
|
|
|
_objects.clear();
|
|
|
|
JSGarbageCollect(_context);
|
|
|
|
[RealmJS clearTestState];
|
2015-10-15 10:12:28 +00:00
|
|
|
return nil;
|
|
|
|
};
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)performRequest:(NSString *)name args:(NSDictionary *)args {
|
|
|
|
// perform all realm ops on the main thread
|
2015-10-08 18:23:42 +00:00
|
|
|
RPCRequest action = _requests[name.UTF8String];
|
2015-10-16 00:07:10 +00:00
|
|
|
assert(action);
|
|
|
|
|
2015-10-08 17:57:07 +00:00
|
|
|
__block id response;
|
|
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
2015-10-08 18:23:42 +00:00
|
|
|
try {
|
|
|
|
response = action(args);
|
|
|
|
} catch (std::exception &exception) {
|
|
|
|
response = @{@"error": [@"exception thrown: " stringByAppendingString:@(exception.what())]};
|
|
|
|
}
|
2015-10-08 17:57:07 +00:00
|
|
|
});
|
2015-10-15 10:00:13 +00:00
|
|
|
return response ?: @{};
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)performObjectMethod:(const char *)name
|
|
|
|
classMethods:(const JSStaticFunction [])methods
|
|
|
|
args:(NSArray *)args
|
|
|
|
objectId:(RPCObjectID)oid {
|
2015-10-15 10:00:13 +00:00
|
|
|
NSUInteger argCount = args.count;
|
|
|
|
JSValueRef argValues[argCount];
|
|
|
|
for (NSUInteger i = 0; i < argCount; i++) {
|
2015-10-16 00:07:10 +00:00
|
|
|
argValues[i] = [self deserializeDictionaryValue:args[i]];
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t index = 0;
|
|
|
|
while (methods[index].name) {
|
|
|
|
if (!strcmp(methods[index].name, name)) {
|
2015-10-15 10:00:13 +00:00
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSValueRef ret = methods[index].callAsFunction(_context, NULL, _objects[oid], argCount, argValues, &exception);
|
|
|
|
if (exception) {
|
|
|
|
return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
return @{@"result": [self resultForJSValue:ret]};
|
|
|
|
}
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return @{@"error": @"invalid method"};
|
|
|
|
}
|
|
|
|
|
|
|
|
- (RPCObjectID)storeObject:(JSObjectRef)object {
|
|
|
|
static RPCObjectID s_next_id = 1;
|
|
|
|
RPCObjectID next_id = s_next_id++;
|
2015-10-08 18:23:42 +00:00
|
|
|
JSValueProtect(_context, object);
|
|
|
|
_objects[next_id] = object;
|
2015-10-08 17:57:07 +00:00
|
|
|
return next_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)resultForJSValue:(JSValueRef)value {
|
2015-10-08 18:23:42 +00:00
|
|
|
switch (JSValueGetType(_context, value)) {
|
2015-10-08 17:57:07 +00:00
|
|
|
case kJSTypeUndefined:
|
|
|
|
return @{};
|
|
|
|
case kJSTypeNull:
|
|
|
|
return @{@"value": [NSNull null]};
|
|
|
|
case kJSTypeBoolean:
|
2015-10-08 18:23:42 +00:00
|
|
|
return @{@"value": @(JSValueToBoolean(_context, value))};
|
2015-10-08 17:57:07 +00:00
|
|
|
case kJSTypeNumber:
|
2015-10-08 18:23:42 +00:00
|
|
|
return @{@"value": @(JSValueToNumber(_context, value, NULL))};
|
2015-10-08 17:57:07 +00:00
|
|
|
case kJSTypeString:
|
2015-10-08 18:23:42 +00:00
|
|
|
return @{@"value": @(RJSStringForValue(_context, value).c_str())};
|
2015-10-08 17:57:07 +00:00
|
|
|
case kJSTypeObject:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
JSObjectRef jsObject = JSValueToObject(_context, value, NULL);
|
2015-10-08 17:57:07 +00:00
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
if (JSValueIsObjectOfClass(_context, value, RJSObjectClass())) {
|
2015-10-08 17:57:07 +00:00
|
|
|
realm::Object *object = RJSGetInternal<realm::Object *>(jsObject);
|
2015-10-08 22:31:29 +00:00
|
|
|
RPCObjectID oid = [self storeObject:jsObject];
|
2015-10-08 17:57:07 +00:00
|
|
|
return @{
|
|
|
|
@"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()),
|
|
|
|
@"id": @(oid),
|
|
|
|
@"schema": [self objectSchemaToJSONObject:object->object_schema]
|
|
|
|
};
|
|
|
|
}
|
2015-10-13 22:27:24 +00:00
|
|
|
else if (JSValueIsObjectOfClass(_context, value, RJSListClass())) {
|
2015-10-13 21:41:51 +00:00
|
|
|
realm::List *list = RJSGetInternal<realm::List *>(jsObject);
|
2015-10-08 22:31:29 +00:00
|
|
|
RPCObjectID oid = [self storeObject:jsObject];
|
2015-10-08 17:57:07 +00:00
|
|
|
return @{
|
|
|
|
@"type": @(RJSTypeGet(realm::PropertyTypeArray).c_str()),
|
|
|
|
@"id": @(oid),
|
2015-10-13 21:41:51 +00:00
|
|
|
@"size": @(list->link_view->size()),
|
|
|
|
@"schema": [self objectSchemaToJSONObject:list->object_schema]
|
2015-10-08 17:57:07 +00:00
|
|
|
};
|
|
|
|
}
|
2015-10-08 18:23:42 +00:00
|
|
|
else if (JSValueIsObjectOfClass(_context, value, RJSResultsClass())) {
|
2015-10-08 17:57:07 +00:00
|
|
|
realm::Results *results = RJSGetInternal<realm::Results *>(jsObject);
|
2015-10-08 22:31:29 +00:00
|
|
|
RPCObjectID oid = [self storeObject:jsObject];
|
2015-10-08 17:57:07 +00:00
|
|
|
return @{
|
|
|
|
@"type": @"ObjectTypesRESULTS",
|
2015-10-08 22:31:29 +00:00
|
|
|
@"id": @(oid),
|
2015-10-08 17:57:07 +00:00
|
|
|
@"size": @(results->size()),
|
|
|
|
@"schema": [self objectSchemaToJSONObject:results->object_schema]
|
|
|
|
};
|
|
|
|
}
|
2015-10-15 01:00:21 +00:00
|
|
|
else if (JSValueIsObjectOfClass(_context, value, RJSNotificationClass())) {
|
|
|
|
RPCObjectID oid = [self storeObject:jsObject];
|
|
|
|
return @{
|
|
|
|
@"type": @"ObjectTypesNOTIFICATION",
|
|
|
|
@"id": @(oid),
|
|
|
|
};
|
|
|
|
}
|
2015-10-08 18:23:42 +00:00
|
|
|
else if (RJSIsValueArray(_context, value)) {
|
2015-10-13 22:25:06 +00:00
|
|
|
size_t length = RJSValidatedListLength(_context, jsObject);
|
2015-10-08 17:57:07 +00:00
|
|
|
NSMutableArray *array = [NSMutableArray new];
|
|
|
|
for (unsigned int i = 0; i < length; i++) {
|
2015-10-08 18:23:42 +00:00
|
|
|
[array addObject:[self resultForJSValue:JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)]];
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
return @{@"value": array};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)objectSchemaToJSONObject:(realm::ObjectSchema &)objectSchema {
|
|
|
|
NSMutableArray *properties = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
for (realm::Property prop : objectSchema.properties) {
|
|
|
|
NSDictionary *dict = @{
|
|
|
|
@"name": @(prop.name.c_str()),
|
|
|
|
@"type": @(RJSTypeGet(prop.type).c_str()),
|
|
|
|
};
|
|
|
|
|
|
|
|
[properties addObject:dict];
|
|
|
|
}
|
|
|
|
|
|
|
|
return @{
|
|
|
|
@"name": @(objectSchema.name.c_str()),
|
|
|
|
@"properties": properties,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-10-16 00:07:10 +00:00
|
|
|
- (JSValueRef)deserializeDictionaryValue:(NSDictionary *)dict {
|
2015-10-08 17:57:07 +00:00
|
|
|
RPCObjectID oid = [dict[@"id"] longValue];
|
|
|
|
if (oid) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return _objects[oid];
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
2015-10-15 01:00:21 +00:00
|
|
|
NSString *type = dict[@"type"];
|
|
|
|
if ([type isEqualToString:@"ObjectTypesFUNCTION"]) {
|
|
|
|
// FIXME: Make this actually call the function by its id once we need it to.
|
|
|
|
JSStringRef jsBody = JSStringCreateWithUTF8CString("");
|
|
|
|
JSObjectRef jsFunction = JSObjectMakeFunction(_context, NULL, 0, NULL, jsBody, NULL, 1, NULL);
|
|
|
|
JSStringRelease(jsBody);
|
|
|
|
|
|
|
|
return jsFunction;
|
|
|
|
}
|
|
|
|
|
2015-10-08 17:57:07 +00:00
|
|
|
id value = dict[@"value"];
|
|
|
|
if (!value) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return JSValueMakeUndefined(_context);
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
else if ([value isKindOfClass:[NSNull class]]) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return JSValueMakeNull(_context);
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
else if ([value isKindOfClass:[@YES class]]) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return JSValueMakeBoolean(_context, [value boolValue]);
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
else if ([value isKindOfClass:[NSNumber class]]) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return JSValueMakeNumber(_context, [value doubleValue]);
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
else if ([value isKindOfClass:[NSString class]]) {
|
2015-10-08 18:23:42 +00:00
|
|
|
return RJSValueForString(_context, std::string([value UTF8String]));
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
else if ([value isKindOfClass:[NSArray class]]) {
|
|
|
|
NSUInteger count = [value count];
|
|
|
|
JSValueRef jsValues[count];
|
|
|
|
|
|
|
|
for (NSUInteger i = 0; i < count; i++) {
|
2015-10-16 00:07:10 +00:00
|
|
|
jsValues[i] = [self deserializeDictionaryValue:value[i]];
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
return JSObjectMakeArray(_context, count, jsValues, NULL);
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
else if ([value isKindOfClass:[NSDictionary class]]) {
|
2015-10-08 18:23:42 +00:00
|
|
|
JSObjectRef jsObject = JSObjectMake(_context, NULL, NULL);
|
2015-10-08 17:57:07 +00:00
|
|
|
|
|
|
|
for (NSString *key in value) {
|
2015-10-16 00:07:10 +00:00
|
|
|
JSValueRef jsValue = [self deserializeDictionaryValue:value[key]];
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRef jsKey = JSStringCreateWithCFString((__bridge CFStringRef)key);
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
JSObjectSetProperty(_context, jsObject, jsKey, jsValue, 0, NULL);
|
2015-10-08 17:57:07 +00:00
|
|
|
JSStringRelease(jsKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
return jsObject;
|
|
|
|
}
|
|
|
|
|
2015-10-08 18:23:42 +00:00
|
|
|
return JSValueMakeUndefined(_context);
|
2015-10-08 17:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|