diff --git a/src/RJSObject.mm b/src/RJSObject.mm index b56c2801..9a927286 100644 --- a/src/RJSObject.mm +++ b/src/RJSObject.mm @@ -21,6 +21,7 @@ #import "RJSResults.hpp" #import "RJSSchema.hpp" #import "RJSList.hpp" +#import "RJSRealm.hpp" #import "object_store.hpp" #import "object_accessor.hpp" @@ -63,7 +64,7 @@ JSClassRef RJSObjectClass() { } JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { - JSValueRef prototype = RJSPrototypeForClassName(object.object_schema.name); + JSValueRef prototype = RJSPrototypes(object.realm.get())[object.object_schema.name]; JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype); return jsObject; } @@ -86,13 +87,13 @@ template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueR return ret; } -template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) { - ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name); +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]; return defaults.find(prop_name) != defaults.end(); } -template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) { - ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name); +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]; return defaults[prop_name]; } diff --git a/src/RJSRealm.hpp b/src/RJSRealm.hpp index d2002ea1..0f28c4dc 100644 --- a/src/RJSRealm.hpp +++ b/src/RJSRealm.hpp @@ -17,6 +17,12 @@ //////////////////////////////////////////////////////////////////////////// #import "RJSUtil.hpp" +#include + +namespace realm { + class Realm; + using ObjectDefaults = std::map; +} extern const JSStaticFunction RJSRealmFuncs[]; @@ -28,3 +34,6 @@ std::string RJSDefaultPath(); void RJSSetDefaultPath(std::string path); JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException); + +std::map &RJSDefaults(realm::Realm *realm); +std::map &RJSPrototypes(realm::Realm *realm); diff --git a/src/RJSRealm.mm b/src/RJSRealm.mm index 78d45c5c..4f6cf76d 100644 --- a/src/RJSRealm.mm +++ b/src/RJSRealm.mm @@ -59,8 +59,36 @@ public: std::vector const& invalidated) { } + + JSGlobalContextRef m_context; + std::map m_defaults; + std::map m_prototypes; + + RJSRealmDelegate(JSGlobalContextRef ctx) : m_context(ctx) { + JSGlobalContextRetain(m_context); + } + + ~RJSRealmDelegate() { + for (auto prototype : m_prototypes) { + JSValueUnprotect(m_context, prototype.second); + } + for (auto objectDefaults : m_defaults) { + for (auto value : objectDefaults.second) { + JSValueUnprotect(m_context, value.second); + } + } + JSGlobalContextRelease(m_context); + } }; +std::map &RJSDefaults(Realm *realm) { + return static_cast(realm->m_delegate.get())->m_defaults; +} + +std::map &RJSPrototypes(Realm *realm) { + return static_cast(realm->m_delegate.get())->m_prototypes; +} + std::string writeablePathForFile(const std::string &fileName) { #if TARGET_OS_IPHONE // On iOS the Documents directory isn't user-visible, so put files there @@ -114,6 +142,8 @@ static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef pro JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { try { Realm::Config config; + std::map defaults; + std::map prototypes; switch (argumentCount) { case 0: config.path = RJSDefaultPath(); @@ -139,7 +169,7 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); if (!JSValueIsUndefined(ctx, schemaValue)) { - config.schema = std::make_unique(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue))); + config.schema = std::make_unique(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, prototypes)); } static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); @@ -159,8 +189,10 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a } SharedRealm realm = Realm::get_shared_realm(config); if (!realm->m_delegate) { - realm->m_delegate = std::make_unique(); + realm->m_delegate = std::make_unique(JSContextGetGlobalContext(ctx)); } + RJSDefaults(realm.get()) = defaults; + RJSPrototypes(realm.get()) = prototypes; return RJSWrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); } catch (std::exception &ex) { @@ -422,8 +454,9 @@ JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb try { RJSValidateArgumentCount(argumentCount, 0); SharedRealm realm = *RJSGetInternal(thisObject); - realm->invalidate(); + realm->close(); realm::Realm::s_global_cache.remove(realm->config().path, realm->thread_id()); + } catch (std::exception &exp) { if (jsException) { diff --git a/src/RJSSchema.hpp b/src/RJSSchema.hpp index 418725d4..f9a2e0bf 100644 --- a/src/RJSSchema.hpp +++ b/src/RJSSchema.hpp @@ -27,8 +27,4 @@ namespace realm { JSClassRef RJSSchemaClass(); JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema); -realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject); - -JSValueRef RJSPrototypeForClassName(const std::string &className); -realm::ObjectDefaults &RJSDefaultsForClassName(const std::string &className); -void RJSSchemaClearState(JSContextRef ctx); +realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map &defaults, std::map &prototypes); diff --git a/src/RJSSchema.mm b/src/RJSSchema.mm index 4b00eb96..da01f7f0 100644 --- a/src/RJSSchema.mm +++ b/src/RJSSchema.mm @@ -45,30 +45,6 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { return RJSWrapObject(ctx, RJSSchemaClass(), wrapper); } -static std::map s_defaults; -ObjectDefaults &RJSDefaultsForClassName(const std::string &className) { - return s_defaults[className]; -} - -static std::map s_prototypes; -JSValueRef RJSPrototypeForClassName(const std::string &className) { - return s_prototypes[className]; -} - -void RJSSchemaClearState(JSContextRef ctx) { - for (auto prototype : s_prototypes) { - JSValueUnprotect(ctx, prototype.second); - } - s_prototypes.clear(); - - for (auto defaults : s_defaults) { - for (auto value : defaults.second) { - JSValueUnprotect(ctx, value.second); - } - } - s_defaults.clear(); -} - static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyObject) { static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); static JSStringRef typeString = JSStringCreateWithUTF8CString("type"); @@ -116,7 +92,7 @@ static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyOb return prop; } -static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject) { +static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject, std::map &defaults, std::map &prototypes) { static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype"); JSObjectRef prototypeObject = NULL; @@ -136,8 +112,7 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema object must have a 'properties' array."); ObjectSchema objectSchema; - ObjectDefaults defaults; - + ObjectDefaults objectDefaults; static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); @@ -150,10 +125,10 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob JSValueRef defaultValue = JSObjectGetProperty(ctx, property, defaultString, NULL); if (!JSValueIsUndefined(ctx, defaultValue)) { JSValueProtect(ctx, defaultValue); - defaults.emplace(objectSchema.properties.back().name, defaultValue); + objectDefaults.emplace(objectSchema.properties.back().name, defaultValue); } } - s_defaults.emplace(objectSchema.name, std::move(defaults)); + defaults.emplace(objectSchema.name, std::move(objectDefaults)); static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey"); JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); @@ -169,18 +144,18 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob // store prototype if (prototypeObject) { JSValueProtect(ctx, prototypeObject); - s_prototypes[objectSchema.name] = std::move(prototypeObject); + prototypes[objectSchema.name] = std::move(prototypeObject); } return objectSchema; } -realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject) { +realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map &defaults, std::map &prototypes) { std::vector schema; size_t length = RJSValidatedListLength(ctx, jsonObject); for (unsigned int i = 0; i < length; i++) { JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i); - ObjectSchema objectSchema = RJSParseObjectSchema(ctx, jsonObjectSchema); + ObjectSchema objectSchema = RJSParseObjectSchema(ctx, jsonObjectSchema, defaults, prototypes); schema.emplace_back(std::move(objectSchema)); } diff --git a/src/RealmJS.h b/src/RealmJS.h index ad65e68b..daf34de6 100644 --- a/src/RealmJS.h +++ b/src/RealmJS.h @@ -24,7 +24,6 @@ // add realm apis to the given js context + (void)initializeContext:(JSContextRef)ctx; - + (void)clearTestState; @end diff --git a/src/RealmJS.mm b/src/RealmJS.mm index 13076b06..59699320 100644 --- a/src/RealmJS.mm +++ b/src/RealmJS.mm @@ -58,7 +58,6 @@ NSString *RealmFileDirectory() { static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { [RealmJS clearTestState]; - //RJSSchemaClearState(ctx); return NULL; } @@ -84,7 +83,7 @@ static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjec } + (void)clearTestState { - realm::Realm::s_global_cache.invalidate_all(); + realm::Realm::s_global_cache.close_all(); realm::Realm::s_global_cache.clear(); NSFileManager *manager = [NSFileManager defaultManager]; diff --git a/src/object-store/object_accessor.hpp b/src/object-store/object_accessor.hpp index 69bffa70..58802a06 100644 --- a/src/object-store/object_accessor.hpp +++ b/src/object-store/object_accessor.hpp @@ -60,8 +60,8 @@ namespace realm { static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); - static bool has_default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); - static ValueType default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); + static bool has_default_value_for_property(ContextType ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name); + static ValueType default_value_for_property(ContextType ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name); static bool to_bool(ContextType, ValueType &); static ValueType from_bool(ContextType, bool); @@ -260,8 +260,8 @@ namespace realm { object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update); } else if (created) { - if (Accessor::has_default_value_for_property(ctx, object_schema, prop.name)) { - object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, object_schema, prop.name), try_update); + if (Accessor::has_default_value_for_property(ctx, realm.get(), object_schema, prop.name)) { + object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, realm.get(), object_schema, prop.name), try_update); } else { throw std::runtime_error("Missing property value for property " + prop.name); diff --git a/src/object-store/shared_realm.cpp b/src/object-store/shared_realm.cpp index 42217a8f..8eab3700 100644 --- a/src/object-store/shared_realm.cpp +++ b/src/object-store/shared_realm.cpp @@ -295,6 +295,16 @@ void Realm::invalidate() m_group = nullptr; } +void Realm::close() +{ + invalidate(); + if (m_notifier) { + m_notifier->remove_realm(this); + m_notifier = nullptr; + } + m_delegate = nullptr; +} + bool Realm::compact() { verify_thread(); @@ -440,14 +450,15 @@ void RealmCache::cache_realm(SharedRealm &realm, std::thread::id thread_id) } } -void RealmCache::invalidate_all() +void RealmCache::close_all(std::thread::id thread_id) { std::lock_guard lock(m_mutex); for (auto &path_realms : m_cache) { - for (auto &realm_iter : path_realms.second) { - if (auto realm = realm_iter.second.lock()) { - realm->invalidate(); + auto thread_realm = path_realms.second.find(thread_id); + if (thread_realm != path_realms.second.end()) { + if (auto realm = thread_realm->second.lock()) { + realm->close(); } } } diff --git a/src/object-store/shared_realm.hpp b/src/object-store/shared_realm.hpp index 4721e37e..310c41a1 100644 --- a/src/object-store/shared_realm.hpp +++ b/src/object-store/shared_realm.hpp @@ -94,6 +94,7 @@ namespace realm { void invalidate(); bool compact(); + void close(); std::thread::id thread_id() const { return m_thread_id; } void verify_thread() const; @@ -131,7 +132,7 @@ namespace realm { SharedRealm get_any_realm(const std::string &path); void remove(const std::string &path, std::thread::id thread_id); void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id()); - void invalidate_all(); + void close_all(std::thread::id thread_id = std::this_thread::get_id()); void clear(); private: