From 62bfaf2d056664755ac91825dfc08daec28433d6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 12 May 2017 14:38:27 -0700 Subject: [PATCH 1/7] Update to core 2.7.0 --- dependencies.list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.list b/dependencies.list index 90979194..2d6fdca0 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js VERSION=1.3.0 -REALM_CORE_VERSION=2.6.0 -REALM_SYNC_VERSION=1.8.3 +REALM_CORE_VERSION=2.7.0 +REALM_SYNC_VERSION=1.8.4 REALM_OBJECT_SERVER_VERSION=1.3.0 From 08052e12f684c3f67fa732c0a9258979a97451c6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 26 Apr 2017 12:33:15 -0700 Subject: [PATCH 2/7] Update to work with new API for arrays of primitives --- src/js_list.hpp | 20 +- src/js_object_accessor.hpp | 325 +++++++++++++++++++----------- src/js_realm.hpp | 23 +-- src/js_realm_object.hpp | 9 +- src/js_results.hpp | 8 +- src/js_sync.hpp | 2 - src/jsc/jsc_object_accessor.hpp | 51 +++-- src/node/node_object_accessor.hpp | 25 +-- src/object-store | 2 +- src/rpc.cpp | 7 +- 10 files changed, 292 insertions(+), 180 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index 14b73f32..c60e3028 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -19,6 +19,7 @@ #pragma once #include "js_collection.hpp" +#include "js_object_accessor.hpp" #include "js_realm_object.hpp" #include "js_results.hpp" #include "js_types.hpp" @@ -32,12 +33,15 @@ namespace realm { namespace js { +template +class NativeAccessor; + template class List : public realm::List { public: List(std::shared_ptr r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {} List(const realm::List &l) : realm::List(l) {} - + std::vector, NotificationToken>> m_notification_tokens; }; @@ -104,7 +108,7 @@ typename T::Object ListClass::create_instance(ContextType ctx, realm::List li } template -void ListClass::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void ListClass::get_length(ContextType, ObjectType object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set((uint32_t)list->size()); } @@ -120,7 +124,8 @@ void ListClass::get_index(ContextType ctx, ObjectType object, uint32_t index, template bool ListClass::set_index(ContextType ctx, ObjectType object, uint32_t index, ValueType value) { auto list = get_internal>(object); - list->set(ctx, value, index); + NativeAccessor accessor(ctx, list->get_realm(), &list->get_object_schema()); + list->set(accessor, index, value); return true; } @@ -129,8 +134,9 @@ void ListClass::push(ContextType ctx, FunctionType, ObjectType this_object, s validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); + NativeAccessor accessor(ctx, list->get_realm(), &list->get_object_schema()); for (size_t i = 0; i < argc; i++) { - list->add(ctx, arguments[i]); + list->add(accessor, arguments[i]); } return_value.set((uint32_t)list->size()); @@ -160,8 +166,9 @@ void ListClass::unshift(ContextType ctx, FunctionType, ObjectType this_object validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); + NativeAccessor accessor(ctx, list->get_realm(), &list->get_object_schema()); for (size_t i = 0; i < argc; i++) { - list->insert(ctx, arguments[i], i); + list->insert(accessor, i, arguments[i]); } return_value.set((uint32_t)list->size()); @@ -207,6 +214,7 @@ void ListClass::splice(ContextType ctx, FunctionType, ObjectType this_object, std::vector removed_objects; removed_objects.reserve(remove); + NativeAccessor accessor(ctx, list->get_realm(), &list->get_object_schema()); for (size_t i = 0; i < remove; i++) { auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); @@ -214,7 +222,7 @@ void ListClass::splice(ContextType ctx, FunctionType, ObjectType this_object, list->remove(index); } for (size_t i = 2; i < argc; i++) { - list->insert(ctx, arguments[i], index + i - 2); + list->insert(accessor, index + i - 2, arguments[i]); } return_value.set(Object::create_array(ctx, removed_objects)); diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index d9ec7f1b..ddda2920 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -23,136 +23,235 @@ #include "js_schema.hpp" namespace realm { +class List; +class Object; +class ObjectSchema; +class Realm; +class Results; +struct Property; + namespace js { +namespace _impl { +template +struct Unbox; +} -template -struct NativeAccessor { - using ContextType = typename T::Context; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; - using Object = js::Object; - using Value = js::Value; +template +class NativeAccessor { +public: + using ContextType = typename JSEngine::Context; + using ObjectType = typename JSEngine::Object; + using ValueType = typename JSEngine::Value; + using Object = js::Object; + using Value = js::Value; + using OptionalValue = util::Optional; - static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { - ObjectType object = Value::validated_to_object(ctx, dict); - return Object::has_property(ctx, object, prop_name); - } - static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { - ObjectType object = Value::validated_to_object(ctx, dict); - return Object::get_property(ctx, object, prop_name); - } + NativeAccessor(ContextType ctx, std::shared_ptr realm=nullptr, const ObjectSchema* object_schema=nullptr) + : m_ctx(ctx), m_realm(std::move(realm)), m_object_schema(object_schema) { } - static bool has_default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; - return defaults.count(prop_name) != 0; - } - static ValueType default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; - return defaults.at(prop_name); - } + NativeAccessor(NativeAccessor& parent, const Property& prop) + : m_ctx(parent.m_ctx) + , m_realm(parent.m_realm) + , m_object_schema(&*m_realm->schema().find(prop.object_type)) + { } - // These must be implemented for each JS engine. - static std::string to_binary(ContextType, ValueType &); - static ValueType from_binary(ContextType, BinaryData); - - static bool to_bool(ContextType ctx, ValueType &value) { - return Value::validated_to_boolean(ctx, value, "Property"); - } - static ValueType from_bool(ContextType ctx, bool boolean) { - return Value::from_boolean(ctx, boolean); - } - static long long to_long(ContextType ctx, ValueType &value) { - return Value::validated_to_number(ctx, value, "Property"); - } - static ValueType from_long(ContextType ctx, long long number) { - return Value::from_number(ctx, number); - } - static float to_float(ContextType ctx, ValueType &value) { - return Value::validated_to_number(ctx, value, "Property"); - } - static ValueType from_float(ContextType ctx, float number) { - return Value::from_number(ctx, number); - } - static double to_double(ContextType ctx, ValueType &value) { - return Value::validated_to_number(ctx, value, "Property"); - } - static ValueType from_double(ContextType ctx, double number) { - return Value::from_number(ctx, number); - } - static std::string to_string(ContextType ctx, ValueType &value) { - return Value::validated_to_string(ctx, value, "Property"); - } - static ValueType from_string(ContextType ctx, StringData string) { - return Value::from_string(ctx, string.data()); - } - static Timestamp to_timestamp(ContextType ctx, ValueType &value) { - ObjectType date = Value::validated_to_date(ctx, value, "Property"); - double milliseconds = Value::to_number(ctx, date); - int64_t seconds = milliseconds / 1000; - int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000; - return Timestamp(seconds, nanoseconds); - } - static ValueType from_timestamp(ContextType ctx, Timestamp ts) { - return Object::create_date(ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000); - } - - static bool is_null(ContextType ctx, ValueType &value) { - return Value::is_null(ctx, value) || Value::is_undefined(ctx, value); - } - static ValueType null_value(ContextType ctx) { - return Value::from_null(ctx); - } - - static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &value, const std::string &type, bool try_update) { - ObjectType object = Value::validated_to_object(ctx, value); - if (Object::template is_instance>(ctx, object)) { - auto realm_object = get_internal>(object); - if (realm_object->realm() == realm) { - return realm_object->row().get_index(); - } + OptionalValue value_for_property(ValueType dict, std::string const& prop_name, size_t) { + ObjectType object = Value::validated_to_object(m_ctx, dict); + if (!Object::has_property(m_ctx, object, prop_name)) { + return util::none; } + return Object::get_property(m_ctx, object, prop_name); + } - auto object_schema = realm->schema().find(type); - if (Value::is_array(ctx, object)) { - object = Schema::dict_for_property_array(ctx, *object_schema, object); + OptionalValue default_value_for_property(const ObjectSchema &object_schema, const std::string &prop_name) { + auto defaults = get_delegate(m_realm.get())->m_defaults[object_schema.name]; + auto it = defaults.find(prop_name); + return it != defaults.end() ? util::make_optional(ValueType(it->second)) : util::none; + } + + template + T unbox(ValueType const& value, bool create = false, bool update = false); + + ValueType box(bool boolean) { return Value::from_boolean(m_ctx, boolean); } + ValueType box(long long number) { return Value::from_number(m_ctx, number); } + ValueType box(float number) { return Value::from_number(m_ctx, number); } + ValueType box(double number) { return Value::from_number(m_ctx, number); } + ValueType box(StringData string) { return Value::from_string(m_ctx, string.data()); } + ValueType box(Mixed) { throw std::runtime_error("'Any' type is unsupported"); } + + ValueType box(BinaryData); + ValueType box(Timestamp ts) { + return Object::create_date(m_ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000); + } + ValueType box(realm::Object realm_object) { + return RealmObjectClass::create_instance(m_ctx, std::move(realm_object)); + } + ValueType box(realm::List list) { + return ListClass::create_instance(m_ctx, std::move(list)); + } + ValueType box(realm::Results results) { + return ResultsClass::create_instance(m_ctx, std::move(results)); + } + + bool is_null(ValueType const& value) { + return Value::is_null(m_ctx, value) || Value::is_undefined(m_ctx, value); + } + ValueType null_value() { + return Value::from_null(m_ctx); + } + + template + void enumerate_list(ValueType& value, Fn&& func) { + auto obj = Value::validated_to_object(m_ctx, value); + uint32_t size = Object::validated_get_length(m_ctx, obj); + for (uint32_t i = 0; i < size; ++i) { + func(Object::validated_get_object(m_ctx, obj, i)); } - - auto child = realm::Object::create(ctx, realm, *object_schema, static_cast(object), try_update); - return child.row().get_index(); - } - static size_t to_existing_object_index(ContextType ctx, SharedRealm realm, ValueType &value) { - ObjectType object = Value::validated_to_object(ctx, value); - if (!Object::template is_instance>(ctx, object)) { - throw std::runtime_error("object is not a Realm Object"); - } - - auto realm_object = get_internal>(object); - if (realm_object->realm() != realm) { - throw std::runtime_error("Realm object is from another Realm"); - - } - return realm_object->row().get_index(); - } - static ValueType from_object(ContextType ctx, realm::Object realm_object) { - return RealmObjectClass::create_instance(ctx, std::move(realm_object)); } - static size_t list_size(ContextType ctx, ValueType &value) { - return Object::validated_get_length(ctx, Value::validated_to_object(ctx, value)); + bool allow_missing(ValueType const&) const noexcept { return false; } + void will_change(realm::Object&, realm::Property const&) { } + void did_change() { } + + std::string print(ValueType const&) { return "not implemented"; } + +private: + ContextType m_ctx; + std::shared_ptr m_realm; + const ObjectSchema* m_object_schema = nullptr; + std::string m_string_buffer; + + template + friend struct _impl::Unbox; +}; + +namespace _impl { +template +struct Unbox { + static bool call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_boolean(ctx->m_ctx, value, "Property"); } - static ValueType list_value_at_index(ContextType ctx, ValueType &value, size_t index) { - return Object::validated_get_object(ctx, Value::validated_to_object(ctx, value), (uint32_t)index); +}; + +template +struct Unbox { + static long long call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } - static ValueType from_list(ContextType ctx, realm::List list) { - return ListClass::create_instance(ctx, std::move(list)); +}; + +template +struct Unbox { + static float call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } - static ValueType from_results(ContextType ctx, realm::Results results) { - return ResultsClass::create_instance(ctx, std::move(results)); +}; + +template +struct Unbox { + static double call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } - static Mixed to_mixed(ContextType ctx, ValueType &val) { +}; + +template +struct Unbox> { + static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_boolean(ctx->m_ctx, value, "Property"); +} +}; + +template +struct Unbox> { + static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); +} +}; + +template +struct Unbox> { + static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); +} +}; + +template +struct Unbox> { + static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); +} +}; + +template +struct Unbox { + static StringData call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + ctx->m_string_buffer = js::Value::validated_to_string(ctx->m_ctx, value, "Property"); + return ctx->m_string_buffer; + } +}; + +// Need separate implementations per-engine +template +struct Unbox { + static BinaryData call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool); +}; + +template +struct Unbox { + static Mixed call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { throw std::runtime_error("'Any' type is unsupported"); } }; +template +struct Unbox { + static Timestamp call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { + auto date = js::Value::validated_to_date(ctx->m_ctx, value, "Property"); + double milliseconds = js::Value::to_number(ctx->m_ctx, date); + int64_t seconds = milliseconds / 1000; + int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000; + return Timestamp(seconds, nanoseconds); + } +}; + +template +struct Unbox { + static RowExpr call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool create, bool try_update) { + using Value = js::Value; + using ValueType = typename JSEngine::Value; + + auto object = Value::validated_to_object(ctx->m_ctx, value); + if (js::Object::template is_instance>(ctx->m_ctx, object)) { + auto realm_object = get_internal>(object); + if (realm_object->realm() == ctx->m_realm) { + return realm_object->row(); + } + if (!create) { + throw std::runtime_error("Realm object is from another Realm"); + } + } + else if (!create) { + throw std::runtime_error("object is not a Realm Object"); + } + + if (Value::is_array(ctx->m_ctx, object)) { + object = Schema::dict_for_property_array(ctx->m_ctx, *ctx->m_object_schema, object); + } + + auto child = realm::Object::create(*ctx, ctx->m_realm, + *ctx->m_object_schema, + static_cast(object), try_update); + return child.row(); + } +}; +} // namespace _impl + +template +template +U NativeAccessor::unbox(ValueType const& value, bool create, bool update) { + return _impl::Unbox::call(this, value, create, update); +} + + } // js } // realm diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 16769b7f..2c4e0d20 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -79,10 +79,6 @@ class RealmDelegate : public BindingContext { virtual void did_change(std::vector const& observers, std::vector const& invalidated, bool version_changed) { notify("change"); } - virtual std::vector get_observed_rows() { - return std::vector(); - } - virtual void will_change(std::vector const& observers, std::vector const& invalidated) {} RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {} @@ -159,7 +155,7 @@ class RealmClass : public ClassDefinition> { using Object = js::Object; using Value = js::Value; using ReturnValue = js::ReturnValue; - using NativeAccessor = realm::NativeAccessor; + using NativeAccessor = realm::js::NativeAccessor; public: using ObjectDefaultsMap = typename Schema::ObjectDefaultsMap; @@ -353,8 +349,9 @@ void RealmClass::constructor(ContextType ctx, ObjectType this_object, size_t static const String encryption_key_string = "encryptionKey"; ValueType encryption_key_value = Object::get_property(ctx, object, encryption_key_string); if (!Value::is_undefined(ctx, encryption_key_value)) { - std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value); - config.encryption_key = std::vector(encryption_key.begin(), encryption_key.end()); + NativeAccessor accessor(ctx); + auto encryption_key = accessor.template unbox(encryption_key_value); + config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size()); } #if REALM_ENABLE_SYNC @@ -469,9 +466,9 @@ void RealmClass::schema_version(ContextType ctx, FunctionType, ObjectType thi realm::Realm::Config config; config.path = normalize_realm_path(Value::validated_to_string(ctx, arguments[0])); if (argc == 2) { - auto encryptionKeyValue = arguments[1]; - std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); + NativeAccessor accessor(ctx); + auto encryption_key = accessor.template unbox(arguments[1]); + config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size()); } auto version = realm::Realm::get_schema_version(config); @@ -634,7 +631,8 @@ void RealmClass::object_for_primary_key(ContextType ctx, FunctionType, Object SharedRealm realm = *get_internal>(this_object); std::string object_type; auto &object_schema = validated_object_schema_for_value(ctx, realm, arguments[0], object_type); - auto realm_object = realm::Object::get_for_primary_key(ctx, realm, object_schema, arguments[1]); + NativeAccessor accessor(ctx, realm); + auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, arguments[1]); if (realm_object.is_valid()) { return_value.set(RealmObjectClass::create_instance(ctx, std::move(realm_object))); @@ -662,7 +660,8 @@ void RealmClass::create(ContextType ctx, FunctionType, ObjectType this_object update = Value::validated_to_boolean(ctx, arguments[2], "update"); } - auto realm_object = realm::Object::create(ctx, realm, object_schema, object, update); + NativeAccessor accessor(ctx, realm); + auto realm_object = realm::Object::create(accessor, realm, object_schema, object, update); return_value.set(RealmObjectClass::create_instance(ctx, std::move(realm_object))); } diff --git a/src/js_realm_object.hpp b/src/js_realm_object.hpp index 93405205..44ab4039 100644 --- a/src/js_realm_object.hpp +++ b/src/js_realm_object.hpp @@ -28,6 +28,8 @@ namespace realm { namespace js { +template class NativeAccessor; + template struct RealmObjectClass : ClassDefinition { using ContextType = typename T::Context; @@ -94,7 +96,9 @@ template void RealmObjectClass::get_property(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) { try { auto realm_object = get_internal>(object); - auto result = realm_object->template get_property_value(ctx, property); + NativeAccessor accessor(ctx); + std::string name = property; + auto result = realm_object->template get_property_value(accessor, name); return_value.set(result); } catch (InvalidPropertyException &ex) { // getters for nonexistent properties in JS should always return undefined @@ -111,7 +115,8 @@ bool RealmObjectClass::set_property(ContextType ctx, ObjectType object, const } try { - realm_object->set_property_value(ctx, property_name, value, true); + NativeAccessor accessor(ctx, realm_object->realm()); + realm_object->set_property_value(accessor, property_name, value, true); } catch (TypeErrorException &ex) { throw TypeErrorException(realm_object->get_object_schema().name + "." + property_name, ex.type()); diff --git a/src/js_results.hpp b/src/js_results.hpp index d3e904f7..317e166a 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -30,6 +30,9 @@ namespace realm { namespace js { +template +class NativeAccessor; + template class Results : public realm::Results { public: @@ -117,10 +120,9 @@ typename T::Object ResultsClass::create_filtered(ContextType ctx, const U &co auto const &realm = collection.get_realm(); auto const &object_schema = collection.get_object_schema(); - std::vector args(&arguments[1], &arguments[argc]); - parser::Predicate predicate = parser::parse(query_string); - query_builder::ArgumentConverter converter(ctx, realm, args); + NativeAccessor accessor(ctx, realm); + query_builder::ArgumentConverter> converter(accessor, &arguments[1], argc - 1); query_builder::apply_predicate(query, predicate, converter, realm->schema(), object_schema.name); return create_instance(ctx, realm::Results(realm, std::move(query))); diff --git a/src/js_sync.hpp b/src/js_sync.hpp index 427a1268..400a3d5b 100644 --- a/src/js_sync.hpp +++ b/src/js_sync.hpp @@ -51,7 +51,6 @@ class UserClass : public ClassDefinition { using Value = js::Value; using Function = js::Function; using ReturnValue = js::ReturnValue; - using NativeAccessor = realm::NativeAccessor; public: std::string const name = "User"; @@ -337,7 +336,6 @@ class SyncClass : public ClassDefinition { using Value = js::Value; using Function = js::Function; using ReturnValue = js::ReturnValue; - using NativeAccessor = realm::NativeAccessor; public: std::string const name = "Sync"; diff --git a/src/jsc/jsc_object_accessor.hpp b/src/jsc/jsc_object_accessor.hpp index d210d90a..9ffa6a6d 100644 --- a/src/jsc/jsc_object_accessor.hpp +++ b/src/jsc/jsc_object_accessor.hpp @@ -24,13 +24,12 @@ namespace realm { // Specialize a native accessor class for JSC. -template<> -class NativeAccessor : public js::NativeAccessor {}; namespace js { template<> -inline std::string NativeAccessor::to_binary(JSContextRef ctx, JSValueRef &value) { +template<> +inline BinaryData NativeAccessor::unbox(ValueType const& value, bool, bool) { static jsc::String s_array_buffer = "ArrayBuffer"; static jsc::String s_buffer = "buffer"; static jsc::String s_byte_length = "byteLength"; @@ -38,25 +37,25 @@ inline std::string NativeAccessor::to_binary(JSContextRef ctx, JSVal static jsc::String s_is_view = "isView"; static jsc::String s_uint8_array = "Uint8Array"; - JSObjectRef global_object = JSContextGetGlobalObject(ctx); - JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_array_buffer); - JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_uint8_array); + JSObjectRef global_object = JSContextGetGlobalObject(m_ctx); + JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(m_ctx, global_object, s_array_buffer); + JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(m_ctx, global_object, s_uint8_array); JSValueRef uint8_array_arguments[3]; uint32_t uint8_array_argc = 0; // Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView). - if (JSValueIsInstanceOfConstructor(ctx, value, array_buffer_constructor, nullptr)) { + if (JSValueIsInstanceOfConstructor(m_ctx, value, array_buffer_constructor, nullptr)) { uint8_array_arguments[0] = value; uint8_array_argc = 1; } - else if (JSObjectRef object = JSValueToObject(ctx, value, nullptr)) { + else if (JSObjectRef object = JSValueToObject(m_ctx, value, nullptr)) { // Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val). - JSValueRef is_view = jsc::Object::call_method(ctx, array_buffer_constructor, s_is_view, 1, &object); + JSValueRef is_view = jsc::Object::call_method(m_ctx, array_buffer_constructor, s_is_view, 1, &object); - if (jsc::Value::to_boolean(ctx, is_view)) { - uint8_array_arguments[0] = jsc::Object::validated_get_object(ctx, object, s_buffer); - uint8_array_arguments[1] = jsc::Object::get_property(ctx, object, s_byte_offset); - uint8_array_arguments[2] = jsc::Object::get_property(ctx, object, s_byte_length); + if (jsc::Value::to_boolean(m_ctx, is_view)) { + uint8_array_arguments[0] = jsc::Object::validated_get_object(m_ctx, object, s_buffer); + uint8_array_arguments[1] = jsc::Object::get_property(m_ctx, object, s_byte_offset); + uint8_array_arguments[2] = jsc::Object::get_property(m_ctx, object, s_byte_length); uint8_array_argc = 3; } } @@ -65,34 +64,34 @@ inline std::string NativeAccessor::to_binary(JSContextRef ctx, JSVal throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary"); } - JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments); - uint32_t byte_count = jsc::Object::validated_get_length(ctx, uint8_array); - std::string bytes(byte_count, 0); + JSObjectRef uint8_array = jsc::Function::construct(m_ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments); + uint32_t byte_count = jsc::Object::validated_get_length(m_ctx, uint8_array); + m_string_buffer.resize(byte_count); for (uint32_t i = 0; i < byte_count; i++) { - JSValueRef byteValue = jsc::Object::get_property(ctx, uint8_array, i); - bytes[i] = jsc::Value::to_number(ctx, byteValue); + JSValueRef byteValue = jsc::Object::get_property(m_ctx, uint8_array, i); + m_string_buffer[i] = jsc::Value::to_number(m_ctx, byteValue); } - return bytes; + return BinaryData(m_string_buffer.data(), m_string_buffer.size()); } template<> -inline JSValueRef NativeAccessor::from_binary(JSContextRef ctx, BinaryData data) { +inline JSValueRef NativeAccessor::box(BinaryData data) { static jsc::String s_buffer = "buffer"; static jsc::String s_uint8_array = "Uint8Array"; size_t byte_count = data.size(); - JSValueRef byte_count_value = jsc::Value::from_number(ctx, byte_count); - JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, JSContextGetGlobalObject(ctx), s_uint8_array); - JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, 1, &byte_count_value); + JSValueRef byte_count_value = jsc::Value::from_number(m_ctx, byte_count); + JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(m_ctx, JSContextGetGlobalObject(m_ctx), s_uint8_array); + JSObjectRef uint8_array = jsc::Function::construct(m_ctx, uint8_array_constructor, 1, &byte_count_value); for (uint32_t i = 0; i < byte_count; i++) { - JSValueRef num = jsc::Value::from_number(ctx, data[i]); - jsc::Object::set_property(ctx, uint8_array, i, num); + JSValueRef num = jsc::Value::from_number(m_ctx, data[i]); + jsc::Object::set_property(m_ctx, uint8_array, i, num); } - return jsc::Object::validated_get_object(ctx, uint8_array, s_buffer); + return jsc::Object::validated_get_object(m_ctx, uint8_array, s_buffer); } } // js diff --git a/src/node/node_object_accessor.hpp b/src/node/node_object_accessor.hpp index 029ffdd0..87309457 100644 --- a/src/node/node_object_accessor.hpp +++ b/src/node/node_object_accessor.hpp @@ -24,38 +24,39 @@ namespace realm { // Specialize a native accessor class for Node. -template<> -class NativeAccessor : public js::NativeAccessor {}; - namespace js { template<> -inline std::string NativeAccessor::to_binary(v8::Isolate* isolate, v8::Local &value) { - if (Value::is_array_buffer(isolate, value)) { +template<> +inline BinaryData NativeAccessor::unbox(ValueType const& value, bool, bool) { + if (Value::is_array_buffer(m_ctx, value)) { // TODO: This probably needs some abstraction for older V8. #if REALM_V8_ARRAY_BUFFER_API v8::Local array_buffer = value.As(); v8::ArrayBuffer::Contents contents = array_buffer->GetContents(); - return std::string(static_cast(contents.Data()), contents.ByteLength()); + m_string_buffer = std::string(static_cast(contents.Data()), contents.ByteLength()); #else // TODO: Implement this for older V8 #endif } - else if (Value::is_array_buffer_view(isolate, value)) { + else if (Value::is_array_buffer_view(m_ctx, value)) { Nan::TypedArrayContents contents(value); - return std::string(*contents, contents.length()); + m_string_buffer = std::string(*contents, contents.length()); } else if (::node::Buffer::HasInstance(value)) { - return std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value)); + m_string_buffer = std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value)); + } + else { + throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary"); } - throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary"); + return BinaryData(m_string_buffer.data(), m_string_buffer.size()); } template<> -inline v8::Local NativeAccessor::from_binary(v8::Isolate* isolate, BinaryData data) { +inline v8::Local NativeAccessor::box(BinaryData data) { #if REALM_V8_ARRAY_BUFFER_API size_t byte_count = data.size(); void* bytes = nullptr; @@ -65,7 +66,7 @@ inline v8::Local NativeAccessor::from_binary(v8::Isolate } // An "internalized" ArrayBuffer will free the malloc'd memory when garbage collected. - return v8::ArrayBuffer::New(isolate, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized); + return v8::ArrayBuffer::New(m_ctx, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized); #else // TODO: Implement this for older V8 #endif diff --git a/src/object-store b/src/object-store index 872a154b..4330f13e 160000 --- a/src/object-store +++ b/src/object-store @@ -1 +1 @@ -Subproject commit 872a154b1bfd843f7dad98cab73e23a5aff2ee66 +Subproject commit 4330f13eedfad4f34bcecdca25f71fb949fbc4b2 diff --git a/src/rpc.cpp b/src/rpc.cpp index bec700d5..77ba8cb1 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -32,7 +32,7 @@ using namespace realm; using namespace realm::rpc; -using Accessor = NativeAccessor; +using Accessor = realm::js::NativeAccessor; static const char * const RealmObjectTypesData = "data"; static const char * const RealmObjectTypesDate = "date"; @@ -413,7 +413,8 @@ json RPCServer::serialize_json_value(JSValueRef js_value) { return {{"value", array}}; } else if (jsc::Value::is_array_buffer(m_context, js_object)) { - std::string data = Accessor::to_binary(m_context, js_value); + Accessor accessor(m_context); + auto data = accessor.unbox(js_value); return { {"type", RealmObjectTypesData}, {"value", base64_encode((unsigned char *)data.data(), data.size())}, @@ -511,7 +512,7 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) { if (!base64_decode(value.get(), &bytes)) { throw std::runtime_error("Failed to decode base64 encoded data"); } - return Accessor::from_binary(m_context, realm::BinaryData(bytes)); + return Accessor(m_context).box(realm::BinaryData(bytes.data(), bytes.size())); } else if (type_string == RealmObjectTypesDate) { return jsc::Object::create_date(m_context, value.get()); From 43200b1db5ce2c28a1a2d0e82c29d72107479ce8 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 22 May 2017 14:33:39 -0700 Subject: [PATCH 3/7] Update the recently-added `RealmClass::wait_for_download_completion` to use the new object accessor interface. --- src/js_realm.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 2c4e0d20..5d2a0aa4 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -553,8 +553,9 @@ void RealmClass::wait_for_download_completion(ContextType ctx, FunctionType, static const String encryption_key_string = "encryptionKey"; ValueType encryption_key_value = Object::get_property(ctx, config_object, encryption_key_string); if (!Value::is_undefined(ctx, encryption_key_value)) { - std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value); - config.encryption_key = std::vector(encryption_key.begin(), encryption_key.end()); + NativeAccessor accessor(ctx); + auto encryption_key = accessor.template unbox(encryption_key_value); + config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size()); } Protected thiz(ctx, this_object); @@ -798,4 +799,4 @@ void RealmClass::close(ContextType ctx, FunctionType, ObjectType this_object, } } // js -} // realm \ No newline at end of file +} // realm From 75f8b28831eee2cb90b23c0caeb0afd7361b16da Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 22 May 2017 16:19:15 -0700 Subject: [PATCH 4/7] Fix the Node build. Tweak the signature on some of the accessor methods to take `JSEngine::Value`s by value rather than by const reference. Some Node APIs appear to assume that only non-const objects will be used. --- src/js_object_accessor.hpp | 8 ++++---- src/jsc/jsc_object_accessor.hpp | 2 +- src/node/node_object_accessor.hpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index ddda2920..fe73d83e 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -70,7 +70,7 @@ public: } template - T unbox(ValueType const& value, bool create = false, bool update = false); + T unbox(ValueType value, bool create = false, bool update = false); ValueType box(bool boolean) { return Value::from_boolean(m_ctx, boolean); } ValueType box(long long number) { return Value::from_number(m_ctx, number); } @@ -193,7 +193,7 @@ struct Unbox { // Need separate implementations per-engine template struct Unbox { - static BinaryData call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool); + static BinaryData call(NativeAccessor *ctx, typename JSEngine::Value value, bool, bool); }; template @@ -248,8 +248,8 @@ struct Unbox { template template -U NativeAccessor::unbox(ValueType const& value, bool create, bool update) { - return _impl::Unbox::call(this, value, create, update); +U NativeAccessor::unbox(ValueType value, bool create, bool update) { + return _impl::Unbox::call(this, std::move(value), create, update); } diff --git a/src/jsc/jsc_object_accessor.hpp b/src/jsc/jsc_object_accessor.hpp index 9ffa6a6d..7a02f6d6 100644 --- a/src/jsc/jsc_object_accessor.hpp +++ b/src/jsc/jsc_object_accessor.hpp @@ -29,7 +29,7 @@ namespace js { template<> template<> -inline BinaryData NativeAccessor::unbox(ValueType const& value, bool, bool) { +inline BinaryData NativeAccessor::unbox(ValueType value, bool, bool) { static jsc::String s_array_buffer = "ArrayBuffer"; static jsc::String s_buffer = "buffer"; static jsc::String s_byte_length = "byteLength"; diff --git a/src/node/node_object_accessor.hpp b/src/node/node_object_accessor.hpp index 87309457..9c338496 100644 --- a/src/node/node_object_accessor.hpp +++ b/src/node/node_object_accessor.hpp @@ -28,7 +28,7 @@ namespace js { template<> template<> -inline BinaryData NativeAccessor::unbox(ValueType const& value, bool, bool) { +inline BinaryData NativeAccessor::unbox(ValueType value, bool, bool) { if (Value::is_array_buffer(m_ctx, value)) { // TODO: This probably needs some abstraction for older V8. #if REALM_V8_ARRAY_BUFFER_API From b108d06231c5c8ee06abe36093138d7da7ec9eb4 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Mon, 22 May 2017 20:17:35 -0700 Subject: [PATCH 5/7] Use `int64_t` rather than `long long` as the integer type in the accessor implementation. This matches what core uses for integers, and avoids ambiguity that otherwise results when `int64_t` is declared as `long` rather than `long long`. --- src/js_object_accessor.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index fe73d83e..6fb3b043 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -73,7 +73,7 @@ public: T unbox(ValueType value, bool create = false, bool update = false); ValueType box(bool boolean) { return Value::from_boolean(m_ctx, boolean); } - ValueType box(long long number) { return Value::from_number(m_ctx, number); } + ValueType box(int64_t number) { return Value::from_number(m_ctx, number); } ValueType box(float number) { return Value::from_number(m_ctx, number); } ValueType box(double number) { return Value::from_number(m_ctx, number); } ValueType box(StringData string) { return Value::from_string(m_ctx, string.data()); } @@ -134,8 +134,8 @@ struct Unbox { }; template -struct Unbox { - static long long call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { +struct Unbox { + static int64_t call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; @@ -162,8 +162,8 @@ struct Unbox> { }; template -struct Unbox> { - static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { +struct Unbox> { + static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; From 98d57a34ceccb42ce828ac7a1ff96a9c201b96ad Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Tue, 23 May 2017 11:51:28 -0700 Subject: [PATCH 6/7] Update to core v2.8.1 and sync v1.9.x. FIXME: We'll need to update to sync v1.9.2 once it is available as v1.9.1 does not include core v2.8.1. This may result in the missing symbols on Linux problem only being fixed when sync is disabled. --- dependencies.list | 6 +++--- src/object-store | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.list b/dependencies.list index 51f17dbe..c86bcb1e 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js VERSION=1.3.1 -REALM_CORE_VERSION=2.7.0 -REALM_SYNC_VERSION=1.8.4 -REALM_OBJECT_SERVER_VERSION=1.3.0 +REALM_CORE_VERSION=2.8.1 +REALM_SYNC_VERSION=1.9.1 +REALM_OBJECT_SERVER_VERSION=1.7.1 diff --git a/src/object-store b/src/object-store index 4330f13e..6decdfbb 160000 --- a/src/object-store +++ b/src/object-store @@ -1 +1 @@ -Subproject commit 4330f13eedfad4f34bcecdca25f71fb949fbc4b2 +Subproject commit 6decdfbb88494908c8116b64b21c8715175fc2f3 From 8c33481011f3eafb9d7b2ec3b2d55fb0ffb9a1f5 Mon Sep 17 00:00:00 2001 From: Radu Tutueanu Date: Wed, 24 May 2017 12:50:27 +0200 Subject: [PATCH 7/7] Use sync 1.9.2 --- dependencies.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.list b/dependencies.list index c86bcb1e..be39372f 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js VERSION=1.3.1 REALM_CORE_VERSION=2.8.1 -REALM_SYNC_VERSION=1.9.1 +REALM_SYNC_VERSION=1.9.2 REALM_OBJECT_SERVER_VERSION=1.7.1