From c928ab716ee281a74fdf5e0fc33b699662c8e298 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 2 Nov 2015 16:36:24 -0800 Subject: [PATCH] RPC now keeps object keys in the same order Maintaining insertion order when passing objects through the RPC is essential to make the new schema API work. --- lib/constants.js | 1 + lib/rpc.js | 25 ++++++++++++++++------- src/rpc.cpp | 53 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index 063d3eef..ff538a0c 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -17,6 +17,7 @@ let propTypes = {}; }); [ + 'DICT', 'FUNCTION', 'REALM', 'RESULTS', diff --git a/lib/rpc.js b/lib/rpc.js index e69275dc..43fb140b 100644 --- a/lib/rpc.js +++ b/lib/rpc.js @@ -10,6 +10,7 @@ const constants = require('./constants'); const DEVICE_HOST = 'localhost:8082'; const {keys, objectTypes, propTypes} = constants; +const {id: idKey, realm: realmKey} = keys; const typeConverters = {}; let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest; @@ -40,6 +41,7 @@ module.exports = { registerTypeConverter(propTypes.DATA, (_, {value}) => base64.decode(value)); registerTypeConverter(propTypes.DATE, (_, {value}) => new Date(value)); +registerTypeConverter(objectTypes.DICT, deserializeDict); function registerTypeConverter(type, handler) { typeConverters[type] = handler; @@ -106,9 +108,9 @@ function serialize(realmId, value) { return {value: value}; } - let id = value[keys.id]; + let id = value[idKey]; if (id) { - if (value[keys.realm] != realmId) { + if (value[realmKey] != realmId) { throw new Error('Unable to serialize value from another Realm'); } @@ -128,11 +130,9 @@ function serialize(realmId, value) { return {type: propTypes.DATA, value: base64.encode(value)}; } - let object = {}; - for (let key in value) { - object[key] = serialize(realmId, value[key]); - } - return {value: object}; + let keys = Object.keys(value); + let values = keys.map((key) => serialize(realmId, value[key])); + return {type: objectTypes.DICT, keys, values}; } function deserialize(realmId, info) { @@ -150,6 +150,17 @@ function deserialize(realmId, info) { return value; } +function deserializeDict(realmId, info) { + let {keys, values} = info; + let object = {}; + + for (let i = 0, len = keys.length; i < len; i++) { + object[keys[i]] = values[i]; + } + + return object; +} + function sendRequest(command, data) { data = Object.assign({}, data, sessionId ? {sessionId} : null); diff --git a/src/rpc.cpp b/src/rpc.cpp index ce270d5a..ba7d5a7e 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -22,6 +22,7 @@ using RJSAccessor = realm::NativeAccessor; using namespace realm_js; +static const char * const RealmObjectTypesDictionary = "ObjectTypesDICT"; static const char * const RealmObjectTypesFunction = "ObjectTypesFUNCTION"; static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS"; @@ -262,6 +263,28 @@ json RPCServer::serialize_json_value(JSValueRef value) { {"value", RJSValidatedValueToNumber(m_context, value)}, }; } + else { + JSPropertyNameArrayRef js_keys = JSObjectCopyPropertyNames(m_context, js_object); + size_t count = JSPropertyNameArrayGetCount(js_keys); + std::vector keys; + std::vector values; + + for (size_t i = 0; i < count; i++) { + JSStringRef js_key = JSPropertyNameArrayGetNameAtIndex(js_keys, i); + JSValueRef js_value = RJSValidatedPropertyValue(m_context, js_object, js_key); + + keys.push_back(RJSStringForJSString(js_key)); + values.push_back(serialize_json_value(js_value)); + } + + JSPropertyNameArrayRelease(js_keys); + + return { + {"type", RealmObjectTypesDictionary}, + {"keys", keys}, + {"values", values}, + }; + } assert(0); } @@ -299,6 +322,22 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) return js_function; } + else if (type_string == RealmObjectTypesDictionary) { + JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL); + json keys = dict["keys"]; + json values = dict["values"]; + size_t count = keys.size(); + + for (size_t i = 0; i < count; i++) { + JSStringRef js_key = RJSStringForString(keys.at(i)); + JSValueRef js_value = deserialize_json_value(values.at(i)); + + JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL); + JSStringRelease(js_key); + } + + return js_object; + } else if (type_string == RJSTypeGet(realm::PropertyTypeData)) { std::string bytes; if (!base64_decode(value.get(), &bytes)) { @@ -344,19 +383,5 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) return JSObjectMakeArray(m_context, count, js_values, NULL); } - else if (value.is_object()) { - JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL); - - for (json::iterator it = value.begin(); it != value.end(); ++it) { - JSValueRef js_value = deserialize_json_value(it.value()); - JSStringRef js_key = JSStringCreateWithUTF8CString(it.key().c_str()); - - JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL); - JSStringRelease(js_key); - } - - return js_object; - } - assert(0); }