diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bc4b402..7e949134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ x.x.x Release notes (yyyy-MM-dd) * Added `objectForPrimaryKey(type, key)` method to `Realm` ### Bugfixes -* None +* Fix for crash when setting object properties to objects from other Realms 0.13.2 Release notes (2016-5-26) ============================================================= diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index b6e8eb40..bcfc06fb 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -106,7 +106,10 @@ struct NativeAccessor { 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)) { - return get_internal>(object)->row().get_index(); + auto realm_object = get_internal>(object); + if (realm_object->realm() == realm) { + return realm_object->row().get_index(); + } } auto object_schema = realm->config().schema->find(type); @@ -117,12 +120,18 @@ struct NativeAccessor { 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, ValueType &value) { + 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)) { - return get_internal>(object)->row().get_index(); + if (!Object::template is_instance>(ctx, object)) { + throw std::runtime_error("object is not a Realm 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)); diff --git a/src/js_results.hpp b/src/js_results.hpp index 5ef44699..08991f46 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -119,7 +119,7 @@ typename T::Object ResultsClass::create_filtered(ContextType ctx, const U &co } parser::Predicate predicate = parser::parse(query_string); - query_builder::ArgumentConverter converter(ctx, args); + query_builder::ArgumentConverter converter(ctx, realm, args); query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name); return create_instance(ctx, realm, object_schema, std::move(query)); diff --git a/src/object-store/src/object_accessor.hpp b/src/object-store/src/object_accessor.hpp index f57f42e7..5c54ad1d 100644 --- a/src/object-store/src/object_accessor.hpp +++ b/src/object-store/src/object_accessor.hpp @@ -107,7 +107,7 @@ namespace realm { static ValueType from_object(ContextType ctx, Object); // object index for an existing object - static size_t to_existing_object_index(ContextType ctx, ValueType &val); + static size_t to_existing_object_index(ContextType ctx, SharedRealm realm, ValueType &val); // list value acessors static size_t list_size(ContextType ctx, ValueType &val); diff --git a/src/object-store/src/parser/query_builder.hpp b/src/object-store/src/parser/query_builder.hpp index f9dba1bd..ae491072 100644 --- a/src/object-store/src/parser/query_builder.hpp +++ b/src/object-store/src/parser/query_builder.hpp @@ -50,7 +50,8 @@ class Arguments { template class ArgumentConverter : public Arguments { public: - ArgumentConverter(ContextType context, std::vector arguments) : m_arguments(arguments), m_ctx(context) {} + ArgumentConverter(ContextType context, SharedRealm realm, std::vector arguments) + : m_arguments(arguments), m_ctx(context), m_realm(std::move(realm)) {} using Accessor = realm::NativeAccessor; virtual bool bool_for_argument(size_t argument_index) { return Accessor::to_bool(m_ctx, argument_at(argument_index)); } @@ -60,12 +61,13 @@ class ArgumentConverter : public Arguments { virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } virtual std::string binary_for_argument(size_t argument_index) { return Accessor::to_binary(m_ctx, argument_at(argument_index)); } virtual Timestamp timestamp_for_argument(size_t argument_index) { return Accessor::to_timestamp(m_ctx, argument_at(argument_index)); } - virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } + virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, m_realm, argument_at(argument_index)); } virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } private: std::vector m_arguments; ContextType m_ctx; + SharedRealm m_realm; ValueType &argument_at(size_t index) { if (index >= m_arguments.size()) { diff --git a/tests/js/object-tests.js b/tests/js/object-tests.js index 86cb626a..a446f8f7 100644 --- a/tests/js/object-tests.js +++ b/tests/js/object-tests.js @@ -328,6 +328,17 @@ module.exports = { TestCase.assertEqual(obj.arrayCol[0].doubleCol, 3); TestCase.assertEqual(obj.arrayCol[1].doubleCol, 1); TestCase.assertEqual(obj.arrayCol[2].doubleCol, 2); + + // set object from another realm + var another = new Realm({path: 'another.realm', schema: realm.schema}); + var anotherObj; + another.write(function() { + anotherObj = another.create('TestObject', {doubleCol: 3}); + }); + realm.write(function() { + obj.objectCol = anotherObj; + }); + TestCase.assertEqual(obj.objectCol.doubleCol, 3); }, testEnumerablePropertyNames: function() { var realm = new Realm({schema: [schemas.BasicTypes]}); diff --git a/tests/js/results-tests.js b/tests/js/results-tests.js index 24b3822f..ad3636eb 100644 --- a/tests/js/results-tests.js +++ b/tests/js/results-tests.js @@ -130,6 +130,7 @@ module.exports = { testResultsFiltered: function() { var realm = new Realm({schema: [schemas.PersonObject, schemas.DefaultValues, schemas.TestObject]}); + realm.write(function() { realm.create('PersonObject', {name: 'Ari', age: 10}); realm.create('PersonObject', {name: 'Tim', age: 11}); @@ -155,9 +156,13 @@ module.exports = { TestCase.assertEqual(realm.objects('PersonObject').filtered('name = $0', 'Tim').length, 1); TestCase.assertEqual(realm.objects('PersonObject').filtered('age > $1 && age < $0', 13, 10).length, 3); + TestCase.assertThrows(function() { realm.objects('PersonObject').filtered('age > $2 && age < $0', 13, 10) }); + TestCase.assertThrows(function() { + realm.objects('PersonObject').filtered("invalidQuery"); + }); realm.write(function() { realm.create('DefaultValuesObject', {'dateCol': new Date(3)}); @@ -167,9 +172,19 @@ module.exports = { TestCase.assertEqual(realm.objects('DefaultValuesObject').filtered('dateCol > $0', new Date(4)).length, 1); TestCase.assertEqual(realm.objects('DefaultValuesObject').filtered('dateCol <= $0', new Date(4)).length, 2); + }, + + testResultsFilteredByForeignObject: function() { + var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]}); + var realm2 = new Realm({path: '2.realm', schema: realm.schema}); + var object; + + realm2.write(function() { + object = realm2.create('TestObject', {doubleCol: 1}); + }); TestCase.assertThrows(function() { - realm.objects('PersonObject').filtered("invalidQuery"); + realm.objects('LinkTypesObject').filtered('objectCol = $0', object); }); },