diff --git a/objectbox/lib/src/native/bindings/bindings.dart b/objectbox/lib/src/native/bindings/bindings.dart index f6ea35baa..73ef39b0e 100644 --- a/objectbox/lib/src/native/bindings/bindings.dart +++ b/objectbox/lib/src/native/bindings/bindings.dart @@ -122,3 +122,13 @@ late final native_query_close = _lib!.lookup>('obx_query_close'); late final native_query_prop_close = _lib!.lookup>('obx_query_prop_close'); + +/// Keeps `this` alive until this call, preventing finalizers to run. +/// Necessary for objects with a finalizer attached because the optimizer may +/// mark the object as unused (-> GCed -> finalized) even before it's method +/// finished executing. +/// See https://github.com/dart-lang/sdk/issues/35770#issuecomment-840398463 +@pragma('vm:never-inline') +Object reachabilityFence(Object obj) { + return obj; +} diff --git a/objectbox/lib/src/native/query/property.dart b/objectbox/lib/src/native/query/property.dart index 5a6147b22..d0cd870bf 100644 --- a/objectbox/lib/src/native/query/property.dart +++ b/objectbox/lib/src/native/query/property.dart @@ -9,10 +9,8 @@ class PropertyQuery { bool _distinct = false; bool _caseSensitive = false; - PropertyQuery._(Pointer cQuery, ModelProperty property) - : _type = property.type, - _cProp = checkObxPtr( - C.query_prop(cQuery, property.id.id), 'property query') { + PropertyQuery._(this._cProp, this._type) { + checkObxPtr(_cProp, 'property query'); _cFinalizer = C.dartc_attach_finalizer( this, native_query_prop_close, _cProp.cast(), 64); if (_cFinalizer == nullptr) { @@ -38,6 +36,7 @@ class PropertyQuery { final ptr = malloc(); try { checkObx(C.query_prop_count(_cProp, ptr)); + reachabilityFence(this); return ptr.value; } finally { malloc.free(ptr); @@ -53,6 +52,7 @@ class PropertyQuery { try { cItems = checkObxPtr( findFn(_cProp, cDefault ?? nullptr), 'Property query failed'); + reachabilityFence(this); return listReadFn(cItems); } finally { if (cDefault != null) malloc.free(cDefault); @@ -64,6 +64,7 @@ class PropertyQuery { final ptr = malloc(); try { checkObx(C.query_prop_avg(_cProp, ptr, nullptr)); + reachabilityFence(this); return ptr.value; } finally { malloc.free(ptr); @@ -79,6 +80,7 @@ extension IntegerPropertyQuery on PropertyQuery { final ptr = malloc(); try { checkObx(fn(_cProp, ptr, nullptr)); + reachabilityFence(this); return ptr.value; } finally { malloc.free(ptr); @@ -101,6 +103,7 @@ extension IntegerPropertyQuery on PropertyQuery { set distinct(bool d) { _distinct = d; checkObx(C.query_prop_distinct(_cProp, d)); + reachabilityFence(this); } /// Minimum value of the property over all objects matching the query. @@ -175,6 +178,7 @@ extension DoublePropertyQuery on PropertyQuery { final ptr = malloc(); try { checkObx(fn(_cProp, ptr, nullptr)); + reachabilityFence(this); return ptr.value; } finally { malloc.free(ptr); @@ -197,6 +201,7 @@ extension DoublePropertyQuery on PropertyQuery { set distinct(bool d) { _distinct = d; checkObx(C.query_prop_distinct(_cProp, d)); + reachabilityFence(this); } /// Minimum value of the property over all objects matching the query. @@ -248,6 +253,7 @@ extension StringPropertyQuery on PropertyQuery { set caseSensitive(bool caseSensitive) { _caseSensitive = caseSensitive; checkObx(C.query_prop_distinct_case(_cProp, _distinct, _caseSensitive)); + reachabilityFence(this); } /// Get status of the case-sensitive configuration. @@ -263,6 +269,7 @@ extension StringPropertyQuery on PropertyQuery { set distinct(bool d) { _distinct = d; checkObx(C.query_prop_distinct_case(_cProp, d, _caseSensitive)); + reachabilityFence(this); } /// Returns the count of non-null values. diff --git a/objectbox/lib/src/native/query/query.dart b/objectbox/lib/src/native/query/query.dart index c0fe6e1a4..6fd333bf6 100644 --- a/objectbox/lib/src/native/query/query.dart +++ b/objectbox/lib/src/native/query/query.dart @@ -538,7 +538,7 @@ class _ByteVectorCondition func) => withNativeBytes( _value, - (Pointer ptr, int size) => + (Pointer ptr, int size) => func(builder._cBuilder, _property._model.id.id, ptr, size)); @override @@ -652,7 +652,11 @@ class Query { /// the whole result, e.g. for "result paging". /// /// Set offset=0 to reset to the default - starting from the first element. - set offset(int offset) => checkObx(C.query_offset(_ptr, offset)); + set offset(int offset) { + final result = checkObx(C.query_offset(_ptr, offset)); + reachabilityFence(this); + return result; + } /// Configure a [limit] for this query. /// @@ -661,13 +665,18 @@ class Query { /// the whole result, e.g. for "result paging". /// /// Set limit=0 to reset to the default behavior - no limit applied. - set limit(int limit) => checkObx(C.query_limit(_ptr, limit)); + set limit(int limit) { + final result = checkObx(C.query_limit(_ptr, limit)); + reachabilityFence(this); + return result; + } /// Returns the number of matching Objects. int count() { final ptr = malloc(); try { checkObx(C.query_count(_ptr, ptr)); + reachabilityFence(this); return ptr.value; } finally { malloc.free(ptr); @@ -710,12 +719,14 @@ class Query { _store.runInTransaction(TxMode.read, () { checkObx(C.query_visit(_ptr, visitor, nullptr)); }); + reachabilityFence(this); return result; } /// Finds Objects matching the query and returns their IDs. List findIds() { final idArrayPtr = checkObxPtr(C.query_find_ids(_ptr), 'find ids'); + reachabilityFence(this); try { final idArray = idArrayPtr.ref; return idArray.count == 0 @@ -732,6 +743,7 @@ class Query { final collector = objectCollector(result, _store, _entity); _store.runInTransaction( TxMode.read, () => checkObx(C.query_visit(_ptr, collector, nullptr))); + reachabilityFence(this); return result; } @@ -754,6 +766,7 @@ class Query { closed = true; C.dartc_stream_close(cStream); port.close(); + reachabilityFence(this); }; try { @@ -838,10 +851,18 @@ class Query { // } /// For internal testing purposes. - String describe() => dartStringFromC(C.query_describe(_ptr)); + String describe() { + final result = dartStringFromC(C.query_describe(_ptr)); + reachabilityFence(this); + return result; + } /// For internal testing purposes. - String describeParameters() => dartStringFromC(C.query_describe_params(_ptr)); + String describeParameters() { + final result = dartStringFromC(C.query_describe_params(_ptr)); + reachabilityFence(this); + return result; + } /// Use the same query conditions but only return a single property (field). /// @@ -852,7 +873,9 @@ class Query { /// var results = query.property(tInteger).find(); /// ``` PropertyQuery property(QueryProperty prop) { - final result = PropertyQuery._(_ptr, prop._model); + final result = PropertyQuery._( + C.query_prop(_ptr, prop._model.id.id), prop._model.type); + reachabilityFence(this); if (prop._model.type == OBXPropertyType.String) { result._caseSensitive = InternalStoreAccess.queryCS(_store); }