diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 3dd31948..b12a7d69 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -100,6 +100,7 @@ /* Begin PBXFileReference section */ 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = ""; }; + 025678951CAB392000FB8501 /* types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = types.hpp; path = jsc/types.hpp; sourceTree = ""; }; 0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = ""; }; 0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = ""; }; @@ -261,6 +262,7 @@ 0290480D1C0428DF00ABDED4 /* js_util.cpp */, 0290480E1C0428DF00ABDED4 /* js_util.hpp */, 02AFE5B11CAB133500953DA3 /* js_compat.hpp */, + 025678951CAB392000FB8501 /* types.hpp */, 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, diff --git a/src/js_object.cpp b/src/js_object.cpp index 718a5b1a..505a2505 100644 --- a/src/js_object.cpp +++ b/src/js_object.cpp @@ -74,9 +74,10 @@ JSClassRef RJSObjectClass() { JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype"); - JSObjectRef constructor = RJSConstructors(object.realm().get())[object.get_object_schema().name]; + auto delegate = realm::js::get_delegate(object.realm().get()); + JSObjectRef constructor = delegate->m_constructors[object.get_object_schema().name]; JSObjectRef prototype = constructor ? RJSValidatedObjectProperty(ctx, constructor, prototypeString) : NULL; - JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype); + JSObjectRef jsObject = realm::js::WrapObject(ctx, RJSObjectClass(), new Object(object), prototype); if (constructor) { JSValueRef exception = NULL; @@ -117,12 +118,12 @@ template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueR } template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name]; + auto defaults = realm::js::get_delegate(realm)->m_defaults[object_schema.name]; return defaults.find(prop_name) != defaults.end(); } template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name]; + auto defaults = realm::js::get_delegate(realm)->m_defaults[object_schema.name]; return defaults[prop_name]; } diff --git a/src/js_realm.cpp b/src/js_realm.cpp index a6689ef9..a9ce6f2f 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -29,99 +29,12 @@ #include "binding_context.hpp" #include "results.hpp" -#include #include + using namespace realm; using RJSAccessor = realm::NativeAccessor; -class RJSRealmDelegate : public BindingContext { -public: - virtual void changes_available() { - assert(0); - } - virtual void did_change(std::vector const& observers, std::vector const& invalidated) { - notify("change"); - } - virtual std::vector get_observed_rows() { - return std::vector(); - } - virtual void will_change(std::vector const& observers, - std::vector const& invalidated) {} - - RJSRealmDelegate(WeakRealm realm, JSGlobalContextRef ctx) : m_context(ctx), m_realm(realm) { - JSGlobalContextRetain(m_context); - } - - ~RJSRealmDelegate() { - remove_all_notifications(); - - for (auto constructor : m_constructors) { - JSValueUnprotect(m_context, constructor.second); - } - for (auto objectDefaults : m_defaults) { - for (auto value : objectDefaults.second) { - JSValueUnprotect(m_context, value.second); - } - } - JSGlobalContextRelease(m_context); - } - - void add_notification(JSObjectRef notification) { - if (!m_notifications.count(notification)) { - JSValueProtect(m_context, notification); - m_notifications.insert(notification); - } - } - void remove_notification(JSObjectRef notification) { - if (m_notifications.count(notification)) { - JSValueUnprotect(m_context, notification); - m_notifications.erase(notification); - } - } - void remove_all_notifications() { - for (auto notification : m_notifications) { - JSValueUnprotect(m_context, notification); - } - m_notifications.clear(); - } - - std::map m_defaults; - std::map m_constructors; - - private: - std::set m_notifications; - JSGlobalContextRef m_context; - WeakRealm m_realm; - - void notify(const char *notification_name) { - JSValueRef arguments[2]; - SharedRealm realm = m_realm.lock(); - if (!realm) { - throw std::runtime_error("Realm no longer exists"); - } - JSObjectRef realm_object = RJSWrapObject(m_context, RJSRealmClass(), new SharedRealm(realm)); - arguments[0] = realm_object; - arguments[1] = RJSValueForString(m_context, notification_name); - - for (auto callback : m_notifications) { - JSValueRef ex = NULL; - JSObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, &ex); - if (ex) { - throw RJSException(m_context, ex); - } - } - } -}; - -std::map &RJSDefaults(Realm *realm) { - return static_cast(realm->m_binding_context.get())->m_defaults; -} - -std::map &RJSConstructors(Realm *realm) { - return static_cast(realm->m_binding_context.get())->m_constructors; -} - // static std::string s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; static std::string s_defaultPath = ""; std::string RJSDefaultPath() { @@ -214,12 +127,13 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a ensure_directory_exists_for_file(config.path); SharedRealm realm = Realm::get_shared_realm(config); + auto delegate = new js::RealmDelegate(realm, JSContextGetGlobalContext(ctx)); if (!realm->m_binding_context) { - realm->m_binding_context.reset(new RJSRealmDelegate(realm, JSContextGetGlobalContext(ctx))); + realm->m_binding_context.reset(delegate); } - RJSDefaults(realm.get()) = defaults; - RJSConstructors(realm.get()) = constructors; - return RJSWrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); + delegate->m_defaults = defaults; + delegate->m_constructors = constructors; + return js::WrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); } catch (std::exception &ex) { if (jsException) { @@ -299,7 +213,8 @@ std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { JSObjectRef constructor = (JSObjectRef)value; - for (auto pair : RJSConstructors(realm.get())) { + auto delegate = js::get_delegate(realm.get()); + for (auto pair : delegate->m_constructors) { if (pair.second == constructor) { return pair.first; } @@ -453,7 +368,7 @@ void RealmAddListener(ContextType ctx, ObjectType thisObject, size_t argumentCou auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->add_notification(callback); + static_cast *>(realm->m_binding_context.get())->add_notification(callback); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); @@ -467,7 +382,7 @@ void RealmRemoveListener(ContextType ctx, ObjectType thisObject, size_t argument auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->remove_notification(callback); + static_cast *>(realm->m_binding_context.get())->remove_notification(callback); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); @@ -482,7 +397,7 @@ void RealmRemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argu } SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->remove_all_notifications(); + static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); @@ -527,3 +442,10 @@ JSClassRef RJSRealmClass() { static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); return s_realmClass; } + + +namespace realm { +namespace js { +JSClassRef RealmClass() { return RJSRealmClass(); }; +} +} diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 167e64bd..54c1c443 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -19,11 +19,101 @@ #pragma once #include "js_util.hpp" +#include "shared_realm.hpp" +#include "binding_context.hpp" #include +#include namespace realm { - class Realm; - using ObjectDefaults = std::map; +namespace js { + +template +class RealmDelegate : public BindingContext { + public: + using GlobalContextType = typename T::GlobalContext; + using ObjectClassType = typename T::ObjectClass; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using ObjectDefaults = std::map; + + virtual void did_change(std::vector const& observers, std::vector const& invalidated) { + 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) { + GlobalContextProtect(m_context); + } + ~RealmDelegate() { + remove_all_notifications(); + + for (auto constructor : m_constructors) { + ValueUnprotect(m_context, constructor.second); + } + for (auto objectDefaults : m_defaults) { + for (auto value : objectDefaults.second) { + ValueUnprotect(m_context, value.second); + } + } + GlobalContextUnprotect(m_context); + } + + void add_notification(JSObjectRef notification) { + if (!m_notifications.count(notification)) { + ValueProtect(m_context, notification); + m_notifications.insert(notification); + } + } + void remove_notification(JSObjectRef notification) { + if (m_notifications.count(notification)) { + ValueUnprotect(m_context, notification); + m_notifications.erase(notification); + } + } + void remove_all_notifications() { + for (auto notification : m_notifications) { + ValueUnprotect(m_context, notification); + } + m_notifications.clear(); + } + + std::map m_defaults; + std::map m_constructors; + +private: + std::set m_notifications; + GlobalContextType m_context; + std::weak_ptr m_realm; + + void notify(const char *notification_name) { + JSValueRef arguments[2]; + SharedRealm realm = m_realm.lock(); + if (!realm) { + throw std::runtime_error("Realm no longer exists"); + } + ObjectType realm_object = WrapObject(m_context, realm::js::RealmClass(), new SharedRealm(realm)); + arguments[0] = realm_object; + arguments[1] = RJSValueForString(m_context, notification_name); + + for (auto callback : m_notifications) { + JSValueRef ex = NULL; + ObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, ex); + if (ex) { + throw RJSException(m_context, ex); + } + } + } +}; + +template +static RealmDelegate *get_delegate(Realm *realm) { + return dynamic_cast *>(realm->m_binding_context.get()); +} + +} } JSClassRef RJSRealmClass(); @@ -32,5 +122,3 @@ JSClassRef RJSRealmConstructorClass(); std::string RJSDefaultPath(); void RJSSetDefaultPath(std::string path); -std::map &RJSDefaults(realm::Realm *realm); -std::map &RJSConstructors(realm::Realm *realm); diff --git a/src/js_results.cpp b/src/js_results.cpp index a332f224..4ce6f159 100644 --- a/src/js_results.cpp +++ b/src/js_results.cpp @@ -96,7 +96,7 @@ JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef Results *copy = new Results(*results); copy->set_live(false); - return RJSWrapObject(ctx, RJSResultsClass(), copy); + return js::WrapObject(ctx, RJSResultsClass(), copy); } catch (std::exception &exp) { if (jsException) { @@ -144,7 +144,7 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl if (object_schema == realm->config().schema->end()) { throw std::runtime_error("Object type '" + className + "' not present in Realm."); } - return RJSWrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table)); + return js::WrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table)); } JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className, std::string queryString, std::vector args) { @@ -159,14 +159,14 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl query_builder::ArgumentConverter arguments(ctx, args); query_builder::apply_predicate(query, predicate, arguments, schema, object_schema->name); - return RJSWrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query))); + return js::WrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query))); } JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, bool live) { Results *results = new Results(realm, objectSchema, std::move(query)); results->set_live(live); - return RJSWrapObject(ctx, RJSResultsClass(), results); + return js::WrapObject(ctx, js::ResultsClass(), results); } JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) { @@ -233,7 +233,7 @@ JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const Ob } Results *results = new Results(realm, objectSchema, std::move(query), {std::move(columns), std::move(ascending)}); - return RJSWrapObject(ctx, RJSResultsClass(), results); + return js::WrapObject(ctx, js::ResultsClass(), results); } static const JSStaticFunction RJSResultsFuncs[] = { @@ -247,3 +247,9 @@ JSClassRef RJSResultsClass() { static JSClassRef s_objectClass = RJSCreateWrapperClass("Results", ResultsGetProperty, ResultsSetProperty, RJSResultsFuncs, ResultsPropertyNames, RJSCollectionClass()); return s_objectClass; } + +namespace realm { +namespace js { +JSClassRef ResultsClass() { return RJSResultsClass(); }; +} +} diff --git a/src/js_schema.cpp b/src/js_schema.cpp index 1bbc83da..e803fcc9 100644 --- a/src/js_schema.cpp +++ b/src/js_schema.cpp @@ -42,7 +42,7 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { SchemaWrapper *wrapper = new SchemaWrapper(); wrapper->schema = &schema; wrapper->owned = false; - return RJSWrapObject(ctx, RJSSchemaClass(), wrapper); + return js::WrapObject(ctx, RJSSchemaClass(), wrapper); } static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) { diff --git a/src/js_util.hpp b/src/js_util.hpp index 72c891c6..83331eb1 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -53,15 +53,6 @@ inline void RJSFinalize(JSObjectRef object) { JSObjectSetPrivate(object, NULL); } -template -inline JSObjectRef RJSWrapObject(JSContextRef ctx, JSClassRef jsClass, T object, JSValueRef prototype = NULL) { - JSObjectRef ref = JSObjectMake(ctx, jsClass, (void *)object); - if (prototype) { - JSObjectSetPrototype(ctx, ref, prototype); - } - return ref; -} - template inline T RJSGetInternal(JSObjectRef jsObject) { return static_cast(JSObjectGetPrivate(jsObject)); diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp index 8e36cd85..1065b23c 100644 --- a/src/jsc/js_compat.hpp +++ b/src/jsc/js_compat.hpp @@ -23,7 +23,7 @@ namespace realm { namespace js { - + static bool ValueIsUndefined(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsUndefined(ctx, value); } static bool ValueIsNull(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNull(ctx, value); } static bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsBoolean(ctx, value); } @@ -31,4 +31,27 @@ static bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { re static bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } static bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } -}} \ No newline at end of file +static void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } +static void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } + +static void ObjectCallAsFunction(jsc::Types::Context ctx, jsc::Types::Object function, jsc::Types::Object thisObject, size_t argsCount, const jsc::Types::Value args[], jsc::Types::Exception &exception) { + JSObjectCallAsFunction(ctx, function, thisObject, argsCount, args, &exception); +} + +static void GlobalContextProtect(jsc::Types::GlobalContext ctx) { JSGlobalContextRetain(ctx); } +static void GlobalContextUnprotect(jsc::Types::GlobalContext ctx) { JSGlobalContextRelease(ctx); } + +template +static jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::ObjectClass objectClass, T internal, jsc::Types::Object prototype = nullptr) { + JSObjectRef ref = JSObjectMake(ctx, objectClass, (void *)internal); + if (prototype) { + JSObjectSetPrototype(ctx, ref, prototype); + } + return ref; +} + +static jsc::Types::ObjectClass RealmClass(); +static jsc::Types::ObjectClass ListClass(); +static jsc::Types::ObjectClass ResultsClass(); + +}} diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index dc059cd6..33fe20c5 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -97,7 +97,7 @@ WRAP_CLASS_METHOD(RJSList, Filtered) WRAP_CLASS_METHOD(RJSList, Sorted) JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { - return RJSWrapObject(ctx, RJSListClass(), new List(list)); + return realm::js::WrapObject(ctx, realm::js::ListClass(), new List(list)); } static const JSStaticFunction RJSListFuncs[] = { @@ -116,3 +116,9 @@ JSClassRef RJSListClass() { static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass()); return s_listClass; } + +namespace realm { +namespace js { +JSClassRef ListClass() { return RJSListClass(); }; +} +} diff --git a/src/jsc/types.hpp b/src/jsc/types.hpp index b5f8db3d..ffb8076a 100644 --- a/src/jsc/types.hpp +++ b/src/jsc/types.hpp @@ -27,6 +27,8 @@ namespace jsc { struct Types { using Context = JSContextRef; + using GlobalContext = JSGlobalContextRef; + using ObjectClass = JSClassRef; using Value = JSValueRef; using Object = JSObjectRef; using String = JSStringRef;