diff --git a/dependencies.list b/dependencies.list index 7211a5f8..be39372f 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js VERSION=1.3.1 -REALM_CORE_VERSION=2.6.0 -REALM_SYNC_VERSION=1.8.3 -REALM_OBJECT_SERVER_VERSION=1.3.0 +REALM_CORE_VERSION=2.8.1 +REALM_SYNC_VERSION=1.9.2 +REALM_OBJECT_SERVER_VERSION=1.7.1 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..6fb3b043 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 value, bool create = false, bool update = false); + + ValueType box(bool boolean) { return Value::from_boolean(m_ctx, boolean); } + 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()); } + 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 int64_t 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 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 value, bool create, bool update) { + return _impl::Unbox::call(this, std::move(value), create, update); +} + + } // js } // realm diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 16769b7f..5d2a0aa4 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); @@ -556,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); @@ -634,7 +632,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 +661,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))); } @@ -799,4 +799,4 @@ void RealmClass::close(ContextType ctx, FunctionType, ObjectType this_object, } } // js -} // realm \ No newline at end of file +} // realm 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..7a02f6d6 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 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..9c338496 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 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..6decdfbb 160000 --- a/src/object-store +++ b/src/object-store @@ -1 +1 @@ -Subproject commit 872a154b1bfd843f7dad98cab73e23a5aff2ee66 +Subproject commit 6decdfbb88494908c8116b64b21c8715175fc2f3 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());