diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index 33e98e93ace..36be330e561 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart @@ -4083,6 +4083,36 @@ const MessageCode messageFfiAbiSpecificIntegerMappingInvalid = const MessageCode problemMessage: r"""Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size."""); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template + templateFfiCompoundImplementsFinalizable = + const Template( + problemMessageTemplate: + r"""#string '#name' can't implement Finalizable.""", + correctionMessageTemplate: + r"""Try removing the implements clause from '#name'.""", + withArguments: _withArgumentsFfiCompoundImplementsFinalizable); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code + codeFfiCompoundImplementsFinalizable = + const Code( + "FfiCompoundImplementsFinalizable", +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsFfiCompoundImplementsFinalizable( + String string, String name) { + if (string.isEmpty) throw 'No string provided'; + if (name.isEmpty) throw 'No name provided'; + name = demangleMixinApplicationName(name); + return new Message(codeFfiCompoundImplementsFinalizable, + problemMessage: """${string} '${name}' can't implement Finalizable.""", + correctionMessage: + """Try removing the implements clause from '${name}'.""", + arguments: {'string': string, 'name': name}); +} + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template< Message Function( diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index 1be33f8a7c1..a9f8b35694c 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -1087,6 +1087,8 @@ FfiCode.ANNOTATION_ON_POINTER_FIELD: status: needsEvaluation FfiCode.ARGUMENT_MUST_BE_A_CONSTANT: status: needsEvaluation +FfiCode.COMPOUND_IMPLEMENTS_FINALIZABLE: + status: needsEvaluation FfiCode.CREATION_OF_STRUCT_OR_UNION: status: needsEvaluation since: ~2.15 diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart index e3393a23dd3..6477d498911 100644 --- a/pkg/analyzer/lib/error/error.dart +++ b/pkg/analyzer/lib/error/error.dart @@ -507,6 +507,7 @@ const List errorCodeValues = [ FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED, FfiCode.ANNOTATION_ON_POINTER_FIELD, FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, + FfiCode.COMPOUND_IMPLEMENTS_FINALIZABLE, FfiCode.CREATION_OF_STRUCT_OR_UNION, FfiCode.EMPTY_STRUCT, FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD, diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart index 18488afb345..c52348acb17 100644 --- a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart +++ b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart @@ -167,6 +167,48 @@ class FfiCode extends AnalyzerErrorCode { hasPublishedDocs: true, ); + /** + * Parameters: + * 0: the name of the struct or union class + */ + // #### Description + // + // The analyzer produces this diagnostic when a subclass of either `Struct` + // or `Union` implements `Finalizable`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `S` + // implements `Finalizable`: + // + // ```dart + // import 'dart:ffi'; + // + // class [!S!] extends Struct implements Finalizable { + // external Pointer notEmpty; + // } + // ``` + // + // #### Common fixes + // + // Try removing the implements clause from the class: + // + // ```dart + // import 'dart:ffi'; + // + // class S extends Struct { + // external Pointer notEmpty; + // } + // ``` + static const FfiCode COMPOUND_IMPLEMENTS_FINALIZABLE = FfiCode( + 'COMPOUND_IMPLEMENTS_FINALIZABLE', + "The class '{0}' can't implement Finalizable.", + correctionMessage: "Try removing the implements clause from '{0}'.", + hasPublishedDocs: true, + ); + /** * No parameters. */ diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart index 34d16ab2a79..4c8241b128c 100644 --- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart +++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart @@ -146,9 +146,25 @@ class FfiVerifier extends RecursiveAstVisitor { } } - if (inCompound && node.declaredElement!.typeParameters.isNotEmpty) { - _errorReporter.reportErrorForNode( - FfiCode.GENERIC_STRUCT_SUBCLASS, node.name, [node.name.name]); + if (inCompound) { + if (node.declaredElement!.typeParameters.isNotEmpty) { + _errorReporter.reportErrorForNode( + FfiCode.GENERIC_STRUCT_SUBCLASS, node.name, [node.name.name]); + } + final implementsClause = node.implementsClause; + if (implementsClause != null) { + final compoundType = node.declaredElement!.thisType; + final structType = compoundType.superclass!; + final ffiLibrary = structType.element.library; + final finalizableElement = ffiLibrary.getType(_finalizableClassName)!; + final finalizableType = finalizableElement.thisType; + if (typeSystem.isSubtypeOf(compoundType, finalizableType)) { + _errorReporter.reportErrorForNode( + FfiCode.COMPOUND_IMPLEMENTS_FINALIZABLE, + node.name, + [node.name.name]); + } + } } super.visitClassDeclaration(node); } diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index c4921ef7390..0b689ee0dbe 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -15689,6 +15689,45 @@ FfiCode: return p.asFunction(isLeaf: true); } ``` + COMPOUND_IMPLEMENTS_FINALIZABLE: + problemMessage: "The class '{0}' can't implement Finalizable." + correctionMessage: "Try removing the implements clause from '{0}'." + comment: |- + Parameters: + 0: the name of the struct or union class + hasPublishedDocs: true + documentation: |- + #### Description + + The analyzer produces this diagnostic when a subclass of either `Struct` + or `Union` implements `Finalizable`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `S` + implements `Finalizable`: + + ```dart + import 'dart:ffi'; + + class [!S!] extends Struct implements Finalizable { + external Pointer notEmpty; + } + ``` + + #### Common fixes + + Try removing the implements clause from the class: + + ```dart + import 'dart:ffi'; + + class S extends Struct { + external Pointer notEmpty; + } + ``` CREATION_OF_STRUCT_OR_UNION: problemMessage: "Subclasses of 'Struct' and 'Union' are backed by native memory, and can't be instantiated by a generative constructor." correctionMessage: "Try allocating it via allocation, or load from a 'Pointer'." diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md index b62c66162f3..c3070d3be38 100644 --- a/pkg/analyzer/tool/diagnostics/diagnostics.md +++ b/pkg/analyzer/tool/diagnostics/diagnostics.md @@ -1992,6 +1992,42 @@ suitable value: var l = const [0]; {% endprettify %} +### compound_implements_finalizable + +_The class '{0}' can't implement Finalizable._ + +#### Description + +The analyzer produces this diagnostic when a subclass of either `Struct` +or `Union` implements `Finalizable`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `S` +implements `Finalizable`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class [!S!] extends Struct implements Finalizable { + external Pointer notEmpty; +} +{% endprettify %} + +#### Common fixes + +Try removing the implements clause from the class: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class S extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + ### concrete_class_has_enum_superinterface _Concrete classes can't have 'Enum' as a superinterface._ diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart index 274c9854c94..d7402a7c26c 100644 --- a/pkg/front_end/lib/src/api_unstable/vm.dart +++ b/pkg/front_end/lib/src/api_unstable/vm.dart @@ -70,6 +70,7 @@ export '../fasta/fasta_codes.dart' noLength, templateCantHaveNamedParameters, templateCantHaveOptionalParameters, + templateFfiCompoundImplementsFinalizable, templateFfiDartTypeMismatch, templateFfiEmptyStruct, templateFfiExpectedConstantArg, diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index ac4cc31a89c..98030eafec0 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -361,6 +361,7 @@ FastaUsageShort/analyzerCode: Fail FastaUsageShort/example: Fail FfiAbiSpecificIntegerInvalid/analyzerCode: Fail FfiAbiSpecificIntegerMappingInvalid/analyzerCode: Fail +FfiCompoundImplementsFinalizable/analyzerCode: Fail FfiDartTypeMismatch/analyzerCode: Fail FfiEmptyStruct/analyzerCode: Fail FfiExceptionalReturnNull/analyzerCode: Fail diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index f0fea4752e0..7079da9623a 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -4690,6 +4690,12 @@ FfiStructGeneric: problemMessage: "#string '#name' should not be generic." external: test/ffi_test.dart +FfiCompoundImplementsFinalizable: + # Used by dart:ffi + problemMessage: "#string '#name' can't implement Finalizable." + correctionMessage: "Try removing the implements clause from '#name'." + external: test/ffi_test.dart + FfiDartTypeMismatch: # Used by dart:ffi problemMessage: "Expected '#type' to be a subtype of '#type2'." diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt index abd5d63d0e6..1d062f6973f 100644 --- a/pkg/front_end/test/spell_checking_list_common.txt +++ b/pkg/front_end/test/spell_checking_list_common.txt @@ -1203,6 +1203,7 @@ filter filtered filtering final +finalizable finalization finalize finalized diff --git a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect index 8c98fe7d652..874fc407ab0 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect @@ -39,6 +39,7 @@ additionalExports = (ffi::nullptr, ffi::unsized, ffi::sizeOf, ffi::Dart_NativeMessageHandler, + ffi::NativeFinalizerFunction, ffi::Abi, ffi::AbiSpecificInteger, ffi::AbiSpecificIntegerArray, @@ -81,6 +82,7 @@ additionalExports = (ffi::nullptr, ffi::Long, ffi::LongLong, ffi::NativeApi, + ffi::NativeFinalizer, ffi::NativeFunction, ffi::NativeFunctionPointer, ffi::NativePort, @@ -129,6 +131,7 @@ additionalExports = (ffi::nullptr, ffi::unsized, ffi::sizeOf, ffi::Dart_NativeMessageHandler, + ffi::NativeFinalizerFunction, ffi::Abi, ffi::AbiSpecificInteger, ffi::AbiSpecificIntegerArray, @@ -171,6 +174,7 @@ additionalExports = (ffi::nullptr, ffi::Long, ffi::LongLong, ffi::NativeApi, + ffi::NativeFinalizer, ffi::NativeFunction, ffi::NativeFunctionPointer, ffi::NativePort, diff --git a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect index 820c44be973..6fb54df427d 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect @@ -39,6 +39,7 @@ additionalExports = (ffi::nullptr, ffi::unsized, ffi::sizeOf, ffi::Dart_NativeMessageHandler, + ffi::NativeFinalizerFunction, ffi::Abi, ffi::AbiSpecificInteger, ffi::AbiSpecificIntegerArray, @@ -81,6 +82,7 @@ additionalExports = (ffi::nullptr, ffi::Long, ffi::LongLong, ffi::NativeApi, + ffi::NativeFinalizer, ffi::NativeFunction, ffi::NativeFunctionPointer, ffi::NativePort, @@ -129,6 +131,7 @@ additionalExports = (ffi::nullptr, ffi::unsized, ffi::sizeOf, ffi::Dart_NativeMessageHandler, + ffi::NativeFinalizerFunction, ffi::Abi, ffi::AbiSpecificInteger, ffi::AbiSpecificIntegerArray, @@ -171,6 +174,7 @@ additionalExports = (ffi::nullptr, ffi::Long, ffi::LongLong, ffi::NativeApi, + ffi::NativeFinalizer, ffi::NativeFunction, ffi::NativeFunctionPointer, ffi::NativePort, diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart index 16571c043a4..7a67f893bcd 100644 --- a/pkg/vm/lib/target/vm.dart +++ b/pkg/vm/lib/target/vm.dart @@ -409,7 +409,8 @@ class VmTarget extends Target { bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) => super.allowPlatformPrivateLibraryAccess(importer, imported) || importer.path.contains('runtime/tests/vm/dart') || - importer.path.contains('test-lib'); + importer.path.contains('test-lib') || + importer.path.contains('tests/ffi'); // TODO(sigmund,ahe): limit this to `dart-ext` libraries only (see // https://github.com/dart-lang/sdk/issues/29763). diff --git a/pkg/vm/lib/transformations/ffi/definitions.dart b/pkg/vm/lib/transformations/ffi/definitions.dart index fe38da05496..b5d095e6f70 100644 --- a/pkg/vm/lib/transformations/ffi/definitions.dart +++ b/pkg/vm/lib/transformations/ffi/definitions.dart @@ -8,6 +8,7 @@ import 'package:front_end/src/api_unstable/vm.dart' messageFfiAbiSpecificIntegerMappingInvalid, messageFfiPackedAnnotationAlignment, messageNonPositiveArrayDimensions, + templateFfiCompoundImplementsFinalizable, templateFfiEmptyStruct, templateFfiFieldAnnotation, templateFfiFieldNull, @@ -321,6 +322,19 @@ class _FfiDefinitionTransformer extends FfiTransformer { return null; } + final finalizableType = FutureOrType( + InterfaceType(finalizableClass, Nullability.nullable), + Nullability.nullable); + if (env.isSubtypeOf(InterfaceType(node, Nullability.nonNullable), + finalizableType, SubtypeCheckMode.ignoringNullabilities)) { + diagnosticReporter.report( + templateFfiCompoundImplementsFinalizable.withArguments( + node.superclass!.name, node.name), + node.fileOffset, + 1, + node.location!.file); + } + if (node.superclass == structClass) { final packingAnnotations = _getPackedAnnotations(node); if (packingAnnotations.length > 1) { diff --git a/pkg/vm/lib/transformations/ffi/finalizable.dart b/pkg/vm/lib/transformations/ffi/finalizable.dart index 758465ba19c..84b146967a3 100644 --- a/pkg/vm/lib/transformations/ffi/finalizable.dart +++ b/pkg/vm/lib/transformations/ffi/finalizable.dart @@ -14,6 +14,9 @@ import 'package:kernel/type_environment.dart'; /// This transformation is not AST-node preserving. [Expression]s and /// [Statement]s can be replaced by other [Expression]s and [Statement]s /// respectively. This means one cannot do `visitX() { super.visitX() as X }`. +/// +/// This transform must be run on the standard libaries as well. For example +/// `NativeFinalizer`s `attach` implementation depends on it. mixin FinalizableTransformer on Transformer { TypeEnvironment get env; Procedure get reachabilityFenceFunction; diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc index 4a4067b6f5c..36516fc0cb4 100644 --- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc @@ -1108,6 +1108,13 @@ DART_EXPORT void ReleaseClosureCallback() { Dart_DeletePersistentHandle_DL(closure_to_callback_); } +//////////////////////////////////////////////////////////////////////////////// +// NativeFinalizer tests + +DART_EXPORT void SetArgumentTo42(intptr_t* token) { + *token = 42; +} + //////////////////////////////////////////////////////////////////////////////// // Functions for testing @FfiNative. diff --git a/runtime/docs/gc.md b/runtime/docs/gc.md index ac9696d47b2..6086968c54a 100644 --- a/runtime/docs/gc.md +++ b/runtime/docs/gc.md @@ -210,15 +210,17 @@ StoreInstanceField(container, value, NoBarrier) The GC is aware of two types of objects for the purposes of running finalizers. 1) `FinalizerEntry` -2) `Finalizer` (`FinalizerBase`, `_FinalizerImpl`) +2) `Finalizer` (`FinalizerBase`, `_FinalizerImpl`, `_NativeFinalizer`) -A `FinalizerEntry` contains the `value`, the optional `detach` key, and the `token`, and a reference to the `finalizer`. +A `FinalizerEntry` contains the `value`, the optional `detach` key, and the `token`, a reference to the `finalizer`, and an `external_size`. An entry only holds on weakly to the value, detach key, and finalizer. (Similar to how `WeakReference` only holds on weakly to target). A `Finalizer` contains all entries, a list of entries of which the value is collected, and a reference to the isolate. When the value of an entry is GCed, the entry is added over to the collected list. If any entry is moved to the collected list, a message is sent that invokes the finalizer to call the callback on all entries in that list. +For native finalizers, the native callback is immediately invoked in the GC. +However, we still send a message to the native finalizer to clean up the entries from all entries and the detachments. When a finalizer is detached by the user, the entry token is set to the entry itself and is removed from the all entries set. This ensures that if the entry was already moved to the collected list, the finalizer is not executed. @@ -236,3 +238,5 @@ An alternative design would be to pre-allocate a `WeakReference` in the finalize This would be at the cost of an extra object. If the finalizer object itself is GCed, the callback is not run for any of the attachments. + +On Isolate shutdown, native finalizers are run, but regular finalizers are not. diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc index e1a6b241e58..20f675667b9 100644 --- a/runtime/lib/ffi.cc +++ b/runtime/lib/ffi.cc @@ -15,6 +15,7 @@ #include "vm/dart_api_impl.h" #include "vm/exceptions.h" #include "vm/flags.h" +#include "vm/heap/gc_shared.h" #include "vm/log.h" #include "vm/native_arguments.h" #include "vm/native_entry.h" @@ -243,4 +244,40 @@ DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) { return Pointer::New(type_arg, reinterpret_cast(FfiResolve)); } +DEFINE_FFI_NATIVE_ENTRY(FinalizerEntry_SetExternalSize, + void, + (Dart_Handle entry_handle, intptr_t external_size)) { + Thread* const thread = Thread::Current(); + TransitionNativeToVM transition(thread); + Zone* const zone = thread->zone(); + const auto& entry_object = + Object::Handle(zone, Api::UnwrapHandle(entry_handle)); + const auto& entry = FinalizerEntry::Cast(entry_object); + + Heap::Space space; + intptr_t external_size_diff; + { + NoSafepointScope no_safepoint; + space = SpaceForExternal(entry.ptr()); + const intptr_t external_size_old = entry.external_size(); + if (FLAG_trace_finalizers) { + THR_Print("Setting external size from %" Pd " to %" Pd + " bytes in %s space\n", + external_size_old, external_size, space == 0 ? "new" : "old"); + } + external_size_diff = external_size - external_size_old; + if (external_size_diff == 0) { + return; + } + entry.set_external_size(external_size); + } + // The next call cannot be in safepoint. + if (external_size_diff > 0) { + IsolateGroup::Current()->heap()->AllocatedExternal(external_size_diff, + space); + } else { + IsolateGroup::Current()->heap()->FreedExternal(-external_size_diff, space); + } +}; + } // namespace dart diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index e2ef579add2..8e95dab294f 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -184,6 +184,7 @@ static ObjectPtr ValidateMessageObject(Zone* zone, bool error_found = false; Function& erroneous_closure_function = Function::Handle(zone); Class& erroneous_nativewrapper_class = Class::Handle(zone); + Class& erroneous_finalizable_class = Class::Handle(zone); const char* error_message = nullptr; { @@ -244,6 +245,7 @@ static ObjectPtr ValidateMessageObject(Zone* zone, MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary); // TODO(http://dartbug.com/47777): Send and exit support: remove this. MESSAGE_SNAPSHOT_ILLEGAL(Finalizer); + MESSAGE_SNAPSHOT_ILLEGAL(NativeFinalizer); MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference); MESSAGE_SNAPSHOT_ILLEGAL(Pointer); MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort); @@ -257,6 +259,11 @@ static ObjectPtr ValidateMessageObject(Zone* zone, error_found = true; break; } + if (klass.implements_finalizable()) { + erroneous_finalizable_class = klass.ptr(); + error_found = true; + break; + } } } raw->untag()->VisitPointers(&visitor); @@ -271,13 +278,18 @@ static ObjectPtr ValidateMessageObject(Zone* zone, "Illegal argument in isolate message" " : (object is a closure - %s)", erroneous_closure_function.ToCString()); - } else { - ASSERT(!erroneous_nativewrapper_class.IsNull()); + } else if (!erroneous_nativewrapper_class.IsNull()) { exception_message = OS::SCreate(zone, "Illegal argument in isolate message" " : (object extends NativeWrapper - %s)", erroneous_nativewrapper_class.ToCString()); + } else { + ASSERT(!erroneous_finalizable_class.IsNull()); + exception_message = OS::SCreate(zone, + "Illegal argument in isolate message" + " : (object implements Finalizable - %s)", + erroneous_finalizable_class.ToCString()); } return Exceptions::CreateUnhandledException( zone, Exceptions::kArgumentValue, exception_message); diff --git a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart index d714c560be8..7dc9690b09d 100644 --- a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart +++ b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart @@ -18,6 +18,7 @@ // inserting an object that cannot be allocated in new space. import 'dart:async'; +import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; import 'dart:nativewrappers'; @@ -246,6 +247,9 @@ class SendReceiveTest extends SendReceiveTestBase { await testWeakProperty(); await testWeakReference(); await testFinalizer(); + await testNativeFinalizer(); + await testFinalizable(); + await testPointer(); await testForbiddenClosures(); } @@ -765,6 +769,27 @@ class SendReceiveTest extends SendReceiveTestBase { Expect.throwsArgumentError(() => sendPort.send(finalizer)); } + Future testNativeFinalizer() async { + print('testNativeFinalizer'); + + final finalizer = NativeFinalizer(nullptr); + Expect.throwsArgumentError(() => sendPort.send(finalizer)); + } + + Future testFinalizable() async { + print('testFinalizable'); + + final finalizable = MyFinalizable(); + Expect.throwsArgumentError(() => sendPort.send(finalizable)); + } + + Future testPointer() async { + print('testPointer'); + + final pointer = Pointer.fromAddress(0xdeadbeef); + Expect.throwsArgumentError(() => sendPort.send(pointer)); + } + Future testForbiddenClosures() async { print('testForbiddenClosures'); for (final closure in nonCopyableClosures) { @@ -788,6 +813,8 @@ class Nonce { String toString() => 'Nonce($value)'; } +class MyFinalizable implements Finalizable {} + main() async { await SendReceiveTest().run(); } diff --git a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart index 91fd27d2ed3..fc6257315f3 100644 --- a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart +++ b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart @@ -20,6 +20,7 @@ // inserting an object that cannot be allocated in new space. import 'dart:async'; +import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; import 'dart:nativewrappers'; @@ -248,6 +249,9 @@ class SendReceiveTest extends SendReceiveTestBase { await testWeakProperty(); await testWeakReference(); await testFinalizer(); + await testNativeFinalizer(); + await testFinalizable(); + await testPointer(); await testForbiddenClosures(); } @@ -767,6 +771,27 @@ class SendReceiveTest extends SendReceiveTestBase { Expect.throwsArgumentError(() => sendPort.send(finalizer)); } + Future testNativeFinalizer() async { + print('testNativeFinalizer'); + + final finalizer = NativeFinalizer(nullptr); + Expect.throwsArgumentError(() => sendPort.send(finalizer)); + } + + Future testFinalizable() async { + print('testFinalizable'); + + final finalizable = MyFinalizable(); + Expect.throwsArgumentError(() => sendPort.send(finalizable)); + } + + Future testPointer() async { + print('testPointer'); + + final pointer = Pointer.fromAddress(0xdeadbeef); + Expect.throwsArgumentError(() => sendPort.send(pointer)); + } + Future testForbiddenClosures() async { print('testForbiddenClosures'); for (final closure in nonCopyableClosures) { @@ -790,6 +815,8 @@ class Nonce { String toString() => 'Nonce($value)'; } +class MyFinalizable implements Finalizable {} + main() async { await SendReceiveTest().run(); } diff --git a/runtime/vm/bootstrap_natives.cc b/runtime/vm/bootstrap_natives.cc index 91b4f6faa13..4258c9ddd2f 100644 --- a/runtime/vm/bootstrap_natives.cc +++ b/runtime/vm/bootstrap_natives.cc @@ -31,6 +31,15 @@ static const struct NativeEntries { #endif // !DART_PRECOMPILED_RUNTIME }; +#define REGISTER_FFI_NATIVE_ENTRY(name, return_type, argument_types) \ + {"" #name, reinterpret_cast(BootstrapNatives::FN_##name)}, + +static const struct FfiNativeEntries { + const char* name_; + void* function_; +} BootStrapFfiEntries[] = { + BOOTSTRAP_FFI_NATIVE_LIST(REGISTER_FFI_NATIVE_ENTRY)}; + Dart_NativeFunction BootstrapNatives::Lookup(Dart_Handle name, int argument_count, bool* auto_setup_scope) { @@ -55,6 +64,19 @@ Dart_NativeFunction BootstrapNatives::Lookup(Dart_Handle name, return NULL; } +void* BootstrapNatives::LookupFfiNative(const char* name, + uintptr_t argument_count) { + int num_entries = + sizeof(BootStrapFfiEntries) / sizeof(struct FfiNativeEntries); + for (int i = 0; i < num_entries; i++) { + const struct FfiNativeEntries* entry = &(BootStrapFfiEntries[i]); + if (strcmp(name, entry->name_) == 0) { + return entry->function_; + } + } + return nullptr; +} + const uint8_t* BootstrapNatives::Symbol(Dart_NativeFunction nf) { int num_entries = sizeof(BootStrapEntries) / sizeof(struct NativeEntries); for (int i = 0; i < num_entries; i++) { @@ -71,6 +93,9 @@ void Bootstrap::SetupNativeResolver() { Dart_NativeEntryResolver resolver = BootstrapNatives::Lookup; + Dart_FfiNativeResolver ffi_native_resolver = + BootstrapNatives::LookupFfiNative; + Dart_NativeEntrySymbol symbol_resolver = BootstrapNatives::Symbol; library = Library::AsyncLibrary(); @@ -107,6 +132,7 @@ void Bootstrap::SetupNativeResolver() { ASSERT(!library.IsNull()); library.set_native_entry_resolver(resolver); library.set_native_entry_symbol_resolver(symbol_resolver); + library.set_ffi_native_resolver(ffi_native_resolver); library = Library::IsolateLibrary(); ASSERT(!library.IsNull()); diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index df65a0dbcbb..00a3c4c2f15 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -452,12 +452,18 @@ namespace dart { V(ParameterMirror_type, 3) \ V(VariableMirror_type, 2) +#define BOOTSTRAP_FFI_NATIVE_LIST(V) \ + V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t)) + class BootstrapNatives : public AllStatic { public: static Dart_NativeFunction Lookup(Dart_Handle name, int argument_count, bool* auto_setup_scope); + // For use with @FfiNative. + static void* LookupFfiNative(const char* name, uintptr_t argument_count); + static const uint8_t* Symbol(Dart_NativeFunction nf); #define DECLARE_BOOTSTRAP_NATIVE(name, ignored) \ @@ -468,8 +474,12 @@ class BootstrapNatives : public AllStatic { #if !defined(DART_PRECOMPILED_RUNTIME) MIRRORS_BOOTSTRAP_NATIVE_LIST(DECLARE_BOOTSTRAP_NATIVE) #endif - #undef DECLARE_BOOTSTRAP_NATIVE + +#define DECLARE_BOOTSTRAP_FFI_NATIVE(name, return_type, argument_types) \ + static return_type FN_##name argument_types; + BOOTSTRAP_FFI_NATIVE_LIST(DECLARE_BOOTSTRAP_FFI_NATIVE) +#undef DECLARE_BOOTSTRAP_FFI_NATIVE }; } // namespace dart diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc index e91180e0030..141ae5afbba 100644 --- a/runtime/vm/class_finalizer.cc +++ b/runtime/vm/class_finalizer.cc @@ -1034,6 +1034,10 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) { if (FLAG_trace_class_finalization) { THR_Print("Finalize types in %s\n", cls.ToCString()); } + bool implements_finalizable = + cls.Name() == Symbols::Finalizable().ptr() && + Library::UrlOf(cls.library()) == Symbols::DartFfi().ptr(); + // Finalize super class. Class& super_class = Class::Handle(zone, cls.SuperClass()); if (!super_class.IsNull()) { @@ -1049,15 +1053,24 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) { if (!super_type.IsNull()) { super_type = FinalizeType(super_type); cls.set_super_type(super_type); + implements_finalizable |= + Class::ImplementsFinalizable(super_type.type_class()); } // Finalize interface types (but not necessarily interface classes). - Array& interface_types = Array::Handle(zone, cls.interfaces()); - AbstractType& interface_type = AbstractType::Handle(zone); + const auto& interface_types = Array::Handle(zone, cls.interfaces()); + auto& interface_type = AbstractType::Handle(zone); + auto& interface_class = Class::Handle(zone); for (intptr_t i = 0; i < interface_types.Length(); i++) { interface_type ^= interface_types.At(i); interface_type = FinalizeType(interface_type); + interface_class = interface_type.type_class(); + ASSERT(!interface_class.IsNull()); + FinalizeTypesInClass(interface_class); interface_types.SetAt(i, interface_type); + implements_finalizable |= + Class::ImplementsFinalizable(interface_type.type_class()); } + cls.set_implements_finalizable(implements_finalizable); cls.set_is_type_finalized(); RegisterClassInHierarchy(thread->zone(), cls); diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h index 501c3a827c5..7d764c7bebe 100644 --- a/runtime/vm/class_id.h +++ b/runtime/vm/class_id.h @@ -70,6 +70,7 @@ typedef uint16_t ClassIdTagType; V(Type) \ V(FinalizerBase) \ V(Finalizer) \ + V(NativeFinalizer) \ V(FinalizerEntry) \ V(FunctionType) \ V(TypeRef) \ diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc index 8d01fc7901d..2c4af9c17d9 100644 --- a/runtime/vm/compiler/backend/range_analysis.cc +++ b/runtime/vm/compiler/backend/range_analysis.cc @@ -2807,6 +2807,7 @@ void LoadFieldInstr::InferRange(RangeAnalysis* analysis, Range* range) { case Slot::Kind::kFinalizerEntry_next: case Slot::Kind::kFinalizerEntry_token: case Slot::Kind::kFinalizerEntry_value: + case Slot::Kind::kNativeFinalizer_callback: case Slot::Kind::kFunction_data: case Slot::Kind::kFunction_signature: case Slot::Kind::kFunctionType_named_parameter_names: diff --git a/runtime/vm/compiler/backend/slot.cc b/runtime/vm/compiler/backend/slot.cc index dfd04aca34f..93561801658 100644 --- a/runtime/vm/compiler/backend/slot.cc +++ b/runtime/vm/compiler/backend/slot.cc @@ -237,6 +237,7 @@ bool Slot::IsImmutableLengthSlot() const { case Slot::Kind::kFinalizerEntry_next: case Slot::Kind::kFinalizerEntry_token: case Slot::Kind::kFinalizerEntry_value: + case Slot::Kind::kNativeFinalizer_callback: case Slot::Kind::kFunction_data: case Slot::Kind::kFunction_signature: case Slot::Kind::kFunctionType_named_parameter_names: diff --git a/runtime/vm/compiler/backend/slot.h b/runtime/vm/compiler/backend/slot.h index 995ce8cf66b..aa0c8ada15a 100644 --- a/runtime/vm/compiler/backend/slot.h +++ b/runtime/vm/compiler/backend/slot.h @@ -101,6 +101,7 @@ class ParsedFunction; V(Closure, UntaggedClosure, context, Context, FINAL) \ V(Closure, UntaggedClosure, hash, Context, VAR) \ V(Finalizer, UntaggedFinalizer, callback, Closure, FINAL) \ + V(NativeFinalizer, UntaggedFinalizer, callback, Pointer, FINAL) \ V(Function, UntaggedFunction, data, Dynamic, FINAL) \ V(FunctionType, UntaggedFunctionType, named_parameter_names, Array, FINAL) \ V(FunctionType, UntaggedFunctionType, parameter_types, Array, FINAL) \ @@ -170,6 +171,7 @@ NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(FOR_EACH_NATIVE_SLOT) V(ClosureData, UntaggedClosureData, default_type_arguments_kind, Uint8, \ FINAL) \ V(FinalizerBase, UntaggedFinalizerBase, isolate, IntPtr, VAR) \ + V(FinalizerEntry, UntaggedFinalizerEntry, external_size, IntPtr, VAR) \ V(Function, UntaggedFunction, entry_point, Uword, FINAL) \ V(Function, UntaggedFunction, kind_tag, Uint32, FINAL) \ V(Function, UntaggedFunction, packed_fields, Uint32, FINAL) \ diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc index 9abd1fd95a7..905570f7aa7 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -825,6 +825,7 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function, V(FinalizerEntry_getNext, FinalizerEntry_next) \ V(FinalizerEntry_getToken, FinalizerEntry_token) \ V(FinalizerEntry_getValue, FinalizerEntry_value) \ + V(NativeFinalizer_getCallback, NativeFinalizer_callback) \ V(GrowableArrayLength, GrowableObjectArray_length) \ V(ImmutableLinkedHashBase_getData, ImmutableLinkedHashBase_data) \ V(ImmutableLinkedHashBase_getIndex, ImmutableLinkedHashBase_index) \ @@ -845,11 +846,8 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function, V(Finalizer_setCallback, Finalizer_callback) \ V(FinalizerBase_setAllEntries, FinalizerBase_all_entries) \ V(FinalizerBase_setDetachments, FinalizerBase_detachments) \ - V(FinalizerEntry_setDetach, FinalizerEntry_detach) \ - V(FinalizerEntry_setFinalizer, FinalizerEntry_finalizer) \ - V(FinalizerEntry_setNext, FinalizerEntry_next) \ V(FinalizerEntry_setToken, FinalizerEntry_token) \ - V(FinalizerEntry_setValue, FinalizerEntry_value) \ + V(NativeFinalizer_setCallback, NativeFinalizer_callback) \ V(LinkedHashBase_setData, LinkedHashBase_data) \ V(LinkedHashBase_setIndex, LinkedHashBase_index) \ V(WeakProperty_setKey, WeakProperty_key) \ @@ -938,6 +936,8 @@ bool FlowGraphBuilder::IsRecognizedMethodForFlowGraph( case MethodRecognizer::kFinalizerBase_getIsolateFinalizers: case MethodRecognizer::kFinalizerBase_setIsolate: case MethodRecognizer::kFinalizerBase_setIsolateFinalizers: + case MethodRecognizer::kFinalizerEntry_allocate: + case MethodRecognizer::kFinalizerEntry_getExternalSize: case MethodRecognizer::kObjectEquals: case MethodRecognizer::kStringBaseLength: case MethodRecognizer::kStringBaseIsEmpty: @@ -1621,6 +1621,42 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( body += NullConstant(); body += StoreNativeField(Slot::FinalizerBase_entries_collected()); break; + case MethodRecognizer::kFinalizerEntry_allocate: { + // Object value, Object token, Object detach, FinalizerBase finalizer + ASSERT_EQUAL(function.NumParameters(), 4); + + const auto class_table = thread_->isolate_group()->class_table(); + ASSERT(class_table->HasValidClassAt(kFinalizerEntryCid)); + const auto& finalizer_entry_class = + Class::ZoneHandle(H.zone(), class_table->At(kFinalizerEntryCid)); + + body += + AllocateObject(TokenPosition::kNoSource, finalizer_entry_class, 0); + LocalVariable* const entry = MakeTemporary("entry"); + // No GC from here to the end. + body += LoadLocal(entry); + body += LoadLocal(parsed_function_->RawParameterVariable(0)); + body += StoreNativeField(Slot::FinalizerEntry_value()); + body += LoadLocal(entry); + body += LoadLocal(parsed_function_->RawParameterVariable(1)); + body += StoreNativeField(Slot::FinalizerEntry_token()); + body += LoadLocal(entry); + body += LoadLocal(parsed_function_->RawParameterVariable(2)); + body += StoreNativeField(Slot::FinalizerEntry_detach()); + body += LoadLocal(entry); + body += LoadLocal(parsed_function_->RawParameterVariable(3)); + body += StoreNativeField(Slot::FinalizerEntry_finalizer()); + body += LoadLocal(entry); + body += UnboxedIntConstant(0, kUnboxedIntPtr); + body += StoreNativeField(Slot::FinalizerEntry_external_size()); + break; + } + case MethodRecognizer::kFinalizerEntry_getExternalSize: + ASSERT_EQUAL(function.NumParameters(), 1); + body += LoadLocal(parsed_function_->RawParameterVariable(0)); + body += LoadNativeField(Slot::FinalizerEntry_external_size()); + body += Box(kUnboxedInt64); + break; #define IL_BODY(method, slot) \ case MethodRecognizer::k##method: \ ASSERT_EQUAL(function.NumParameters(), 1); \ diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h index 1676f7c6468..dc6050c7b69 100644 --- a/runtime/vm/compiler/recognized_methods_list.h +++ b/runtime/vm/compiler/recognized_methods_list.h @@ -123,15 +123,16 @@ namespace dart { 0xb3e66928) \ V(_FinalizerImpl, get:_callback, Finalizer_getCallback, 0x6f3d56bc) \ V(_FinalizerImpl, set:_callback, Finalizer_setCallback, 0xc6aa96f9) \ + V(_NativeFinalizer, get:_callback, NativeFinalizer_getCallback, 0x5cb374f5) \ + V(_NativeFinalizer, set:_callback, NativeFinalizer_setCallback, 0xb12268f2) \ + V(FinalizerEntry, allocate, FinalizerEntry_allocate, 0xe0bad878) \ V(FinalizerEntry, get:value, FinalizerEntry_getValue, 0xf5c9b9d7) \ - V(FinalizerEntry, set:value, FinalizerEntry_setValue, 0x5501cc54) \ V(FinalizerEntry, get:detach, FinalizerEntry_getDetach, 0x171cd968) \ - V(FinalizerEntry, set:detach, FinalizerEntry_setDetach, 0x7654ebe5) \ - V(FinalizerEntry, set:finalizer, FinalizerEntry_setFinalizer, 0x15cfefe9) \ V(FinalizerEntry, get:token, FinalizerEntry_getToken, 0x04915a72) \ V(FinalizerEntry, set:token, FinalizerEntry_setToken, 0x63c96cef) \ V(FinalizerEntry, get:next, FinalizerEntry_getNext, 0x7102d7a4) \ - V(FinalizerEntry, set:next, FinalizerEntry_setNext, 0xd0b2ee61) \ + V(FinalizerEntry, get:externalSize, FinalizerEntry_getExternalSize, \ + 0x47df4d22) \ V(Float32x4, _Float32x4FromDoubles, Float32x4FromDoubles, 0x1845792b) \ V(Float32x4, Float32x4.zero, Float32x4Zero, 0xd3b64002) \ V(Float32x4, _Float32x4Splat, Float32x4Splat, 0x13a552c3) \ diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc index 2973168647d..5ae1b5651bc 100644 --- a/runtime/vm/compiler/runtime_api.cc +++ b/runtime/vm/compiler/runtime_api.cc @@ -447,6 +447,8 @@ static uword GetInstanceSizeImpl(const dart::Class& handle) { return Finalizer::InstanceSize(); case kFinalizerEntryCid: return FinalizerEntry::InstanceSize(); + case kNativeFinalizerCid: + return NativeFinalizer::InstanceSize(); case kByteBufferCid: case kByteDataViewCid: case kPointerCid: diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h index 2b2a7d319fb..41ad454c44f 100644 --- a/runtime/vm/compiler/runtime_api.h +++ b/runtime/vm/compiler/runtime_api.h @@ -1026,7 +1026,14 @@ class FinalizerBase : public AllStatic { class Finalizer : public AllStatic { public: + static word callback_offset(); static word type_arguments_offset(); + static word InstanceSize(); + FINAL_CLASS(); +}; + +class NativeFinalizer : public AllStatic { + public: static word callback_offset(); static word InstanceSize(); FINAL_CLASS(); @@ -1034,11 +1041,12 @@ class Finalizer : public AllStatic { class FinalizerEntry : public AllStatic { public: - static word value_offset(); static word detach_offset(); - static word token_offset(); - static word next_offset(); + static word external_size_offset(); static word finalizer_offset(); + static word next_offset(); + static word token_offset(); + static word value_offset(); static word InstanceSize(); FINAL_CLASS(); }; diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h index e2c52c5fe8a..78c6e5d5ef9 100644 --- a/runtime/vm/compiler/runtime_offsets_extracted.h +++ b/runtime/vm/compiler/runtime_offsets_extracted.h @@ -220,12 +220,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 12; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 164; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 120; + 168; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 124; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 184; + 188; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word OneByteString_data_offset = 12; static constexpr dart::compiler::target::word PointerBase_data_offset = 4; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = 8; @@ -443,12 +443,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 20; static constexpr dart::compiler::target::word FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 24; @@ -532,7 +536,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 28; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 28; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 24; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 88; @@ -806,12 +811,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -1035,12 +1040,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 40; static constexpr dart::compiler::target::word FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 48; @@ -1125,7 +1134,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 48; static constexpr dart::compiler::target::word Field_InstanceSize = 96; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 56; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 56; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 64; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 48; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 128; @@ -1397,12 +1407,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 12; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 164; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 120; + 168; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 124; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 184; + 188; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word OneByteString_data_offset = 12; static constexpr dart::compiler::target::word PointerBase_data_offset = 4; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = 8; @@ -1620,12 +1630,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 20; static constexpr dart::compiler::target::word FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 24; @@ -1706,7 +1720,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 28; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 28; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 24; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 88; @@ -1980,12 +1995,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -2209,12 +2224,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 40; static constexpr dart::compiler::target::word FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 48; @@ -2300,7 +2319,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 48; static constexpr dart::compiler::target::word Field_InstanceSize = 96; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 56; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 56; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 64; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 48; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 128; @@ -2572,12 +2592,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -2801,12 +2821,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 32; static constexpr dart::compiler::target::word FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 36; @@ -2891,7 +2915,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 32; static constexpr dart::compiler::target::word Field_InstanceSize = 64; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 40; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 40; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 40; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 96; @@ -3163,12 +3188,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -3392,12 +3417,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 32; static constexpr dart::compiler::target::word FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 36; @@ -3483,7 +3512,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 32; static constexpr dart::compiler::target::word Field_InstanceSize = 64; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 40; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 40; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 40; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 96; @@ -3755,12 +3785,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 12; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 164; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 120; + 168; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 124; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 184; + 188; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word OneByteString_data_offset = 12; static constexpr dart::compiler::target::word PointerBase_data_offset = 4; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = 8; @@ -3978,12 +4008,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 20; static constexpr dart::compiler::target::word FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 24; @@ -4069,7 +4103,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 28; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 28; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 24; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 88; @@ -4343,12 +4378,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -4572,12 +4607,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 40; static constexpr dart::compiler::target::word FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 48; @@ -4663,7 +4702,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 48; static constexpr dart::compiler::target::word Field_InstanceSize = 96; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 56; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 56; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 64; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 48; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 128; @@ -4931,12 +4971,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 12; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 164; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 120; + 168; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 124; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 184; + 188; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word OneByteString_data_offset = 12; static constexpr dart::compiler::target::word PointerBase_data_offset = 4; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = 8; @@ -5154,12 +5194,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 20; static constexpr dart::compiler::target::word FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 24; @@ -5243,7 +5287,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 28; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 28; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 24; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 88; @@ -5511,12 +5556,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -5740,12 +5785,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 40; static constexpr dart::compiler::target::word FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 48; @@ -5830,7 +5879,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 48; static constexpr dart::compiler::target::word Field_InstanceSize = 96; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 56; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 56; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 64; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 48; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 128; @@ -6096,12 +6146,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 12; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 164; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 120; + 168; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 124; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 184; + 188; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word OneByteString_data_offset = 12; static constexpr dart::compiler::target::word PointerBase_data_offset = 4; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = 8; @@ -6319,12 +6369,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 20; static constexpr dart::compiler::target::word FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 24; @@ -6405,7 +6459,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 28; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 28; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 24; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 88; @@ -6673,12 +6728,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -6902,12 +6957,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 40; static constexpr dart::compiler::target::word FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 48; @@ -6993,7 +7052,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 48; static constexpr dart::compiler::target::word Field_InstanceSize = 96; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 56; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 56; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 64; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 48; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 128; @@ -7259,12 +7319,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -7488,12 +7548,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 32; static constexpr dart::compiler::target::word FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 36; @@ -7578,7 +7642,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 32; static constexpr dart::compiler::target::word Field_InstanceSize = 64; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 40; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 40; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 40; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 96; @@ -7844,12 +7909,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -8073,12 +8138,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 16; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 32; static constexpr dart::compiler::target::word FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 36; @@ -8164,7 +8233,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 32; static constexpr dart::compiler::target::word Field_InstanceSize = 64; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 40; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 40; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 40; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 96; @@ -8430,12 +8500,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 12; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 164; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 120; + 168; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 124; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 184; + 188; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word OneByteString_data_offset = 12; static constexpr dart::compiler::target::word PointerBase_data_offset = 4; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = 8; @@ -8653,12 +8723,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 12; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 4; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 20; static constexpr dart::compiler::target::word FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 24; @@ -8744,7 +8818,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 28; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 28; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 32; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 24; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 88; @@ -9012,12 +9087,12 @@ static constexpr dart::compiler::target::word NativeArguments_retval_offset = 24; static constexpr dart::compiler::target::word NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word ObjectStore_double_type_offset = - 328; -static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 240; + 336; +static constexpr dart::compiler::target::word ObjectStore_int_type_offset = 248; static constexpr dart::compiler::target::word ObjectStore_string_type_offset = - 368; + 376; static constexpr dart::compiler::target::word ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word OneByteString_data_offset = 16; static constexpr dart::compiler::target::word PointerBase_data_offset = 8; static constexpr dart::compiler::target::word Pointer_type_arguments_offset = @@ -9241,12 +9316,16 @@ static constexpr dart::compiler::target::word FinalizerBase_detachments_offset = static constexpr dart::compiler::target::word FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word + FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word FinalizerEntry_token_offset = 24; +static constexpr dart::compiler::target::word FinalizerEntry_value_offset = 8; +static constexpr dart::compiler::target::word NativeFinalizer_callback_offset = + 40; static constexpr dart::compiler::target::word FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word FunctionType_named_parameter_names_offset = 48; @@ -9332,7 +9411,8 @@ static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = 48; static constexpr dart::compiler::target::word Field_InstanceSize = 96; static constexpr dart::compiler::target::word Finalizer_InstanceSize = 56; -static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 56; +static constexpr dart::compiler::target::word FinalizerEntry_InstanceSize = 64; +static constexpr dart::compiler::target::word NativeFinalizer_InstanceSize = 48; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word Function_InstanceSize = 128; @@ -9631,13 +9711,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 164; + AOT_ObjectStore_double_type_offset = 168; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 120; + 124; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 184; + AOT_ObjectStore_string_type_offset = 188; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 12; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 4; @@ -9876,16 +9956,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 4; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 12; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 12; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 4; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 20; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 24; @@ -9982,7 +10066,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 28; + 32; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 24; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 44; @@ -10291,13 +10377,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -10537,16 +10623,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 24; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 48; @@ -10645,7 +10735,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 80; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 56; + 64; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 80; @@ -10957,13 +11049,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -11203,16 +11295,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 24; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 48; @@ -11312,7 +11408,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 80; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 56; + 64; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 80; @@ -11620,13 +11718,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -11866,16 +11964,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 16; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 16; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 32; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 36; @@ -11974,7 +12076,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 40; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 32; + 40; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 40; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 56; @@ -12282,13 +12386,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -12528,16 +12632,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 16; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 16; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 32; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 36; @@ -12637,7 +12745,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 40; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 32; + 40; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 40; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 56; @@ -12945,13 +13055,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 164; + AOT_ObjectStore_double_type_offset = 168; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 120; + 124; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 184; + AOT_ObjectStore_string_type_offset = 188; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 12; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 4; @@ -13190,16 +13300,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 4; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 12; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 12; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 4; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 20; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 24; @@ -13298,7 +13412,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 28; + 32; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 24; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 44; @@ -13607,13 +13723,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -13853,16 +13969,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 24; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 48; @@ -13962,7 +14082,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 80; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 56; + 64; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 80; @@ -14265,13 +14387,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 164; + AOT_ObjectStore_double_type_offset = 168; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 120; + 124; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 184; + AOT_ObjectStore_string_type_offset = 188; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 12; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 4; @@ -14510,16 +14632,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 4; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 12; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 12; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 4; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 20; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 24; @@ -14616,7 +14742,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 28; + 32; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 24; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 44; @@ -14918,13 +15046,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -15164,16 +15292,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 24; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 48; @@ -15272,7 +15404,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 80; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 56; + 64; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 80; @@ -15577,13 +15711,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -15823,16 +15957,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 24; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 48; @@ -15932,7 +16070,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 80; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 56; + 64; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 80; @@ -16233,13 +16373,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -16479,16 +16619,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 16; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 16; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 32; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 36; @@ -16587,7 +16731,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 40; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 32; + 40; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 40; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 56; @@ -16888,13 +17034,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -17134,16 +17280,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 24; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 12; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 16; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 20; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 16; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 32; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 36; @@ -17243,7 +17393,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 40; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 32; + 40; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 40; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 56; @@ -17544,13 +17696,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 164; + AOT_ObjectStore_double_type_offset = 168; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 120; + 124; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 184; + AOT_ObjectStore_string_type_offset = 188; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 108; + 112; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 12; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 4; @@ -17789,16 +17941,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 4; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 4; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 12; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 16; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 20; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 12; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 4; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 20; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 24; @@ -17897,7 +18053,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 28; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 28; + 32; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 24; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 44; @@ -18199,13 +18357,13 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_NativeArguments_thread_offset = 0; static constexpr dart::compiler::target::word - AOT_ObjectStore_double_type_offset = 328; + AOT_ObjectStore_double_type_offset = 336; static constexpr dart::compiler::target::word AOT_ObjectStore_int_type_offset = - 240; + 248; static constexpr dart::compiler::target::word - AOT_ObjectStore_string_type_offset = 368; + AOT_ObjectStore_string_type_offset = 376; static constexpr dart::compiler::target::word AOT_ObjectStore_type_type_offset = - 216; + 224; static constexpr dart::compiler::target::word AOT_OneByteString_data_offset = 16; static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 8; @@ -18445,16 +18603,20 @@ static constexpr dart::compiler::target::word AOT_FinalizerBase_entries_collected_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset = 8; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = - 8; static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset = 16; -static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = - 24; +static constexpr dart::compiler::target::word + AOT_FinalizerEntry_external_size_offset = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_finalizer_offset = 32; static constexpr dart::compiler::target::word AOT_FinalizerEntry_next_offset = 40; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_token_offset = + 24; +static constexpr dart::compiler::target::word AOT_FinalizerEntry_value_offset = + 8; +static constexpr dart::compiler::target::word + AOT_NativeFinalizer_callback_offset = 40; static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56; static constexpr dart::compiler::target::word AOT_FunctionType_named_parameter_names_offset = 48; @@ -18554,7 +18716,9 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 80; static constexpr dart::compiler::target::word AOT_Finalizer_InstanceSize = 56; static constexpr dart::compiler::target::word AOT_FinalizerEntry_InstanceSize = - 56; + 64; +static constexpr dart::compiler::target::word AOT_NativeFinalizer_InstanceSize = + 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Function_InstanceSize = 80; diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h index a2da8368fa5..1e03be5f276 100644 --- a/runtime/vm/compiler/runtime_offsets_list.h +++ b/runtime/vm/compiler/runtime_offsets_list.h @@ -305,11 +305,13 @@ FIELD(FinalizerBase, detachments_offset) \ FIELD(FinalizerBase, entries_collected_offset) \ FIELD(FinalizerBase, isolate_offset) \ - FIELD(FinalizerEntry, value_offset) \ FIELD(FinalizerEntry, detach_offset) \ - FIELD(FinalizerEntry, token_offset) \ + FIELD(FinalizerEntry, external_size_offset) \ FIELD(FinalizerEntry, finalizer_offset) \ FIELD(FinalizerEntry, next_offset) \ + FIELD(FinalizerEntry, token_offset) \ + FIELD(FinalizerEntry, value_offset) \ + FIELD(NativeFinalizer, callback_offset) \ FIELD(FunctionType, hash_offset) \ FIELD(FunctionType, named_parameter_names_offset) \ FIELD(FunctionType, nullability_offset) \ @@ -374,6 +376,7 @@ SIZEOF(Field, InstanceSize, UntaggedField) \ SIZEOF(Finalizer, InstanceSize, UntaggedFinalizer) \ SIZEOF(FinalizerEntry, InstanceSize, UntaggedFinalizerEntry) \ + SIZEOF(NativeFinalizer, InstanceSize, UntaggedNativeFinalizer) \ SIZEOF(Float32x4, InstanceSize, UntaggedFloat32x4) \ SIZEOF(Float64x2, InstanceSize, UntaggedFloat64x2) \ SIZEOF(Function, InstanceSize, UntaggedFunction) \ diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc index 501568e0661..2081ad03dbd 100644 --- a/runtime/vm/dart_entry.cc +++ b/runtime/vm/dart_entry.cc @@ -744,8 +744,13 @@ ObjectPtr DartLibraryCalls::HandleFinalizerMessage( auto* const zone = thread->zone(); auto* const isolate = thread->isolate(); auto* const object_store = thread->isolate_group()->object_store(); - const auto& function = - Function::Handle(zone, object_store->handle_finalizer_message_function()); + auto& function = Function::Handle(zone); + if (finalizer.IsFinalizer()) { + function ^= object_store->handle_finalizer_message_function(); + } else { + ASSERT(finalizer.IsNativeFinalizer()); + function ^= object_store->handle_native_finalizer_message_function(); + } ASSERT(!function.IsNull()); Array& args = Array::Handle(zone, isolate->isolate_object_store()->dart_args_1()); diff --git a/runtime/vm/heap/gc_shared.cc b/runtime/vm/heap/gc_shared.cc index 65f56c3ddd7..a9e727514df 100644 --- a/runtime/vm/heap/gc_shared.cc +++ b/runtime/vm/heap/gc_shared.cc @@ -36,4 +36,9 @@ void GCLinkedLists::FlushInto(GCLinkedLists* to) { #undef FOREACH } +Heap::Space SpaceForExternal(FinalizerEntryPtr raw_entry) { + ASSERT(!raw_entry->untag()->value().IsSmi()); + return raw_entry->untag()->value()->IsOldObject() ? Heap::kOld : Heap::kNew; +} + } // namespace dart diff --git a/runtime/vm/heap/gc_shared.h b/runtime/vm/heap/gc_shared.h index 7e0cb2dd9c8..b7aac4050b5 100644 --- a/runtime/vm/heap/gc_shared.h +++ b/runtime/vm/heap/gc_shared.h @@ -37,6 +37,7 @@ template class GCLinkedList { public: void Enqueue(PtrType ptr) { + ASSERT(ptr->untag()->next_seen_by_gc().IsRawNull()); ptr->untag()->next_seen_by_gc_ = head_; if (head_ == Type::null()) { tail_ = ptr; @@ -92,6 +93,55 @@ struct GCLinkedLists { #define TRACE_FINALIZER(format, ...) #endif +// The space in which `raw_entry`'s `value` is. +Heap::Space SpaceForExternal(FinalizerEntryPtr raw_entry); + +// Runs the finalizer if not detached, detaches the value and set external size +// to 0. +// TODO(http://dartbug.com/47777): Can this be merged with +// NativeFinalizer::RunCallback? +template +void RunNativeFinalizerCallback(NativeFinalizerPtr raw_finalizer, + FinalizerEntryPtr raw_entry, + Heap::Space before_gc_space, + GCVisitorType* visitor) { + PointerPtr callback_pointer = raw_finalizer->untag()->callback(); + const auto callback = reinterpret_cast( + callback_pointer->untag()->data()); + ObjectPtr token_object = raw_entry->untag()->token(); + const bool is_detached = token_object == raw_entry; + const intptr_t external_size = raw_entry->untag()->external_size(); + if (is_detached) { + // Detached from Dart code. + ASSERT(token_object == raw_entry); + ASSERT(external_size == 0); + if (FLAG_trace_finalizers) { + TRACE_FINALIZER("Not running native finalizer %p callback %p, detached", + raw_finalizer->untag(), callback); + } + } else { + // TODO(http://dartbug.com/48615): Unbox pointer address in entry. + ASSERT(token_object.IsPointer()); + PointerPtr token = static_cast(token_object); + void* peer = reinterpret_cast(token->untag()->data()); + if (FLAG_trace_finalizers) { + TRACE_FINALIZER("Running native finalizer %p callback %p with token %p", + raw_finalizer->untag(), callback, peer); + } + raw_entry.untag()->set_token(raw_entry); + callback(peer); + if (external_size > 0) { + if (FLAG_trace_finalizers) { + TRACE_FINALIZER("Clearing external size %" Pd " bytes in %s space", + external_size, before_gc_space == 0 ? "new" : "old"); + } + visitor->isolate_group()->heap()->FreedExternal(external_size, + before_gc_space); + raw_entry->untag()->set_external_size(0); + } + } +} + // This function processes all finalizer entries discovered by a scavenger or // marker. If an entry is referencing an object that is going to die, such entry // is cleared and enqueued in the respective finalizer. @@ -102,9 +152,9 @@ struct GCLinkedLists { // For more documentation see runtime/docs/gc.md. // // |GCVisitorType| is a concrete type implementing either marker or scavenger. -// It is expected to provide |SetNullIfCollected| method for clearing fields -// referring to dead objects and |kName| field which contains visitor name for -// tracing output. +// It is expected to provide |ForwardOrSetNullIfCollected| method for clearing +// fields referring to dead objects and |kName| field which contains visitor +// name for tracing output. template void MournFinalized(GCVisitorType* visitor) { FinalizerEntryPtr current_entry = @@ -117,12 +167,24 @@ void MournFinalized(GCVisitorType* visitor) { current_entry->untag()->next_seen_by_gc_ = FinalizerEntry::null(); uword heap_base = current_entry->heap_base(); - const bool value_collected_this_gc = GCVisitorType::SetNullIfCollected( - heap_base, ¤t_entry->untag()->value_); - GCVisitorType::SetNullIfCollected(heap_base, - ¤t_entry->untag()->detach_); - GCVisitorType::SetNullIfCollected(heap_base, - ¤t_entry->untag()->finalizer_); + const Heap::Space before_gc_space = SpaceForExternal(current_entry); + const bool value_collected_this_gc = + GCVisitorType::ForwardOrSetNullIfCollected( + heap_base, ¤t_entry->untag()->value_); + if (!value_collected_this_gc && before_gc_space == Heap::kNew) { + const Heap::Space after_gc_space = SpaceForExternal(current_entry); + if (after_gc_space == Heap::kOld) { + const intptr_t external_size = current_entry->untag()->external_size_; + TRACE_FINALIZER("Promoting external size %" Pd + " bytes from new to old space", + external_size); + visitor->isolate_group()->heap()->PromotedExternal(external_size); + } + } + GCVisitorType::ForwardOrSetNullIfCollected( + heap_base, ¤t_entry->untag()->detach_); + GCVisitorType::ForwardOrSetNullIfCollected( + heap_base, ¤t_entry->untag()->finalizer_); ObjectPtr token_object = current_entry->untag()->token(); // See sdk/lib/_internal/vm/lib/internal_patch.dart FinalizerBase.detach. @@ -136,7 +198,7 @@ void MournFinalized(GCVisitorType* visitor) { current_entry->untag()); // Do nothing, the finalizer has been GCed. - } else if (finalizer.IsFinalizer()) { + } else { TRACE_FINALIZER("Value collected entry %p finalizer %p", current_entry->untag(), finalizer->untag()); @@ -155,6 +217,18 @@ void MournFinalized(GCVisitorType* visitor) { ASSERT(Thread::Current()->IsAtSafepoint() || Thread::Current()->BypassSafepoints()); + if (finalizer.IsNativeFinalizer()) { + NativeFinalizerPtr native_finalizer = + static_cast(finalizer); + + // Immediately call native callback. + RunNativeFinalizerCallback(native_finalizer, current_entry, + before_gc_space, visitor); + + // Fall-through sending a message to clear the entries and remove + // from detachments. + } + FinalizerEntryPtr previous_head = finalizer_dart->untag()->exchange_entries_collected(current_entry); current_entry->untag()->set_next(previous_head); @@ -179,9 +253,6 @@ void MournFinalized(GCVisitorType* visitor) { /*before_events*/ false); } } - } else { - // TODO(http://dartbug.com/47777): Implement NativeFinalizer. - UNREACHABLE(); } } diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc index 63a150b7f6c..2b77843a9fa 100644 --- a/runtime/vm/heap/marker.cc +++ b/runtime/vm/heap/marker.cc @@ -296,15 +296,16 @@ class MarkingVisitorBase : public ObjectPointerVisitor { // If we did not mark the target through a weak property in a later round, // then the target is dead and we should clear it. - SetNullIfCollected(cur_weak->heap_base(), &cur_weak->untag()->target_); + ForwardOrSetNullIfCollected(cur_weak->heap_base(), + &cur_weak->untag()->target_); cur_weak = next_weak; } } // Returns whether the object referred to in `ptr_address` was GCed this GC. - static bool SetNullIfCollected(uword heap_base, - CompressedObjectPtr* ptr_address) { + static bool ForwardOrSetNullIfCollected(uword heap_base, + CompressedObjectPtr* ptr_address) { ObjectPtr raw = ptr_address->Decompress(heap_base); if (raw.IsRawNull()) { // Object already null before this GC. diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc index 194529657e9..e11759a3156 100644 --- a/runtime/vm/heap/scavenger.cc +++ b/runtime/vm/heap/scavenger.cc @@ -7,9 +7,11 @@ #include "platform/assert.h" #include "platform/leak_sanitizer.h" #include "vm/class_id.h" +#include "vm/compiler/runtime_api.h" #include "vm/dart.h" #include "vm/dart_api_state.h" #include "vm/flag_list.h" +#include "vm/flags.h" #include "vm/heap/become.h" #include "vm/heap/gc_shared.h" #include "vm/heap/pages.h" @@ -19,12 +21,14 @@ #include "vm/heap/weak_table.h" #include "vm/isolate.h" #include "vm/lockers.h" +#include "vm/log.h" #include "vm/longjump.h" #include "vm/object.h" #include "vm/object_id_ring.h" #include "vm/object_set.h" #include "vm/port.h" #include "vm/stack_frame.h" +#include "vm/tagged_pointer.h" #include "vm/thread_barrier.h" #include "vm/thread_registry.h" #include "vm/timeline.h" @@ -318,8 +322,8 @@ class ScavengerVisitorBase : public ObjectPointerVisitor { NewPage* head() const { return head_; } NewPage* tail() const { return tail_; } - static bool SetNullIfCollected(uword heap_base, - CompressedObjectPtr* ptr_address); + static bool ForwardOrSetNullIfCollected(uword heap_base, + CompressedObjectPtr* ptr_address); private: void UpdateStoreBuffer(ObjectPtr obj) { @@ -1197,9 +1201,44 @@ void Scavenger::IterateStoreBuffers(ScavengerVisitorBase* visitor) { ASSERT(raw_object->untag()->IsRemembered()); raw_object->untag()->ClearRememberedBit(); visitor->VisitingOldObject(raw_object); - // Note that this treats old-space WeakProperties as strong. A dead key - // won't be reclaimed until after the key is promoted. - raw_object->untag()->VisitPointersNonvirtual(visitor); + intptr_t class_id = raw_object->GetClassId(); + // This treats old-space weak references in WeakProperty, WeakReference, + // and FinalizerEntry as strong references. This prevents us from having + // to enqueue them in `visitor->delayed_`. Enqueuing them in the delayed + // would require having two `next_seen_by_gc` fields. One for used during + // marking and one for the objects seen in the store buffers + new space. + // Treating the weak references as strong here means we can have a single + // `next_seen_by_gc` field. + if (UNLIKELY(class_id == kFinalizerEntryCid)) { + FinalizerEntryPtr raw_entry = + static_cast(raw_object); + // Detect `FinalizerEntry::value` promotion to update external space. + // + // This treats old-space FinalizerEntry fields as strong. Values, deatch + // keys, and finalizers in new space won't be reclaimed until after they + // are promoted. + // This will only visit the strong references, end enqueue the entry. + // This enables us to update external space in MournFinalized. + const Heap::Space before_gc_space = SpaceForExternal(raw_entry); + UntaggedFinalizerEntry::VisitFinalizerEntryPointers(raw_entry, visitor); + if (before_gc_space == Heap::kNew) { + const Heap::Space after_gc_space = SpaceForExternal(raw_entry); + if (after_gc_space == Heap::kOld) { + const intptr_t external_size = raw_entry->untag()->external_size_; + if (FLAG_trace_finalizers) { + THR_Print( + "Scavenger %p Store buffer, promoting external size %" Pd + " bytes from new to old space\n", + visitor, external_size); + } + visitor->isolate_group()->heap()->PromotedExternal(external_size); + } + } + } else { + // This treats old-space WeakProperties and WeakReferences as strong. A + // dead key or target won't be reclaimed until after it is promoted. + raw_object->untag()->VisitPointersNonvirtual(visitor); + } } pending->Reset(); // Return the emptied block for recycling (no need to check threshold). @@ -1542,7 +1581,8 @@ void ScavengerVisitorBase::MournOrUpdateWeakReferences() { // If we did not mark the target through a weak property in a later round, // then the target is dead and we should clear it. - SetNullIfCollected(cur_weak->heap_base(), &cur_weak->untag()->target_); + ForwardOrSetNullIfCollected(cur_weak->heap_base(), + &cur_weak->untag()->target_); // Advance to next weak reference in the queue. cur_weak = next_weak; @@ -1551,7 +1591,7 @@ void ScavengerVisitorBase::MournOrUpdateWeakReferences() { // Returns whether the object referred to in `ptr_address` was GCed this GC. template -bool ScavengerVisitorBase::SetNullIfCollected( +bool ScavengerVisitorBase::ForwardOrSetNullIfCollected( uword heap_base, CompressedObjectPtr* ptr_address) { ObjectPtr raw = ptr_address->Decompress(heap_base); diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h index 38c2ed6910f..5a93aec8f68 100644 --- a/runtime/vm/heap/scavenger.h +++ b/runtime/vm/heap/scavenger.h @@ -449,6 +449,7 @@ class Scavenger { template friend class ScavengerVisitorBase; friend class ScavengerWeakVisitor; + friend class ScavengerFinalizerVisitor; DISALLOW_COPY_AND_ASSIGN(Scavenger); }; diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index 287aa91e86e..e797f5ce8af 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -1955,6 +1955,10 @@ void Isolate::set_origin_id(Dart_Port id) { origin_id_ = id; } +void Isolate::set_finalizers(const GrowableObjectArray& value) { + finalizers_ = value.ptr(); +} + bool Isolate::IsPaused() const { #if defined(PRODUCT) return false; @@ -2476,14 +2480,14 @@ void Isolate::LowLevelShutdown() { } // Set live finalizers isolate to null, before deleting the message handler. - // TODO(http://dartbug.com/47777): How to detect if the isolate field was ever - // initialized beyond RAW_NULL? const auto& finalizers = GrowableObjectArray::Handle(stack_zone.GetZone(), finalizers_); if (!finalizers.IsNull()) { const intptr_t num_finalizers = finalizers.Length(); auto& weak_reference = WeakReference::Handle(stack_zone.GetZone()); auto& finalizer = FinalizerBase::Handle(stack_zone.GetZone()); + auto& current_entry = FinalizerEntry::Handle(stack_zone.GetZone()); + auto& all_entries = LinkedHashSet::Handle(stack_zone.GetZone()); for (int i = 0; i < num_finalizers; i++) { weak_reference ^= finalizers.At(i); finalizer ^= weak_reference.target(); @@ -2499,6 +2503,17 @@ void Isolate::LowLevelShutdown() { // TODO(http://dartbug.com/47777): Send and exit support. UNREACHABLE(); } + + if (finalizer.IsNativeFinalizer()) { + // Immediately call native callback. + const auto& native_finalizer = NativeFinalizer::Cast(finalizer); + all_entries = finalizer.all_entries(); + LinkedHashSet::Iterator iterator(all_entries); + while (iterator.MoveNext()) { + current_entry ^= iterator.CurrentKey(); + native_finalizer.RunCallback(current_entry, "Isolate shutdown"); + } + } } } } diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index bd1e9fb72eb..76bbd89bbc1 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -1065,6 +1065,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry { void set_init_callback_data(void* value) { init_callback_data_ = value; } void* init_callback_data() const { return init_callback_data_; } + void set_finalizers(const GrowableObjectArray& value); static intptr_t finalizers_offset() { return OFFSET_OF(Isolate, finalizers_); } diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc index b0d52509e0b..9bd1a91fde0 100644 --- a/runtime/vm/message_snapshot.cc +++ b/runtime/vm/message_snapshot.cc @@ -3944,7 +3944,7 @@ ObjectPtr ReadMessage(Thread* thread, Message* message) { } else if (message->IsFinalizerInvocationRequest()) { PersistentHandle* handle = message->persistent_handle(); Object& msg_obj = Object::Handle(thread->zone(), handle->ptr()); - ASSERT(msg_obj.IsFinalizer()); + ASSERT(msg_obj.IsFinalizer() || msg_obj.IsNativeFinalizer()); return msg_obj.ptr(); } else if (message->IsPersistentHandle()) { return ReadObjectGraphCopyMessage(thread, message->persistent_handle()); diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h index 460d382c97c..5022ffcf08d 100644 --- a/runtime/vm/native_entry.h +++ b/runtime/vm/native_entry.h @@ -15,7 +15,6 @@ #include "vm/native_function.h" #include "vm/runtime_entry.h" - namespace dart { // Forward declarations. @@ -51,6 +50,9 @@ typedef ObjectPtr (*BootstrapNativeFunction)(Thread* thread, static ObjectPtr DN_Helper##name(Isolate* isolate, Thread* thread, \ Zone* zone, NativeArguments* arguments) +#define DEFINE_FFI_NATIVE_ENTRY(name, return_type, argument_types) \ + return_type BootstrapNatives::FN_##name argument_types + // Helpers that throw an argument exception. void DartNativeThrowTypeArgumentCountException(int num_type_args, int num_type_args_expected); diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index a81a147effc..db1c8a16ae7 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -45,6 +45,7 @@ #include "vm/kernel_binary.h" #include "vm/kernel_isolate.h" #include "vm/kernel_loader.h" +#include "vm/log.h" #include "vm/native_symbol.h" #include "vm/object_graph.h" #include "vm/object_store.h" @@ -2344,6 +2345,10 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group, pending_classes.Add(cls); RegisterClass(cls, Symbols::FfiDynamicLibrary(), lib); + cls = Class::New(isolate_group); + object_store->set_native_finalizer_class(cls); + RegisterPrivateClass(cls, Symbols::_NativeFinalizer(), lib); + cls = Class::New(isolate_group); cls.set_type_arguments_field_offset( Finalizer::type_arguments_offset(), @@ -2539,6 +2544,8 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group, object_store->set_weak_reference_class(cls); cls = Class::New(isolate_group); object_store->set_finalizer_class(cls); + cls = Class::New(isolate_group); + object_store->set_native_finalizer_class(cls); cls = Class::New(isolate_group); object_store->set_finalizer_entry_class(cls); @@ -2992,6 +2999,11 @@ void Class::set_has_pragma(bool value) const { set_state_bits(HasPragmaBit::update(value, state_bits())); } +void Class::set_implements_finalizable(bool value) const { + ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter()); + set_state_bits(ImplementsFinalizableBit::update(value, state_bits())); +} + // Initialize class fields of type Array with empty array. void Class::InitEmptyFields() { if (Object::empty_array().ptr() == Array::null()) { @@ -8078,6 +8090,38 @@ void Function::SetIsOptimizable(bool value) const { } } +bool Function::ForceOptimize() const { + return IsFfiFromAddress() || IsFfiGetAddress() || IsFfiLoad() || + IsFfiStore() || IsFfiTrampoline() || IsFfiAsExternalTypedData() || + IsTypedDataViewFactory() || IsUtf8Scan() || IsGetNativeField() || + IsFinalizerForceOptimized(); +} + +bool Function::IsFinalizerForceOptimized() const { + // Either because of unboxed/untagged data, or because we don't want the GC + // to trigger in between. + switch (recognized_kind()) { + case MethodRecognizer::kFinalizerBase_getIsolateFinalizers: + case MethodRecognizer::kFinalizerBase_setIsolate: + case MethodRecognizer::kFinalizerBase_setIsolateFinalizers: + case MethodRecognizer::kFinalizerEntry_getExternalSize: + // Unboxed/untagged representation not supported in unoptimized. + return true; + case MethodRecognizer::kFinalizerBase_exchangeEntriesCollectedWithNull: + // Prevent the GC from running so that the operation is atomic from + // a GC point of view. Always double check implementation in + // kernel_to_il.cc that no GC can happen in between the relevant IL + // instructions. + // TODO(https://dartbug.com/48527): Support inlining. + return true; + case MethodRecognizer::kFinalizerEntry_allocate: + // Both of the above reasons. + return true; + default: + return false; + } +} + #if !defined(DART_PRECOMPILED_RUNTIME) bool Function::CanBeInlined() const { // Our force-optimized functions cannot deoptimize to an unoptimized frame. @@ -26044,6 +26088,10 @@ const char* FinalizerBase::ToCString() const { FinalizerPtr Finalizer::New(Heap::Space space) { ASSERT(IsolateGroup::Current()->object_store()->finalizer_class() != Class::null()); + ASSERT( + Class::Handle(IsolateGroup::Current()->object_store()->finalizer_class()) + .EnsureIsAllocateFinalized(Thread::Current()) == Error::null()); + ObjectPtr raw = Object::Allocate(Finalizer::kClassId, Finalizer::InstanceSize(), space, Finalizer::ContainsCompressedPointers()); @@ -26057,13 +26105,84 @@ const char* Finalizer::ToCString() const { type_args_name.ToCString()); } -FinalizerEntryPtr FinalizerEntry::New(Heap::Space space) { +NativeFinalizerPtr NativeFinalizer::New(Heap::Space space) { + ASSERT(IsolateGroup::Current()->object_store()->native_finalizer_class() != + Class::null()); + ASSERT(Class::Handle( + IsolateGroup::Current()->object_store()->native_finalizer_class()) + .EnsureIsAllocateFinalized(Thread::Current()) == Error::null()); + ObjectPtr raw = Object::Allocate( + NativeFinalizer::kClassId, NativeFinalizer::InstanceSize(), space, + NativeFinalizer::ContainsCompressedPointers()); + return static_cast(raw); +} + +// Runs the finalizer if not detached, detaches the value and set external size +// to 0. +// TODO(http://dartbug.com/47777): Can this be merged with +// RunNativeFinalizerCallback? +void NativeFinalizer::RunCallback(const FinalizerEntry& entry, + const char* trace_context) const { + Thread* const thread = Thread::Current(); + Zone* const zone = thread->zone(); + IsolateGroup* const group = thread->isolate_group(); + const intptr_t external_size = entry.external_size(); + const auto& token_object = Object::Handle(zone, entry.token()); + const auto& callback_pointer = Pointer::Handle(zone, this->callback()); + const auto callback = reinterpret_cast( + callback_pointer.NativeAddress()); + if (token_object.IsFinalizerEntry()) { + // Detached from Dart code. + ASSERT(token_object.ptr() == entry.ptr()); + ASSERT(external_size == 0); + if (FLAG_trace_finalizers) { + THR_Print( + "%s: Not running native finalizer %p callback %p, " + "detached\n", + trace_context, ptr()->untag(), callback); + } + } else { + const auto& token = Pointer::Cast(token_object); + void* peer = reinterpret_cast(token.NativeAddress()); + if (FLAG_trace_finalizers) { + THR_Print( + "%s: Running native finalizer %p callback %p " + "with token %p\n", + trace_context, ptr()->untag(), callback, peer); + } + entry.set_token(entry); + callback(peer); + if (external_size > 0) { + ASSERT(!entry.value()->IsSmi()); + Heap::Space space = + entry.value()->IsOldObject() ? Heap::kOld : Heap::kNew; + if (FLAG_trace_finalizers) { + THR_Print("%s: Clearing external size %" Pd " bytes in %s space\n", + trace_context, external_size, space == 0 ? "new" : "old"); + } + group->heap()->FreedExternal(external_size, space); + entry.set_external_size(0); + } + } +} + +const char* NativeFinalizer::ToCString() const { + const auto& pointer = Pointer::Handle(callback()); + return OS::SCreate(Thread::Current()->zone(), "_NativeFinalizer %s", + pointer.ToCString()); +} + +FinalizerEntryPtr FinalizerEntry::New(const FinalizerBase& finalizer, + Heap::Space space) { ASSERT(IsolateGroup::Current()->object_store()->finalizer_entry_class() != Class::null()); - ObjectPtr raw = + auto& entry = FinalizerEntry::Handle(); + entry ^= Object::Allocate(FinalizerEntry::kClassId, FinalizerEntry::InstanceSize(), space, FinalizerEntry::ContainsCompressedPointers()); - return static_cast(raw); + entry.set_external_size(0); + entry.set_finalizer(finalizer); + return entry.ptr(); } void FinalizerEntry::set_finalizer(const FinalizerBase& value) const { diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 34b5b78b63c..008767bd4df 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -1608,6 +1608,10 @@ class Class : public Object { static uint16_t NumNativeFieldsOf(ClassPtr clazz) { return clazz->untag()->num_native_fields_; } + static bool ImplementsFinalizable(ClassPtr clazz) { + ASSERT(Class::Handle(clazz).is_type_finalized()); + return ImplementsFinalizableBit::decode(clazz->untag()->state_bits_); + } #if !defined(DART_PRECOMPILED_RUNTIME) CodePtr allocation_stub() const { return untag()->allocation_stub(); } @@ -1831,6 +1835,7 @@ class Class : public Object { kIsAllocatedBit, kIsLoadedBit, kHasPragmaBit, + kImplementsFinalizableBit, }; class ConstBit : public BitField {}; class ImplementedBit : public BitField {}; @@ -1853,6 +1858,8 @@ class Class : public Object { class IsAllocatedBit : public BitField {}; class IsLoadedBit : public BitField {}; class HasPragmaBit : public BitField {}; + class ImplementsFinalizableBit + : public BitField {}; void set_name(const String& value) const; void set_user_name(const String& value) const; @@ -1891,6 +1898,12 @@ class Class : public Object { bool has_pragma() const { return HasPragmaBit::decode(state_bits()); } void set_has_pragma(bool has_pragma) const; + bool implements_finalizable() const { + ASSERT(is_type_finalized()); + return ImplementsFinalizable(ptr()); + } + void set_implements_finalizable(bool value) const; + private: void set_functions(const Array& value) const; void set_fields(const Array& value) const; @@ -3185,33 +3198,9 @@ class Function : public Object { // deoptimize, since we won't generate deoptimization info or register // dependencies. It will be compiled into optimized code immediately when it's // run. - bool ForceOptimize() const { - return IsFfiFromAddress() || IsFfiGetAddress() || IsFfiLoad() || - IsFfiStore() || IsFfiTrampoline() || IsFfiAsExternalTypedData() || - IsTypedDataViewFactory() || IsUtf8Scan() || IsGetNativeField() || - IsFinalizerForceOptimized(); - } + bool ForceOptimize() const; - bool IsFinalizerForceOptimized() const { - // Either because of unboxed/untagged data, or because we don't want the GC - // to trigger in between. - switch (recognized_kind()) { - case MethodRecognizer::kFinalizerBase_getIsolateFinalizers: - case MethodRecognizer::kFinalizerBase_setIsolate: - case MethodRecognizer::kFinalizerBase_setIsolateFinalizers: - // Unboxed/untagged representation not supported in unoptimized. - return true; - case MethodRecognizer::kFinalizerBase_exchangeEntriesCollectedWithNull: - // Prevent the GC from running so that the operation is atomic from - // a GC point of view. Always double check implementation in - // kernel_to_il.cc that no GC can happen in between the relevant IL - // instructions. - // TODO(https://dartbug.com/48527): Support inlining. - return true; - default: - return false; - } - } + bool IsFinalizerForceOptimized() const; bool CanBeInlined() const; @@ -4743,6 +4732,7 @@ class Library : public Object { void SetName(const String& name) const; StringPtr url() const { return untag()->url(); } + static StringPtr UrlOf(LibraryPtr lib) { return lib->untag()->url(); } StringPtr private_key() const { return untag()->private_key(); } bool LoadNotStarted() const { return untag()->load_state_ == UntaggedLibrary::kAllocated; @@ -5768,7 +5758,7 @@ class PcDescriptors : public Object { // pc descriptors table to visit objects if any in the table. // Note: never return a reference to a UntaggedPcDescriptors::PcDescriptorRec // as the object can move. - class Iterator : ValueObject { + class Iterator : public ValueObject { public: Iterator(const PcDescriptors& descriptors, intptr_t kind_mask) : descriptors_(descriptors), @@ -9754,7 +9744,7 @@ class String : public Instance { }; // Synchronize with implementation in compiler (intrinsifier). -class StringHasher : ValueObject { +class StringHasher : public ValueObject { public: StringHasher() : hash_(0) {} void Add(uint16_t code_unit) { hash_ = CombineHashes(hash_, code_unit); } @@ -11326,7 +11316,7 @@ class LinkedHashMap : public LinkedHashBase { // - There are no checks for concurrent modifications. // - Accessing a key or value before the first call to MoveNext and after // MoveNext returns false will result in crashes. - class Iterator : ValueObject { + class Iterator : public ValueObject { public: explicit Iterator(const LinkedHashMap& map) : data_(Array::Handle(map.data())), @@ -11422,7 +11412,7 @@ class LinkedHashSet : public LinkedHashBase { // - There are no checks for concurrent modifications. // - Accessing a key or value before the first call to MoveNext and after // MoveNext returns false will result in crashes. - class Iterator : ValueObject { + class Iterator : public ValueObject { public: explicit Iterator(const LinkedHashSet& set) : data_(Array::Handle(set.data())), @@ -12048,6 +12038,7 @@ class WeakReference : public Instance { friend class Class; }; +class FinalizerBase; class FinalizerEntry : public Instance { public: ObjectPtr value() const { return untag()->value(); } @@ -12084,11 +12075,31 @@ class FinalizerEntry : public Instance { return OFFSET_OF(UntaggedFinalizerEntry, next_); } + intptr_t external_size() const { return untag()->external_size(); } + void set_external_size(intptr_t value) const { + untag()->set_external_size(value); + } + static intptr_t external_size_offset() { + return OFFSET_OF(UntaggedFinalizerEntry, external_size_); + } + static intptr_t InstanceSize() { return RoundedAllocationSize(sizeof(UntaggedFinalizerEntry)); } - static FinalizerEntryPtr New(Heap::Space space = Heap::kNew); + // Allocates a new FinalizerEntry, initializing the external size (to 0) and + // finalizer. + // + // Should only be used for object tests. + // + // Does not initialize `value`, `token`, and `detach` to allow for flexible + // testing code setting those manually. + // + // Does _not_ add the entry to the finalizer. We could add the entry to + // finalizer.all_entries.data, but we have no way of initializing the hashset + // index. + static FinalizerEntryPtr New(const FinalizerBase& finalizer, + Heap::Space space = Heap::kNew); private: FINAL_HEAP_OBJECT_IMPLEMENTATION(FinalizerEntry, Instance); @@ -12108,6 +12119,9 @@ class FinalizerBase : public Instance { } LinkedHashSetPtr all_entries() const { return untag()->all_entries(); } + void set_all_entries(const LinkedHashSet& value) const { + untag()->set_all_entries(value.ptr()); + } static intptr_t all_entries_offset() { return OFFSET_OF(UntaggedFinalizerBase, all_entries_); } @@ -12149,6 +12163,32 @@ class Finalizer : public FinalizerBase { friend class Class; }; +class NativeFinalizer : public FinalizerBase { + public: + typedef void (*Callback)(void*); + + PointerPtr callback() const { return untag()->callback(); } + void set_callback(const Pointer& value) const { + untag()->set_callback(value.ptr()); + } + static intptr_t callback_offset() { + return OFFSET_OF(UntaggedNativeFinalizer, callback_); + } + + static intptr_t InstanceSize() { + return RoundedAllocationSize(sizeof(UntaggedNativeFinalizer)); + } + + static NativeFinalizerPtr New(Heap::Space space = Heap::kNew); + + void RunCallback(const FinalizerEntry& entry, + const char* trace_context) const; + + private: + FINAL_HEAP_OBJECT_IMPLEMENTATION(NativeFinalizer, FinalizerBase); + friend class Class; +}; + class MirrorReference : public Instance { public: ObjectPtr referent() const { return untag()->referent(); } diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc index ede344dac69..66ebd7911c2 100644 --- a/runtime/vm/object_graph_copy.cc +++ b/runtime/vm/object_graph_copy.cc @@ -38,6 +38,7 @@ V(Finalizer) \ V(FinalizerBase) \ V(FinalizerEntry) \ + V(NativeFinalizer) \ V(Function) \ V(FunctionType) \ V(FutureOr) \ @@ -588,13 +589,23 @@ class ObjectCopyBase { Class::Handle(class_table_->At(cid)).ToCString()); return false; } + const bool implements_finalizable = + Class::ImplementsFinalizable(class_table_->At(cid)); + if (implements_finalizable) { + exception_msg_ = OS::SCreate( + zone_, + "Illegal argument in isolate message: (object implements " + "Finalizable - %s)", + Class::Handle(class_table_->At(cid)).ToCString()); + return false; + } return true; } #define HANDLE_ILLEGAL_CASE(Type) \ case k##Type##Cid: { \ exception_msg_ = \ "Illegal argument in isolate message: " \ - "(object is a" #Type ")"; \ + "(object is a " #Type ")"; \ return false; \ } @@ -604,6 +615,7 @@ class ObjectCopyBase { // here that cannot happen in reality) HANDLE_ILLEGAL_CASE(DynamicLibrary) HANDLE_ILLEGAL_CASE(Finalizer) + HANDLE_ILLEGAL_CASE(NativeFinalizer) HANDLE_ILLEGAL_CASE(MirrorReference) HANDLE_ILLEGAL_CASE(Pointer) HANDLE_ILLEGAL_CASE(ReceivePort) diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc index 59245d6d023..eab90785d31 100644 --- a/runtime/vm/object_service.cc +++ b/runtime/vm/object_service.cc @@ -1719,6 +1719,21 @@ void Finalizer::PrintJSONImpl(JSONStream* stream, bool ref) const { // Not exposing entries. } +void NativeFinalizer::PrintJSONImpl(JSONStream* stream, bool ref) const { + JSONObject jsobj(stream); + PrintSharedInstanceJSON(&jsobj, ref); + jsobj.AddProperty("kind", "NativeFinalizer"); + jsobj.AddServiceId(*this); + if (ref) { + return; + } + + const Object& finalizer_callback = Object::Handle(callback()); + jsobj.AddProperty("callback_address", finalizer_callback); + + // Not exposing entries. +} + void FinalizerEntry::PrintJSONImpl(JSONStream* stream, bool ref) const { UNREACHABLE(); } diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc index bdbc4a1a9ff..056d9e67276 100644 --- a/runtime/vm/object_store.cc +++ b/runtime/vm/object_store.cc @@ -486,6 +486,15 @@ void ObjectStore::LazyInitFfiMembers() { cls.LookupFunctionAllowPrivate(Symbols::_handleFinalizerMessage()); ASSERT(!function.IsNull()); handle_finalizer_message_function_.store(function.ptr()); + + cls = native_finalizer_class(); + ASSERT(!cls.IsNull()); + error = cls.EnsureIsFinalized(thread); + ASSERT(error.IsNull()); + function = cls.LookupFunctionAllowPrivate( + Symbols::_handleNativeFinalizerMessage()); + ASSERT(!function.IsNull()); + handle_native_finalizer_message_function_.store(function.ptr()); } } diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h index 26bce631497..b9ba248ff2c 100644 --- a/runtime/vm/object_store.h +++ b/runtime/vm/object_store.h @@ -56,6 +56,7 @@ class ObjectPointerVisitor; LAZY_INTERNAL(Class, symbol_class) \ LAZY_INTERNAL(Field, symbol_name_field) \ LAZY_FFI(Function, handle_finalizer_message_function) \ + LAZY_FFI(Function, handle_native_finalizer_message_function) \ LAZY_ASYNC(Type, non_nullable_future_rare_type) \ LAZY_ASYNC(Type, non_nullable_future_never_type) \ LAZY_ASYNC(Type, nullable_future_null_type) \ @@ -129,7 +130,7 @@ class ObjectPointerVisitor; RW(Class, weak_reference_class) \ RW(Class, finalizer_class) \ RW(Class, finalizer_entry_class) \ - RW(Class, finalizer_native_class) \ + RW(Class, native_finalizer_class) \ ARW_AR(Array, symbol_table) \ RW(Array, canonical_types) \ RW(Array, canonical_function_types) \ diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc index 888041a1e23..11bda3c7553 100644 --- a/runtime/vm/object_test.cc +++ b/runtime/vm/object_test.cc @@ -31,6 +31,7 @@ #include "vm/resolver.h" #include "vm/simulator.h" #include "vm/symbols.h" +#include "vm/tagged_pointer.h" #include "vm/unit_test.h" namespace dart { @@ -4031,8 +4032,8 @@ static void Finalizer_PreserveOne(Thread* thread, const auto& finalizer = Finalizer::Handle(Finalizer::New(space)); finalizer.set_isolate(thread->isolate()); - const auto& entry = FinalizerEntry::Handle(FinalizerEntry::New(space)); - entry.set_finalizer(finalizer); + const auto& entry = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, space)); const auto& value = String::Handle(OneByteString::New("value", space)); entry.set_value(value); auto& detach = Object::Handle(); @@ -4097,8 +4098,8 @@ static void Finalizer_ClearDetachOne(Thread* thread, Heap::Space space) { const auto& finalizer = Finalizer::Handle(Finalizer::New(space)); finalizer.set_isolate(thread->isolate()); - const auto& entry = FinalizerEntry::Handle(FinalizerEntry::New(space)); - entry.set_finalizer(finalizer); + const auto& entry = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, space)); const auto& value = String::Handle(OneByteString::New("value", space)); entry.set_value(value); const auto& token = String::Handle(OneByteString::New("token", space)); @@ -4156,8 +4157,8 @@ static void Finalizer_ClearValueOne(Thread* thread, const auto& finalizer = Finalizer::Handle(Finalizer::New(space)); finalizer.set_isolate(thread->isolate()); - const auto& entry = FinalizerEntry::Handle(FinalizerEntry::New(space)); - entry.set_finalizer(finalizer); + const auto& entry = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, space)); const auto& detach = String::Handle(OneByteString::New("detach", space)); auto& token = Object::Handle(); if (null_token) { @@ -4228,8 +4229,8 @@ static void Finalizer_DetachOne(Thread* thread, const auto& finalizer = Finalizer::Handle(Finalizer::New(space)); finalizer.set_isolate(thread->isolate()); - const auto& entry = FinalizerEntry::Handle(FinalizerEntry::New(space)); - entry.set_finalizer(finalizer); + const auto& entry = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, space)); const auto& detach = String::Handle(OneByteString::New("detach", space)); entry.set_detach(detach); @@ -4303,8 +4304,8 @@ static void Finalizer_GcFinalizer(Thread* thread, Heap::Space space) { HANDLESCOPE(thread); const auto& finalizer = Finalizer::Handle(Finalizer::New(space)); finalizer.set_isolate(thread->isolate()); - const auto& entry = FinalizerEntry::Handle(FinalizerEntry::New(space)); - entry.set_finalizer(finalizer); + const auto& entry = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, space)); entry.set_detach(detach); entry.set_token(token); const auto& value = String::Handle(OneByteString::New("value", space)); @@ -4358,10 +4359,10 @@ static void Finalizer_TwoEntriesCrossGen( const auto& finalizer = Finalizer::Handle(Finalizer::New(spaces[0])); finalizer.set_isolate(thread->isolate()); - const auto& entry1 = FinalizerEntry::Handle(FinalizerEntry::New(spaces[1])); - entry1.set_finalizer(finalizer); - const auto& entry2 = FinalizerEntry::Handle(FinalizerEntry::New(spaces[2])); - entry2.set_finalizer(finalizer); + const auto& entry1 = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, spaces[1])); + const auto& entry2 = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, spaces[2])); auto& value1 = String::Handle(); auto& detach1 = String::Handle(); @@ -5040,6 +5041,241 @@ static void Finalizer_TwoEntriesCrossGen(Thread* thread, intptr_t test_i) { REPEAT_512(FINALIZER_CROSS_GEN_TEST_CASE) +#undef FINALIZER_CROSS_GEN_TEST_CASE + +void NativeFinalizer_TwoEntriesCrossGen_Finalizer(intptr_t* token) { + (*token)++; +} + +static void NativeFinalizer_TwoEntriesCrossGen( + Thread* thread, + Heap::Space* spaces, + bool collect_new_space, + bool evacuate_new_space_and_collect_old_space, + bool clear_value_1, + bool clear_value_2, + bool clear_detach_1, + bool clear_detach_2) { +#ifdef DEBUG + SetFlagScope sfs(&FLAG_trace_finalizers, true); +#endif + + intptr_t token1_memory = 0; + intptr_t token2_memory = 0; + + MessageHandler* handler = thread->isolate()->message_handler(); + // We're reusing the isolate in a loop, so there are messages from previous + // runs of this test. + intptr_t queue_length_start = 0; + { + MessageHandler::AcquiredQueues aq(handler); + queue_length_start = aq.queue()->Length(); + } + + ObjectStore* object_store = thread->isolate_group()->object_store(); + const auto& void_type = Type::Handle(object_store->never_type()); + const auto& callback = Pointer::Handle(Pointer::New( + void_type, + reinterpret_cast(&NativeFinalizer_TwoEntriesCrossGen_Finalizer), + spaces[3])); + + const auto& finalizer = + NativeFinalizer::Handle(NativeFinalizer::New(spaces[0])); + finalizer.set_callback(callback); + finalizer.set_isolate(thread->isolate()); + + const auto& isolate_finalizers = + GrowableObjectArray::Handle(GrowableObjectArray::New()); + const auto& weak1 = WeakReference::Handle(WeakReference::New()); + weak1.set_target(finalizer); + isolate_finalizers.Add(weak1); + thread->isolate()->set_finalizers(isolate_finalizers); + + const auto& all_entries = LinkedHashSet::Handle(LinkedHashSet::NewDefault()); + finalizer.set_all_entries(all_entries); + const auto& all_entries_data = Array::Handle(all_entries.data()); + THR_Print("entry1 space: %s\n", spaces[1] == Heap::kNew ? "new" : "old"); + const auto& entry1 = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, spaces[1])); + all_entries_data.SetAt(0, entry1); + THR_Print("entry2 space: %s\n", spaces[2] == Heap::kNew ? "new" : "old"); + const auto& entry2 = + FinalizerEntry::Handle(FinalizerEntry::New(finalizer, spaces[2])); + all_entries_data.SetAt(1, entry2); + all_entries.set_used_data(2); // Don't bother setting the index. + + const intptr_t external_size1 = 1024; + const intptr_t external_size2 = 2048; + entry1.set_external_size(external_size1); + entry2.set_external_size(external_size2); + IsolateGroup::Current()->heap()->AllocatedExternal(external_size1, spaces[5]); + IsolateGroup::Current()->heap()->AllocatedExternal(external_size2, spaces[7]); + + auto& value1 = String::Handle(); + auto& detach1 = String::Handle(); + const auto& token1 = Pointer::Handle(Pointer::New( + void_type, reinterpret_cast(&token1_memory), spaces[3])); + entry1.set_token(token1); + + auto& value2 = String::Handle(); + auto& detach2 = String::Handle(); + const auto& token2 = Pointer::Handle(Pointer::New( + void_type, reinterpret_cast(&token2_memory), spaces[4])); + entry2.set_token(token2); + entry2.set_detach(detach2); + + { + HANDLESCOPE(thread); + auto& object = String::Handle(); + + THR_Print("value1 space: %s\n", spaces[5] == Heap::kNew ? "new" : "old"); + object ^= OneByteString::New("value1", spaces[5]); + entry1.set_value(object); + if (!clear_value_1) { + value1 = object.ptr(); + } + + object ^= OneByteString::New("detach", spaces[6]); + entry1.set_detach(object); + if (!clear_detach_1) { + detach1 = object.ptr(); + } + + THR_Print("value2 space: %s\n", spaces[7] == Heap::kNew ? "new" : "old"); + object ^= OneByteString::New("value2", spaces[7]); + entry2.set_value(object); + if (!clear_value_2) { + value2 = object.ptr(); + } + + object ^= OneByteString::New("detach", spaces[8]); + entry2.set_detach(object); + if (!clear_detach_2) { + detach2 = object.ptr(); + } + } + + THR_Print("CollectOldSpace\n"); + GCTestHelper::CollectOldSpace(); + if (collect_new_space) { + THR_Print("CollectNewSpace\n"); + GCTestHelper::CollectNewSpace(); + } + if (evacuate_new_space_and_collect_old_space) { + THR_Print("CollectAllGarbage\n"); + GCTestHelper::CollectAllGarbage(); + } + + EXPECT((entry1.value() == Object::null()) ^ !clear_value_1); + EXPECT((entry2.value() == Object::null()) ^ !clear_value_2); + EXPECT((entry1.detach() == Object::null()) ^ !clear_detach_1); + EXPECT((entry2.detach() == Object::null()) ^ !clear_detach_2); + EXPECT_NE(Object::null(), entry1.token()); + EXPECT_NE(Object::null(), entry2.token()); + + const intptr_t expect_num_cleared = + (clear_value_1 ? 1 : 0) + (clear_value_2 ? 1 : 0); + EXPECT_EQ(expect_num_cleared, + NumEntries(FinalizerEntry::Handle(finalizer.entries_collected()))); + + EXPECT_EQ(clear_value_1 ? 1 : 0, token1_memory); + EXPECT_EQ(clear_value_2 ? 1 : 0, token2_memory); + + const intptr_t expect_num_messages = expect_num_cleared == 0 ? 0 : 1; + { + // Acquire ownership of message handler queues. + MessageHandler::AcquiredQueues aq(handler); + EXPECT_EQ(expect_num_messages + queue_length_start, aq.queue()->Length()); + } + + // Simulate detachments. + entry1.set_token(entry1); + entry2.set_token(entry2); + all_entries_data.SetAt(0, Object::Handle(Object::null())); + all_entries_data.SetAt(1, Object::Handle(Object::null())); + all_entries.set_used_data(0); +} + +static void NativeFinalizer_TwoEntriesCrossGen(Thread* thread, + intptr_t test_i) { + ASSERT(test_i < (1 << kFinalizerTwoEntriesNumObjects)); + Heap::Space spaces[kFinalizerTwoEntriesNumObjects]; + for (intptr_t i = 0; i < kFinalizerTwoEntriesNumObjects; i++) { + spaces[i] = ((test_i >> i) & 0x1) == 0x1 ? Heap::kOld : Heap::kNew; + } + // Either collect or evacuate new space. + for (const bool collect_new_space : {true, false}) { + // Always run old space collection after new space. + const bool evacuate_new_space_and_collect_old_space = true; + const bool clear_value_1 = true; + const bool clear_value_2 = true; + const bool clear_detach_1 = false; + const bool clear_detach_2 = false; + THR_Print( + "collect_new_space: %s evacuate_new_space_and_collect_old_space: %s\n", + collect_new_space ? "true" : "false", + evacuate_new_space_and_collect_old_space ? "true" : "false"); + NativeFinalizer_TwoEntriesCrossGen(thread, spaces, collect_new_space, + evacuate_new_space_and_collect_old_space, + clear_value_1, clear_value_2, + clear_detach_1, clear_detach_2); + } +} + +#define FINALIZER_NATIVE_CROSS_GEN_TEST_CASE(n) \ + ISOLATE_UNIT_TEST_CASE(NativeFinalizer_CrossGen_##n) { \ + NativeFinalizer_TwoEntriesCrossGen(thread, n); \ + } + +REPEAT_512(FINALIZER_NATIVE_CROSS_GEN_TEST_CASE) + +#undef FINALIZER_NATIVE_CROSS_GEN_TEST_CASE + +#undef REPEAT_512 + +static ClassPtr GetClass(const Library& lib, const char* name) { + const Class& cls = Class::Handle( + lib.LookupClass(String::Handle(Symbols::New(Thread::Current(), name)))); + EXPECT(!cls.IsNull()); // No ambiguity error expected. + return cls.ptr(); +} + +TEST_CASE(ImplementsFinalizable) { + Zone* const zone = Thread::Current()->zone(); + + const char* kScript = R"( +import 'dart:ffi'; + +class AImpl implements A {} +class ASub extends A {} +// Wonky class order and non-alhpabetic naming on purpose. +class C extends Z {} +class E extends D {} +class A implements Finalizable {} +class Z implements A {} +class D implements C {} +class X extends E {} +)"; + Dart_Handle h_lib = TestCase::LoadTestScript(kScript, nullptr); + EXPECT_VALID(h_lib); + + TransitionNativeToVM transition(thread); + const Library& lib = Library::CheckedHandle(zone, Api::UnwrapHandle(h_lib)); + EXPECT(!lib.IsNull()); + + const auto& class_x = Class::Handle(zone, GetClass(lib, "X")); + ClassFinalizer::FinalizeTypesInClass(class_x); + EXPECT(class_x.implements_finalizable()); + + const auto& class_a_impl = Class::Handle(zone, GetClass(lib, "AImpl")); + ClassFinalizer::FinalizeTypesInClass(class_a_impl); + EXPECT(class_a_impl.implements_finalizable()); + + const auto& class_a_sub = Class::Handle(zone, GetClass(lib, "ASub")); + ClassFinalizer::FinalizeTypesInClass(class_a_sub); + EXPECT(class_a_sub.implements_finalizable()); +} + ISOLATE_UNIT_TEST_CASE(MirrorReference) { const MirrorReference& reference = MirrorReference::Handle(MirrorReference::New(Object::Handle())); @@ -5098,13 +5334,6 @@ static FieldPtr GetField(const Class& cls, const char* name) { return field.ptr(); } -static ClassPtr GetClass(const Library& lib, const char* name) { - const Class& cls = Class::Handle( - lib.LookupClass(String::Handle(Symbols::New(Thread::Current(), name)))); - EXPECT(!cls.IsNull()); // No ambiguity error expected. - return cls.ptr(); -} - ISOLATE_UNIT_TEST_CASE(FindClosureIndex) { // Allocate the class first. const String& class_name = String::Handle(Symbols::New(thread, "MyClass")); diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc index 82e1bf8892a..80c10084415 100644 --- a/runtime/vm/raw_object.cc +++ b/runtime/vm/raw_object.cc @@ -554,6 +554,7 @@ COMPRESSED_VISITOR(WeakProperty) COMPRESSED_VISITOR(WeakReference) COMPRESSED_VISITOR(Finalizer) COMPRESSED_VISITOR(FinalizerEntry) +COMPRESSED_VISITOR(NativeFinalizer) COMPRESSED_VISITOR(MirrorReference) COMPRESSED_VISITOR(UserTag) REGULAR_VISITOR(SubtypeTestCache) diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index c21d64a6852..89dae699b00 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -2846,6 +2846,9 @@ class UntaggedTwoByteString : public UntaggedString { // TypedData extends this with a length field, while Pointer extends this with // TypeArguments field. class UntaggedPointerBase : public UntaggedInstance { + public: + uint8_t* data() { return data_; } + protected: // The contents of [data_] depends on what concrete subclass is used: // @@ -3391,7 +3394,26 @@ class UntaggedFinalizer : public UntaggedFinalizerBase { friend class ScavengerVisitorBase; }; +class UntaggedNativeFinalizer : public UntaggedFinalizerBase { + RAW_HEAP_OBJECT_IMPLEMENTATION(NativeFinalizer); + + COMPRESSED_POINTER_FIELD(PointerPtr, callback) + VISIT_TO(callback) + + friend class GCMarker; + template + friend class MarkingVisitorBase; + friend class Scavenger; + template + friend class ScavengerVisitorBase; +}; + class UntaggedFinalizerEntry : public UntaggedInstance { + public: + intptr_t external_size() { return external_size_; } + void set_external_size(intptr_t value) { external_size_ = value; } + + private: RAW_HEAP_OBJECT_IMPLEMENTATION(FinalizerEntry); COMPRESSED_POINTER_FIELD(ObjectPtr, value) // Weak reference. @@ -3409,6 +3431,8 @@ class UntaggedFinalizerEntry : public UntaggedInstance { // Only populated during the GC, otherwise null. COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, next_seen_by_gc) + intptr_t external_size_; + template friend class GCLinkedList; template @@ -3419,6 +3443,7 @@ class UntaggedFinalizerEntry : public UntaggedInstance { friend class Scavenger; template friend class ScavengerVisitorBase; + friend class ScavengerFinalizerVisitor; }; // MirrorReferences are used by mirrors to hold reflectees that are VM diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h index 7953a1fc47b..ba78ad7aa3a 100644 --- a/runtime/vm/symbols.h +++ b/runtime/vm/symbols.h @@ -137,6 +137,7 @@ class ObjectPointerVisitor; V(FfiVoid, "Void") \ V(FfiHandle, "Handle") \ V(Field, "Field") \ + V(Finalizable, "Finalizable") \ V(FinalizerBase, "FinalizerBase") \ V(FinalizerEntry, "FinalizerEntry") \ V(FinallyRetVal, ":finally_ret_val") \ @@ -311,6 +312,7 @@ class ObjectPointerVisitor; V(_ExternalUint8Array, "_ExternalUint8Array") \ V(_ExternalUint8ClampedArray, "_ExternalUint8ClampedArray") \ V(_FinalizerImpl, "_FinalizerImpl") \ + V(_NativeFinalizer, "_NativeFinalizer") \ V(_Float32ArrayFactory, "Float32List.") \ V(_Float32ArrayView, "_Float32ArrayView") \ V(_Float32List, "_Float32List") \ @@ -411,6 +413,7 @@ class ObjectPointerVisitor; V(_future, "_future") \ V(_handleMessage, "_handleMessage") \ V(_handleFinalizerMessage, "_handleFinalizerMessage") \ + V(_handleNativeFinalizerMessage, "_handleNativeFinalizerMessage") \ V(_instanceOf, "_instanceOf") \ V(_listGetAt, "_listGetAt") \ V(_listLength, "_listLength") \ diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h index 5a0b99f20e8..09968d4e7fd 100644 --- a/runtime/vm/tagged_pointer.h +++ b/runtime/vm/tagged_pointer.h @@ -416,6 +416,7 @@ DEFINE_TAGGED_POINTER(WeakReference, Instance) DEFINE_TAGGED_POINTER(FinalizerBase, Instance) DEFINE_TAGGED_POINTER(Finalizer, Instance) DEFINE_TAGGED_POINTER(FinalizerEntry, Instance) +DEFINE_TAGGED_POINTER(NativeFinalizer, Instance) DEFINE_TAGGED_POINTER(MirrorReference, Instance) DEFINE_TAGGED_POINTER(UserTag, Instance) DEFINE_TAGGED_POINTER(FutureOr, Instance) diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc index c897d45dc62..fe904d00e9c 100644 --- a/runtime/vm/type_testing_stubs_test.cc +++ b/runtime/vm/type_testing_stubs_test.cc @@ -2303,7 +2303,7 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial_Reload) { // during the checks that the instantiation of Y is int. ISOLATE_UNIT_TEST_CASE(TTS_Regress_CidRangeChecks) { // Bump this appropriately if the EXPECT_EQ below fails. - const intptr_t kNumUnrelated = 1183; + const intptr_t kNumUnrelated = 1185; TextBuffer buffer(1024); buffer.AddString(R"( abstract class B {} diff --git a/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart b/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart index 0edd45ce603..791bd90c40e 100644 --- a/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart @@ -4,9 +4,9 @@ // All imports must be in all FFI patch files to not depend on the order // the patches are applied. -import "dart:_internal" show patch; -import 'dart:typed_data'; +import 'dart:_internal'; import 'dart:isolate'; +import 'dart:typed_data'; extension AllocatorAlloc on Allocator { // TODO(http://dartbug.com/39964): Add `alignmentOf()` call. diff --git a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart index b9bcaf0fba5..83a6a8a795b 100644 --- a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart @@ -4,9 +4,9 @@ // All imports must be in all FFI patch files to not depend on the order // the patches are applied. -import "dart:_internal" show patch; -import 'dart:typed_data'; +import 'dart:_internal'; import 'dart:isolate'; +import 'dart:typed_data'; @pragma("vm:external-name", "Ffi_dl_open") external DynamicLibrary _open(String path); diff --git a/sdk/lib/_internal/vm/lib/ffi_native_finalizer_patch.dart b/sdk/lib/_internal/vm/lib/ffi_native_finalizer_patch.dart index e6972308770..7c7d38deaf6 100644 --- a/sdk/lib/_internal/vm/lib/ffi_native_finalizer_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_native_finalizer_patch.dart @@ -8,5 +8,96 @@ import 'dart:_internal'; import 'dart:isolate'; import 'dart:typed_data'; -// This is a placeholder file which will shortly contain a NativeFinalizer -// implementation. +@patch +@pragma("vm:entry-point") +abstract class Finalizable {} + +@patch +@pragma("vm:entry-point") +abstract class NativeFinalizer { + @patch + factory NativeFinalizer(Pointer callback) = + _NativeFinalizer; +} + +@pragma("vm:entry-point") +class _NativeFinalizer extends FinalizerBase implements NativeFinalizer { + @pragma("vm:recognized", "other") + @pragma("vm:prefer-inline") + external Pointer get _callback; + @pragma("vm:recognized", "other") + @pragma("vm:prefer-inline") + external set _callback(Pointer value); + + _NativeFinalizer(Pointer callback) { + allEntries = {}; + _callback = callback; + setIsolate(); + isolateRegisterFinalizer(); + } + + void attach( + Finalizable value, + Pointer token, { + Object? detach, + int? externalSize, + }) { + externalSize ??= 0; + RangeError.checkNotNegative(externalSize, 'externalSize'); + if (value == null) { + throw ArgumentError.value(value, 'value', "Cannot be a null"); + } + if (detach != null) { + checkValidWeakTarget(detach, 'detach'); + } + + final entry = FinalizerEntry.allocate(value, token, detach, this); + allEntries.add(entry); + + if (externalSize > 0) { + entry.setExternalSize(externalSize); + } + + if (detach != null) { + (detachments[detach] ??= {}).add(entry); + } + + // The `value` stays reachable till here because the static type is + // `Finalizable`. + } + + @override + void detach(Object detach) { + final entries = detachments[detach]; + if (entries != null) { + for (final entry in entries) { + entry.token = entry; + final externalSize = entry.externalSize; + if (externalSize > 0) { + entry.setExternalSize(0); + assert(entry.externalSize == 0); + } + allEntries.remove(entry); + } + detachments[detach] = null; + } + } + + void _removeEntries() { + FinalizerEntry? entry = exchangeEntriesCollectedWithNull(); + while (entry != null) { + assert(entry.externalSize == 0); + allEntries.remove(entry); + final detach = entry.detach; + if (detach != null) { + detachments[detach]?.remove(entry); + } + entry = entry.next; + } + } + + @pragma("vm:entry-point", "call") + static _handleNativeFinalizerMessage(_NativeFinalizer finalizer) { + finalizer._removeEntries(); + } +} diff --git a/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart b/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart index ca47a83cba4..d62f0a0bf71 100644 --- a/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart @@ -4,9 +4,9 @@ // All imports must be in all FFI patch files to not depend on the order // the patches are applied. -import "dart:_internal" show patch; -import 'dart:typed_data'; +import 'dart:_internal'; import 'dart:isolate'; +import 'dart:typed_data'; // NativeType is not private, because it is used in type arguments. // NativeType is abstract because it not used with const constructors in diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart index 174cc69f55e..be501048a85 100644 --- a/sdk/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart @@ -4,9 +4,9 @@ // All imports must be in all FFI patch files to not depend on the order // the patches are applied. -import "dart:_internal" show patch, has63BitSmis; -import 'dart:typed_data'; +import 'dart:_internal'; import 'dart:isolate'; +import 'dart:typed_data'; const Map _knownSizes = { Int8: 1, diff --git a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart index 00279f30ea8..3c8358307fa 100644 --- a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart @@ -4,9 +4,9 @@ // All imports must be in all FFI patch files to not depend on the order // the patches are applied. -import "dart:_internal" show patch; -import 'dart:typed_data'; +import 'dart:_internal'; import 'dart:isolate'; +import 'dart:typed_data'; @pragma("vm:entry-point") abstract class _Compound extends NativeType {} diff --git a/sdk/lib/_internal/vm/lib/finalizer_patch.dart b/sdk/lib/_internal/vm/lib/finalizer_patch.dart index a9db8536c6a..417344ffe64 100644 --- a/sdk/lib/_internal/vm/lib/finalizer_patch.dart +++ b/sdk/lib/_internal/vm/lib/finalizer_patch.dart @@ -43,15 +43,7 @@ class _FinalizerImpl extends FinalizerBase implements Finalizer { checkValidWeakTarget(detach, 'detach'); } - // Initializing the entry in a non-atomic way should be fine. - // The only interesting step in the GC is when value is collected. - // If the entry gets processed before initializing value, it will be null, - // and this is fine. We will not consider it as being collected that GC. - final entry = FinalizerEntry() - ..value = value - ..token = token - ..detach = detach - ..finalizer = this; + final entry = FinalizerEntry.allocate(value, token, detach, this); allEntries.add(entry); // Ensure value stays reachable until after having initialized the entry. // This ensures the token and finalizer are set. diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart index 547835cf5cd..c02b2f80903 100644 --- a/sdk/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk/lib/_internal/vm/lib/internal_patch.dart @@ -9,7 +9,7 @@ import "dart:async" show Timer; import "dart:core" hide Symbol; -import "dart:ffi" show Pointer, Struct, Union; +import "dart:ffi" show Pointer, Struct, Union, IntPtr, Handle, Void, FfiNative; import "dart:isolate" show SendPort; import "dart:typed_data" show Int32List, Uint8List; @@ -384,15 +384,18 @@ extension FinalizerBaseMembers on FinalizerBase { /// linked list of pending entries while running the GC. @pragma("vm:entry-point") class FinalizerEntry { + @pragma('vm:never-inline') + @pragma("vm:recognized", "other") + @pragma("vm:external-name", "FinalizerEntry_allocate") + external static FinalizerEntry allocate( + Object value, Object? token, Object? detach, FinalizerBase finalizer); + /// The [value] the [FinalizerBase] is attached to. /// /// Set to `null` by GC when unreachable. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external Object? get value; - @pragma("vm:recognized", "other") - @pragma("vm:prefer-inline") - external set value(Object? value); /// The [detach] object can be passed to [FinalizerBase] to detach /// the finalizer. @@ -401,9 +404,6 @@ class FinalizerEntry { @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external Object? get detach; - @pragma("vm:recognized", "other") - @pragma("vm:prefer-inline") - external set detach(Object? value); /// The [token] is passed to [FinalizerBase] when the finalizer is run. @pragma("vm:recognized", "other") @@ -413,13 +413,6 @@ class FinalizerEntry { @pragma("vm:prefer-inline") external set token(Object? value); - /// The [finalizer] this [FinalizerEntry] belongs to. - /// - /// Set to `null` by GC when unreachable. - @pragma("vm:recognized", "other") - @pragma("vm:prefer-inline") - external set finalizer(FinalizerBase? finalizer); - /// The [next] entry in a linked list. /// /// Used in for the linked list starting from @@ -427,7 +420,12 @@ class FinalizerEntry { @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external FinalizerEntry? get next; + @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") - external set next(FinalizerEntry? value); + external int get externalSize; + + /// Update the external size. + @FfiNative('FinalizerEntry_SetExternalSize') + external void setExternalSize(int externalSize); } diff --git a/sdk/lib/ffi/native_finalizer.dart b/sdk/lib/ffi/native_finalizer.dart index 49fc6670a03..14074cf6cae 100644 --- a/sdk/lib/ffi/native_finalizer.dart +++ b/sdk/lib/ffi/native_finalizer.dart @@ -221,3 +221,77 @@ part of dart.ffi; abstract class Finalizable { factory Finalizable._() => throw UnsupportedError(""); } + +/// The native function type for [NativeFinalizer]s. +/// +/// A [NativeFinalizer]'s `callback` should have the C +/// `void nativeFinalizer(void* token)` type. +typedef NativeFinalizerFunction + = NativeFunction token)>; + +/// A native finalizer which can be attached to Dart objects. +/// +/// When [attach]ed to a Dart object, this finalizer's native callback is called +/// after the Dart object is garbage collected or becomes inaccessible for other +/// reasons. +/// +/// Callbacks will happen as early as possible, when the object becomes +/// inaccessible to the program, and may happen at any moment during execution +/// of the program. At the latest, when an isolate group shuts down, +/// this callback is guaranteed to be called for each object in that isolate +/// group that the finalizer is still attached to. +/// +/// Compared to the [Finalizer] from `dart:core`, which makes no promises to +/// ever call an attached callback, this native finalizer promises that all +/// attached finalizers are definitely called at least once before the program +/// ends, and the callbacks are called as soon as possible after an object +/// is recognized as inaccessible. +abstract class NativeFinalizer { + /// Creates a finalizer with the given finalization callback. + /// + /// The [callback] must be a native function which can be executed outside of + /// a Dart isolate. This means that passing an FFI trampoline (a function + /// pointer obtained via [Pointer.fromFunction]) is not supported. + /// + /// The [callback] might be invoked on an arbitrary thread and not necessary + /// on the same thread that created [NativeFinalizer]. + // TODO(https://dartbug.com/47778): Implement isolate independent code and + // update the above comment. + external factory NativeFinalizer(Pointer callback); + + /// Attaches this finalizer to [value]. + /// + /// When [value] is no longer accessible to the program, + /// the finalizer will call its callback function with [token] + /// as argument. + /// + /// If a non-`null` [detach] value is provided, that object can be + /// passed to [Finalizer.detach] to remove the attachment again. + /// + /// The [value] and [detach] arguments do not count towards those + /// objects being accessible to the program. Both must be objects supported + /// as an [Expando] key. They may be the *same* object. + /// + /// Multiple objects may be using the same finalization token, + /// and the finalizer can be attached multiple times to the same object + /// with different, or the same, finalization token. + /// + /// The callback will be called exactly once per attachment, except for + /// registrations which have been detached since they were attached. + /// + /// The [externalSize] should represent the amount of native (non-Dart) memory + /// owned by the given [value]. This information is used for garbage + /// collection scheduling heuristics. + void attach(Finalizable value, Pointer token, + {Object? detach, int? externalSize}); + + /// Detaches this finalizer from values attached with [detach]. + /// + /// If this finalizer was attached multiple times to the same object with + /// different detachment keys, only those attachments which used [detach] + /// are removed. + /// + /// After detaching, an attachment won't cause any callbacks to happen if the + /// object become inaccessible. + void detach(Object detach); +} diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart index 6b2b940f56b..d1ff9fc9921 100644 --- a/sdk/lib/isolate/isolate.dart +++ b/sdk/lib/isolate/isolate.dart @@ -723,7 +723,9 @@ abstract class SendPort implements Capability { /// therefore not be sent. /// - [ReceivePort] /// - [DynamicLibrary] + /// - [Finalizable] /// - [Finalizer] + /// - [NativeFinalizer] /// - [Pointer] /// - [UserTag] /// - `MirrorReference` diff --git a/tests/ffi/ffi.status b/tests/ffi/ffi.status index e22df5b89a7..361a2520d20 100644 --- a/tests/ffi/ffi.status +++ b/tests/ffi/ffi.status @@ -20,6 +20,7 @@ regress_47594_test: Skip # Profiler is not available in Product. [ $system == android ] *: Pass, Slow # https://github.com/dart-lang/sdk/issues/38489 regress_47594_test: Skip # DartDev is not available on Android. +vmspecific_native_finalizer_isolate_groups_test: Skip # SpawnUri not available on Android tester. [ $system == windows ] regress_47594_test: Skip # DynamicLibrary.process() is not available on Windows. diff --git a/tests/ffi/ffi_test_helpers.dart b/tests/ffi/ffi_test_helpers.dart index 0c598ecae80..92dc3c7fe6d 100644 --- a/tests/ffi/ffi_test_helpers.dart +++ b/tests/ffi/ffi_test_helpers.dart @@ -4,6 +4,8 @@ // // Helpers for tests which trigger GC in delicate places. +// ignore: import_internal_library, unused_import +import 'dart:_internal'; import 'dart:ffi'; import 'dylib_utils.dart'; @@ -17,12 +19,45 @@ typedef UnaryOpVoid = void Function(int); final DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); -final triggerGc = ffiTestFunctions - .lookupFunction("TriggerGC"); - final collectOnNthAllocation = ffiTestFunctions .lookupFunction("CollectOnNthAllocation"); extension PointerOffsetBy on Pointer { Pointer offsetBy(int bytes) => Pointer.fromAddress(address + bytes); } + +/// Triggers garbage collection. +// Defined in `dart:_internal`. +// ignore: undefined_identifier +void triggerGc() => VMInternalsForTesting.collectAllGarbage(); + +void Function(String) _namedPrint(String? name) { + if (name != null) { + return (String value) => print('$name: $value'); + } + return (String value) => print(value); +} + +/// Does a GC and if [doAwait] awaits a future to enable running finalizers. +/// +/// Also prints for debug purposes. +/// +/// If provided, [name] prefixes the debug prints. +void doGC({String? name}) { + final _print = _namedPrint(name); + + _print('Do GC.'); + triggerGc(); + _print('GC done'); +} + +void createAndLoseFinalizable(Pointer token) { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token.cast()); +} + +final setTokenTo42Ptr = + ffiTestFunctions.lookup("SetArgumentTo42"); +final setTokenFinalizer = NativeFinalizer(setTokenTo42Ptr); + +class MyFinalizable implements Finalizable {} diff --git a/tests/ffi/vmspecific_native_finalizer_2_test.dart b/tests/ffi/vmspecific_native_finalizer_2_test.dart new file mode 100644 index 00000000000..41fb3bd9f04 --- /dev/null +++ b/tests/ffi/vmspecific_native_finalizer_2_test.dart @@ -0,0 +1,95 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions +// +// VMOptions=--trace-finalizers + +import 'dart:ffi'; + +import 'package:expect/expect.dart'; +import 'package:ffi/ffi.dart'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; + +void main() { + testFinalizerRuns(); + testFinalizerDetach(); + testDoubleDetach(); + testDetachNonDetach(); + testWrongArguments(); +} + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +void testFinalizerRuns() { + using((Arena allocator) { + final token = allocator(); + createAndLoseFinalizable(token); + doGC(); + Expect.equals(42, token.value); + }); +} + +void createAndLoseFinalizable(Pointer token) { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token.cast()); + Expect.equals(0, token.value); +} + +void testFinalizerDetach() { + using((Arena allocator) { + final token = allocator(); + attachAndDetach(token); + doGC(); + Expect.equals(0, token.value); + }); +} + +class Detach { + String identifier; + + Detach(this.identifier); +} + +void attachAndDetach(Pointer token) { + final myFinalizable = MyFinalizable(); + final detach = Detach('detach 123'); + setTokenFinalizer.attach(myFinalizable, token.cast(), detach: detach); + setTokenFinalizer.detach(detach); + Expect.equals(0, token.value); +} + +void testDoubleDetach() { + using((Arena allocator) { + final token = allocator(); + final myFinalizable = MyFinalizable(); + final detach = Detach('detach 321'); + setTokenFinalizer.attach(myFinalizable, token.cast(), detach: detach); + setTokenFinalizer.detach(detach); + setTokenFinalizer.detach(detach); + Expect.equals(0, token.value); + }); +} + +void testDetachNonDetach() { + final detach = Detach('detach 456'); + setTokenFinalizer.detach(detach); + setTokenFinalizer.detach(detach); +} + +void testWrongArguments() { + using((Arena allocator) { + final token = allocator().cast(); + Expect.throws(() { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token, externalSize: -1024); + }); + Expect.throws(() { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token, detach: 123); + }); + }); +} diff --git a/tests/ffi/vmspecific_native_finalizer_isolate_groups_test.dart b/tests/ffi/vmspecific_native_finalizer_isolate_groups_test.dart new file mode 100644 index 00000000000..fb90ef119f9 --- /dev/null +++ b/tests/ffi/vmspecific_native_finalizer_isolate_groups_test.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions +// +// VMOptions=--trace-finalizers + +import 'dart:async'; +import 'dart:ffi'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:expect/expect.dart'; +import 'package:ffi/ffi.dart'; + +import 'ffi_test_helpers.dart'; + +void main(List args, int? address) async { + if (address != null) { + await mainHelper(args, address); + } else { + await testFinalizerRunsOnIsolateGroupShutdown(); + } +} + +Future mainHelper(List args, int address) async { + final token = Pointer.fromAddress(address); + createAndLoseFinalizable(token); + print('Isolate done.'); +} + +Future testFinalizerRunsOnIsolateGroupShutdown() async { + await using((Arena allocator) async { + final token = allocator(); + Expect.equals(0, token.value); + final portExitMessage = ReceivePort(); + await Isolate.spawnUri( + Platform.script, + [], + token.address, + onExit: portExitMessage.sendPort, + ); + await portExitMessage.first; + print('Helper isolate has exited.'); + + Expect.equals(42, token.value); + + print('End of test, shutting down.'); + }); +} diff --git a/tests/ffi/vmspecific_native_finalizer_isolates_test.dart b/tests/ffi/vmspecific_native_finalizer_isolates_test.dart new file mode 100644 index 00000000000..27ad4e08eb2 --- /dev/null +++ b/tests/ffi/vmspecific_native_finalizer_isolates_test.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions +// +// VMOptions=--trace-finalizers + +import 'dart:async'; +import 'dart:ffi'; +import 'dart:isolate'; + +import 'package:expect/expect.dart'; +import 'package:ffi/ffi.dart'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; + +void main() async { + await testSendAndExitFinalizable(); + await testSendAndExitFinalizer(); + await testFinalizerRunsOnIsolateShutdown(); + print('End of test, shutting down.'); +} + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +void runIsolateAttachFinalizer(int address) { + final token = Pointer.fromAddress(address); + createAndLoseFinalizable(token); + print('Isolate done.'); +} + +Future testFinalizerRunsOnIsolateShutdown() async { + await using((Arena allocator) async { + final token = allocator(); + Expect.equals(0, token.value); + final portExitMessage = ReceivePort(); + await Isolate.spawn( + runIsolateAttachFinalizer, + token.address, + onExit: portExitMessage.sendPort, + ); + await portExitMessage.first; + + doGC(); + Expect.equals(42, token.value); + }); +} + +Future testSendAndExitFinalizable() async { + final receivePort = ReceivePort(); + await Isolate.spawn( + (SendPort sendPort) { + try { + Isolate.exit(sendPort, MyFinalizable()); + } catch (e) { + print('Expected exception: $e.'); + Isolate.exit(sendPort, e); + } + }, + receivePort.sendPort, + ); + final result = await receivePort.first; + Expect.type(result); +} + +Future testSendAndExitFinalizer() async { + final receivePort = ReceivePort(); + await Isolate.spawn( + (SendPort sendPort) { + try { + Isolate.exit(sendPort, MyFinalizable()); + } catch (e) { + print('Expected exception: $e.'); + Isolate.exit(sendPort, e); + } + }, + receivePort.sendPort, + ); + final result = await receivePort.first; + Expect.type(result); +} diff --git a/tests/ffi/vmspecific_native_finalizer_test.dart b/tests/ffi/vmspecific_native_finalizer_test.dart new file mode 100644 index 00000000000..ad315fbd8b6 --- /dev/null +++ b/tests/ffi/vmspecific_native_finalizer_test.dart @@ -0,0 +1,84 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions + +import 'dart:ffi'; +import 'dart:io'; + +import 'ffi_test_helpers.dart'; + +void main() { + testMallocFree(); + print('end of test, shutting down'); +} + +void testMallocFree() { + if (Platform.isWindows) { + // malloc and free not supported. + return; + } + + print('freePtr $freePtr'); + + { + final resource = MyNativeResource(); + resource.close(); + doGC(); + } + + { + MyNativeResource(); + doGC(); + } + + // Run finalizer on shutdown (or on a GC that runs before shutdown). + MyNativeResource(); +} + +class MyNativeResource implements Finalizable { + final Pointer pointer; + + bool _closed = false; + + MyNativeResource._(this.pointer, {int? externalSize}) { + print('pointer $pointer'); + freeFinalizer.attach(this, pointer, + externalSize: externalSize, detach: this); + } + + factory MyNativeResource() { + const num = 1; + const size = 16; + final pointer = calloc(num, size); + return MyNativeResource._(pointer, externalSize: size); + } + + /// Eagerly stop using the native resource. Cancelling the finalizer. + void close() { + _closed = true; + freeFinalizer.detach(this); + free(pointer); + } + + void useResource() { + if (_closed) { + throw UnsupportedError('The native resource has already been released'); + } + print(pointer.address); + } +} + +final DynamicLibrary stdlib = DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc calloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +final freePtr = stdlib.lookup>('free'); +final free = freePtr.asFunction)>(); + +final freeFinalizer = NativeFinalizer(freePtr); diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart index ba7f5ab16ad..47df844ebab 100644 --- a/tests/ffi/vmspecific_static_checks_test.dart +++ b/tests/ffi/vmspecific_static_checks_test.dart @@ -880,3 +880,9 @@ class AbiSpecificInteger4 { const AbiSpecificInteger4(); } + +class MyFinalizableStruct extends Struct + implements Finalizable //# 2000: compile-time error +{ + external Pointer field; +} diff --git a/tests/ffi_2/ffi_2.status b/tests/ffi_2/ffi_2.status index e22df5b89a7..361a2520d20 100644 --- a/tests/ffi_2/ffi_2.status +++ b/tests/ffi_2/ffi_2.status @@ -20,6 +20,7 @@ regress_47594_test: Skip # Profiler is not available in Product. [ $system == android ] *: Pass, Slow # https://github.com/dart-lang/sdk/issues/38489 regress_47594_test: Skip # DartDev is not available on Android. +vmspecific_native_finalizer_isolate_groups_test: Skip # SpawnUri not available on Android tester. [ $system == windows ] regress_47594_test: Skip # DynamicLibrary.process() is not available on Windows. diff --git a/tests/ffi_2/ffi_test_helpers.dart b/tests/ffi_2/ffi_test_helpers.dart index aa2ed864a19..9573a5c3ff3 100644 --- a/tests/ffi_2/ffi_test_helpers.dart +++ b/tests/ffi_2/ffi_test_helpers.dart @@ -6,6 +6,8 @@ // @dart = 2.9 +// ignore: import_internal_library, unused_import +import 'dart:_internal'; import 'dart:ffi'; import 'dylib_utils.dart'; @@ -19,12 +21,45 @@ typedef UnaryOpVoid = void Function(int); final DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); -final triggerGc = ffiTestFunctions - .lookupFunction("TriggerGC"); - final collectOnNthAllocation = ffiTestFunctions .lookupFunction("CollectOnNthAllocation"); extension PointerOffsetBy on Pointer { Pointer offsetBy(int bytes) => Pointer.fromAddress(address + bytes); } + +/// Triggers garbage collection. +// Defined in `dart:_internal`. +// ignore: undefined_identifier +void triggerGc() => VMInternalsForTesting.collectAllGarbage(); + +void Function(String) _namedPrint(String name) { + if (name != null) { + return (String value) => print('$name: $value'); + } + return (String value) => print(value); +} + +/// Does a GC and if [doAwait] awaits a future to enable running finalizers. +/// +/// Also prints for debug purposes. +/// +/// If provided, [name] prefixes the debug prints. +void doGC({String name}) { + final _print = _namedPrint(name); + + _print('Do GC.'); + triggerGc(); + _print('GC done'); +} + +void createAndLoseFinalizable(Pointer token) { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token.cast()); +} + +final setTokenTo42Ptr = + ffiTestFunctions.lookup("SetArgumentTo42"); +final setTokenFinalizer = NativeFinalizer(setTokenTo42Ptr); + +class MyFinalizable implements Finalizable {} diff --git a/tests/ffi_2/vmspecific_native_finalizer_2_test.dart b/tests/ffi_2/vmspecific_native_finalizer_2_test.dart new file mode 100644 index 00000000000..4f651db1fc6 --- /dev/null +++ b/tests/ffi_2/vmspecific_native_finalizer_2_test.dart @@ -0,0 +1,97 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions +// +// VMOptions=--trace-finalizers + +// @dart = 2.9 + +import 'dart:ffi'; + +import 'package:expect/expect.dart'; +import 'package:ffi/ffi.dart'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; + +void main() { + testFinalizerRuns(); + testFinalizerDetach(); + testDoubleDetach(); + testDetachNonDetach(); + testWrongArguments(); +} + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +void testFinalizerRuns() { + using((Arena allocator) { + final token = allocator(); + createAndLoseFinalizable(token); + doGC(); + Expect.equals(42, token.value); + }); +} + +void createAndLoseFinalizable(Pointer token) { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token.cast()); + Expect.equals(0, token.value); +} + +void testFinalizerDetach() { + using((Arena allocator) { + final token = allocator(); + attachAndDetach(token); + doGC(); + Expect.equals(0, token.value); + }); +} + +class Detach { + String identifier; + + Detach(this.identifier); +} + +void attachAndDetach(Pointer token) { + final myFinalizable = MyFinalizable(); + final detach = Detach('detach 123'); + setTokenFinalizer.attach(myFinalizable, token.cast(), detach: detach); + setTokenFinalizer.detach(detach); + Expect.equals(0, token.value); +} + +void testDoubleDetach() { + using((Arena allocator) { + final token = allocator(); + final myFinalizable = MyFinalizable(); + final detach = Detach('detach 321'); + setTokenFinalizer.attach(myFinalizable, token.cast(), detach: detach); + setTokenFinalizer.detach(detach); + setTokenFinalizer.detach(detach); + Expect.equals(0, token.value); + }); +} + +void testDetachNonDetach() { + final detach = Detach('detach 456'); + setTokenFinalizer.detach(detach); + setTokenFinalizer.detach(detach); +} + +void testWrongArguments() { + using((Arena allocator) { + final token = allocator().cast(); + Expect.throws(() { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token, externalSize: -1024); + }); + Expect.throws(() { + final myFinalizable = MyFinalizable(); + setTokenFinalizer.attach(myFinalizable, token, detach: 123); + }); + }); +} diff --git a/tests/ffi_2/vmspecific_native_finalizer_isolate_groups_test.dart b/tests/ffi_2/vmspecific_native_finalizer_isolate_groups_test.dart new file mode 100644 index 00000000000..c68f4a39ece --- /dev/null +++ b/tests/ffi_2/vmspecific_native_finalizer_isolate_groups_test.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions +// +// VMOptions=--trace-finalizers + +// @dart = 2.9 + +import 'dart:async'; +import 'dart:ffi'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:expect/expect.dart'; +import 'package:ffi/ffi.dart'; + +import 'ffi_test_helpers.dart'; + +void main(List args, int address) async { + if (address != null) { + await mainHelper(args, address); + } else { + await testFinalizerRunsOnIsolateGroupShutdown(); + } +} + +Future mainHelper(List args, int address) async { + final token = Pointer.fromAddress(address); + createAndLoseFinalizable(token); + print('Isolate done.'); +} + +Future testFinalizerRunsOnIsolateGroupShutdown() async { + await using((Arena allocator) async { + final token = allocator(); + Expect.equals(0, token.value); + final portExitMessage = ReceivePort(); + await Isolate.spawnUri( + Platform.script, + [], + token.address, + onExit: portExitMessage.sendPort, + ); + await portExitMessage.first; + print('Helper isolate has exited.'); + + Expect.equals(42, token.value); + + print('End of test, shutting down.'); + }); +} diff --git a/tests/ffi_2/vmspecific_native_finalizer_isolates_test.dart b/tests/ffi_2/vmspecific_native_finalizer_isolates_test.dart new file mode 100644 index 00000000000..50eadfbfd5b --- /dev/null +++ b/tests/ffi_2/vmspecific_native_finalizer_isolates_test.dart @@ -0,0 +1,85 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions +// +// VMOptions=--trace-finalizers + +// @dart = 2.9 + +import 'dart:async'; +import 'dart:ffi'; +import 'dart:isolate'; + +import 'package:expect/expect.dart'; +import 'package:ffi/ffi.dart'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; + +void main() async { + await testSendAndExitFinalizable(); + await testSendAndExitFinalizer(); + await testFinalizerRunsOnIsolateShutdown(); + print('End of test, shutting down.'); +} + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +void runIsolateAttachFinalizer(int address) { + final token = Pointer.fromAddress(address); + createAndLoseFinalizable(token); + print('Isolate done.'); +} + +Future testFinalizerRunsOnIsolateShutdown() async { + await using((Arena allocator) async { + final token = allocator(); + Expect.equals(0, token.value); + final portExitMessage = ReceivePort(); + await Isolate.spawn( + runIsolateAttachFinalizer, + token.address, + onExit: portExitMessage.sendPort, + ); + await portExitMessage.first; + + doGC(); + Expect.equals(42, token.value); + }); +} + +Future testSendAndExitFinalizable() async { + final receivePort = ReceivePort(); + await Isolate.spawn( + (SendPort sendPort) { + try { + Isolate.exit(sendPort, MyFinalizable()); + } catch (e) { + print('Expected exception: $e.'); + Isolate.exit(sendPort, e); + } + }, + receivePort.sendPort, + ); + final result = await receivePort.first; + Expect.type(result); +} + +Future testSendAndExitFinalizer() async { + final receivePort = ReceivePort(); + await Isolate.spawn( + (SendPort sendPort) { + try { + Isolate.exit(sendPort, MyFinalizable()); + } catch (e) { + print('Expected exception: $e.'); + Isolate.exit(sendPort, e); + } + }, + receivePort.sendPort, + ); + final result = await receivePort.first; + Expect.type(result); +} diff --git a/tests/ffi_2/vmspecific_native_finalizer_test.dart b/tests/ffi_2/vmspecific_native_finalizer_test.dart new file mode 100644 index 00000000000..f596609cf97 --- /dev/null +++ b/tests/ffi_2/vmspecific_native_finalizer_test.dart @@ -0,0 +1,89 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions + +// @dart = 2.9 + +import 'dart:ffi'; +import 'dart:io'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; + +void main() { + testMallocFree(); + print('end of test, shutting down'); +} + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +void testMallocFree() { + if (Platform.isWindows) { + // malloc and free not supported. + return; + } + + print('freePtr $freePtr'); + + { + final resource = MyNativeResource(); + resource.close(); + doGC(); + } + + { + MyNativeResource(); + doGC(); + } + + // Run finalizer on shutdown (or on a GC that runs before shutdown). + MyNativeResource(); +} + +class MyNativeResource implements Finalizable { + final Pointer pointer; + + bool _closed = false; + + MyNativeResource._(this.pointer, {int externalSize}) { + print('pointer $pointer'); + freeFinalizer.attach(this, pointer, + externalSize: externalSize, detach: this); + } + + factory MyNativeResource() { + const num = 1; + const size = 16; + final pointer = calloc(num, size); + return MyNativeResource._(pointer, externalSize: size); + } + + /// Eagerly stop using the native resource. Cancelling the finalizer. + void close() { + _closed = true; + freeFinalizer.detach(this); + free(pointer); + } + + void useResource() { + if (_closed) { + throw UnsupportedError('The native resource has already been released'); + } + print(pointer.address); + } +} + +final DynamicLibrary stdlib = DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc calloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +final freePtr = stdlib.lookup>('free'); +final free = freePtr.asFunction)>(); + +final freeFinalizer = NativeFinalizer(freePtr); diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart index ecb15cb615d..cd0feaf5180 100644 --- a/tests/ffi_2/vmspecific_static_checks_test.dart +++ b/tests/ffi_2/vmspecific_static_checks_test.dart @@ -879,3 +879,10 @@ class AbiSpecificInteger4 { const AbiSpecificInteger4(); } + +class MyFinalizableStruct extends Struct + implements Finalizable //# 2000: compile-time error +{ + Pointer field; +} +