Version 2.17.0-244.0.dev

Merge commit '532c116cd2db02adcb6dbf32f19d5c438fd3585b' into 'dev'
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 33e98e9..36be330 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -4084,6 +4084,36 @@
         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<Message Function(String string, String name)>
+    templateFfiCompoundImplementsFinalizable =
+    const Template<Message Function(String string, String name)>(
+        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<Message Function(String string, String name)>
+    codeFfiCompoundImplementsFinalizable =
+    const Code<Message Function(String string, String name)>(
+  "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(
         String string,
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 1be33f8..a9f8b35 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 @@
   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 e3393a2..6477d49 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -507,6 +507,7 @@
   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 18488af..c52348a 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
@@ -168,6 +168,48 @@
   );
 
   /**
+   * 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.
    */
   // #### Description
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 34d16ab..4c8241b 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -146,9 +146,25 @@
       }
     }
 
-    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 c4921ef..0b689ee 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -15689,6 +15689,45 @@
         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 b62c661..c3070d3 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -1992,6 +1992,42 @@
 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 274c985..d7402a7 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 @@
         noLength,
         templateCantHaveNamedParameters,
         templateCantHaveOptionalParameters,
+        templateFfiCompoundImplementsFinalizable,
         templateFfiDartTypeMismatch,
         templateFfiEmptyStruct,
         templateFfiExpectedConstantArg,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index ac4cc31..98030ea 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -361,6 +361,7 @@
 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 f0fea47..7079da9 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4690,6 +4690,12 @@
   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 abd5d63..1d062f6 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 @@
 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 8c98fe7..874fc40 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 @@
   ffi::unsized,
   ffi::sizeOf,
   ffi::Dart_NativeMessageHandler,
+  ffi::NativeFinalizerFunction,
   ffi::Abi,
   ffi::AbiSpecificInteger,
   ffi::AbiSpecificIntegerArray,
@@ -81,6 +82,7 @@
   ffi::Long,
   ffi::LongLong,
   ffi::NativeApi,
+  ffi::NativeFinalizer,
   ffi::NativeFunction,
   ffi::NativeFunctionPointer,
   ffi::NativePort,
@@ -129,6 +131,7 @@
   ffi::unsized,
   ffi::sizeOf,
   ffi::Dart_NativeMessageHandler,
+  ffi::NativeFinalizerFunction,
   ffi::Abi,
   ffi::AbiSpecificInteger,
   ffi::AbiSpecificIntegerArray,
@@ -171,6 +174,7 @@
   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 820c44b..6fb54df 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 @@
   ffi::unsized,
   ffi::sizeOf,
   ffi::Dart_NativeMessageHandler,
+  ffi::NativeFinalizerFunction,
   ffi::Abi,
   ffi::AbiSpecificInteger,
   ffi::AbiSpecificIntegerArray,
@@ -81,6 +82,7 @@
   ffi::Long,
   ffi::LongLong,
   ffi::NativeApi,
+  ffi::NativeFinalizer,
   ffi::NativeFunction,
   ffi::NativeFunctionPointer,
   ffi::NativePort,
@@ -129,6 +131,7 @@
   ffi::unsized,
   ffi::sizeOf,
   ffi::Dart_NativeMessageHandler,
+  ffi::NativeFinalizerFunction,
   ffi::Abi,
   ffi::AbiSpecificInteger,
   ffi::AbiSpecificIntegerArray,
@@ -171,6 +174,7 @@
   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 16571c0..7a67f89 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -409,7 +409,8 @@
   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 fe38da0..b5d095e 100644
--- a/pkg/vm/lib/transformations/ffi/definitions.dart
+++ b/pkg/vm/lib/transformations/ffi/definitions.dart
@@ -8,6 +8,7 @@
         messageFfiAbiSpecificIntegerMappingInvalid,
         messageFfiPackedAnnotationAlignment,
         messageNonPositiveArrayDimensions,
+        templateFfiCompoundImplementsFinalizable,
         templateFfiEmptyStruct,
         templateFfiFieldAnnotation,
         templateFfiFieldNull,
@@ -321,6 +322,19 @@
       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 758465b..84b1469 100644
--- a/pkg/vm/lib/transformations/ffi/finalizable.dart
+++ b/pkg/vm/lib/transformations/ffi/finalizable.dart
@@ -14,6 +14,9 @@
 /// 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 4a4067b..36516fc 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -1109,6 +1109,13 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// NativeFinalizer tests
+
+DART_EXPORT void SetArgumentTo42(intptr_t* token) {
+  *token = 42;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Functions for testing @FfiNative.
 
 DART_EXPORT Dart_Handle GetRootLibraryUrl() {
diff --git a/runtime/docs/gc.md b/runtime/docs/gc.md
index ac9696d..6086968 100644
--- a/runtime/docs/gc.md
+++ b/runtime/docs/gc.md
@@ -210,15 +210,17 @@
 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 @@
 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 e1a6b24..20f6756 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 @@
   return Pointer::New(type_arg, reinterpret_cast<intptr_t>(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 e2ef579..8e95dab 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -184,6 +184,7 @@
   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 @@
           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 @@
               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 @@
                                       "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 d714c56..7dc9690 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 @@
     await testWeakProperty();
     await testWeakReference();
     await testFinalizer();
+    await testNativeFinalizer();
+    await testFinalizable();
+    await testPointer();
 
     await testForbiddenClosures();
   }
@@ -765,6 +769,27 @@
     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<Int8>.fromAddress(0xdeadbeef);
+    Expect.throwsArgumentError(() => sendPort.send(pointer));
+  }
+
   Future testForbiddenClosures() async {
     print('testForbiddenClosures');
     for (final closure in nonCopyableClosures) {
@@ -788,6 +813,8 @@
   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 91fd27d..fc62573 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 @@
     await testWeakProperty();
     await testWeakReference();
     await testFinalizer();
+    await testNativeFinalizer();
+    await testFinalizable();
+    await testPointer();
 
     await testForbiddenClosures();
   }
@@ -767,6 +771,27 @@
     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<Int8>.fromAddress(0xdeadbeef);
+    Expect.throwsArgumentError(() => sendPort.send(pointer));
+  }
+
   Future testForbiddenClosures() async {
     print('testForbiddenClosures');
     for (final closure in nonCopyableClosures) {
@@ -790,6 +815,8 @@
   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 91b4f6f..4258c9d 100644
--- a/runtime/vm/bootstrap_natives.cc
+++ b/runtime/vm/bootstrap_natives.cc
@@ -31,6 +31,15 @@
 #endif  // !DART_PRECOMPILED_RUNTIME
 };
 
+#define REGISTER_FFI_NATIVE_ENTRY(name, return_type, argument_types)           \
+  {"" #name, reinterpret_cast<void*>(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 @@
   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 @@
 
   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 @@
   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 df65a0d..00a3c4c 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -452,12 +452,18 @@
   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 @@
 #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 e91180e..141ae5a 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1034,6 +1034,10 @@
   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 @@
   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 501c3a8..7d764c7 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -70,6 +70,7 @@
   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 8d01fc7..2c4af9c 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -2807,6 +2807,7 @@
     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 dfd04ac..9356180 100644
--- a/runtime/vm/compiler/backend/slot.cc
+++ b/runtime/vm/compiler/backend/slot.cc
@@ -237,6 +237,7 @@
     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 995ce8cf..aa0c8ad 100644
--- a/runtime/vm/compiler/backend/slot.h
+++ b/runtime/vm/compiler/backend/slot.h
@@ -101,6 +101,7 @@
   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 @@
   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 9abd1fd..905570f 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -825,6 +825,7 @@
   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 @@
   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 @@
     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 @@
       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 1676f7c..dc6050c 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -123,15 +123,16 @@
     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 2973168..5ae1b56 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -447,6 +447,8 @@
       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 2b2a7d3..41ad454 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -1026,7 +1026,14 @@
 
 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 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 e2c52c5..78c6e5d 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -220,12 +220,12 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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 @@
     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_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 @@
     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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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
     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 @@
     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 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 a2da836..1e03be5 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 501568e..2081ad0 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -744,8 +744,13 @@
   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 65f56c3..a9e7275 100644
--- a/runtime/vm/heap/gc_shared.cc
+++ b/runtime/vm/heap/gc_shared.cc
@@ -36,4 +36,9 @@
 #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 7e0cb2d..b7aac40 100644
--- a/runtime/vm/heap/gc_shared.h
+++ b/runtime/vm/heap/gc_shared.h
@@ -37,6 +37,7 @@
 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 @@
 #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 <typename GCVisitorType>
+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<NativeFinalizer::Callback>(
+      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<PointerPtr>(token_object);
+    void* peer = reinterpret_cast<void*>(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 @@
 // 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 <typename GCVisitorType>
 void MournFinalized(GCVisitorType* visitor) {
   FinalizerEntryPtr current_entry =
@@ -117,12 +167,24 @@
     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, &current_entry->untag()->value_);
-    GCVisitorType::SetNullIfCollected(heap_base,
-                                      &current_entry->untag()->detach_);
-    GCVisitorType::SetNullIfCollected(heap_base,
-                                      &current_entry->untag()->finalizer_);
+    const Heap::Space before_gc_space = SpaceForExternal(current_entry);
+    const bool value_collected_this_gc =
+        GCVisitorType::ForwardOrSetNullIfCollected(
+            heap_base, &current_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, &current_entry->untag()->detach_);
+    GCVisitorType::ForwardOrSetNullIfCollected(
+        heap_base, &current_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 @@
                         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 @@
         ASSERT(Thread::Current()->IsAtSafepoint() ||
                Thread::Current()->BypassSafepoints());
 
+        if (finalizer.IsNativeFinalizer()) {
+          NativeFinalizerPtr native_finalizer =
+              static_cast<NativeFinalizerPtr>(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 @@
                 /*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 63a150b..2b77843 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -296,15 +296,16 @@
 
       // 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 1945296..e11759a 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 @@
   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 @@
       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<FinalizerEntryPtr>(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 @@
 
     // 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 @@
 
 // Returns whether the object referred to in `ptr_address` was GCed this GC.
 template <bool parallel>
-bool ScavengerVisitorBase<parallel>::SetNullIfCollected(
+bool ScavengerVisitorBase<parallel>::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 38c2ed6..5a93aec 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -449,6 +449,7 @@
   template <bool>
   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 287aa91..e797f5c 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1955,6 +1955,10 @@
   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 @@
   }
 
   // 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 @@
           // 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 bd1e9fb..76bbd89 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -1065,6 +1065,7 @@
   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 b0d5250..9bd1a91 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -3944,7 +3944,7 @@
   } 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 460d382..5022ffc 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 @@
   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 a81a147..db1c8a1 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 @@
     pending_classes.Add(cls);
     RegisterClass(cls, Symbols::FfiDynamicLibrary(), lib);
 
+    cls = Class::New<NativeFinalizer, RTN::NativeFinalizer>(isolate_group);
+    object_store->set_native_finalizer_class(cls);
+    RegisterPrivateClass(cls, Symbols::_NativeFinalizer(), lib);
+
     cls = Class::New<Finalizer, RTN::Finalizer>(isolate_group);
     cls.set_type_arguments_field_offset(
         Finalizer::type_arguments_offset(),
@@ -2539,6 +2544,8 @@
     object_store->set_weak_reference_class(cls);
     cls = Class::New<Finalizer, RTN::Finalizer>(isolate_group);
     object_store->set_finalizer_class(cls);
+    cls = Class::New<NativeFinalizer, RTN::NativeFinalizer>(isolate_group);
+    object_store->set_native_finalizer_class(cls);
     cls = Class::New<FinalizerEntry, RTN::FinalizerEntry>(isolate_group);
     object_store->set_finalizer_entry_class(cls);
 
@@ -2992,6 +2999,11 @@
   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 @@
   }
 }
 
+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 @@
 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 @@
                      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<NativeFinalizerPtr>(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<NativeFinalizer::Callback>(
+      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<void*>(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<FinalizerEntryPtr>(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 34b5b78..008767b 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1608,6 +1608,10 @@
   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 @@
     kIsAllocatedBit,
     kIsLoadedBit,
     kHasPragmaBit,
+    kImplementsFinalizableBit,
   };
   class ConstBit : public BitField<uint32_t, bool, kConstBit, 1> {};
   class ImplementedBit : public BitField<uint32_t, bool, kImplementedBit, 1> {};
@@ -1853,6 +1858,8 @@
   class IsAllocatedBit : public BitField<uint32_t, bool, kIsAllocatedBit, 1> {};
   class IsLoadedBit : public BitField<uint32_t, bool, kIsLoadedBit, 1> {};
   class HasPragmaBit : public BitField<uint32_t, bool, kHasPragmaBit, 1> {};
+  class ImplementsFinalizableBit
+      : public BitField<uint32_t, bool, kImplementsFinalizableBit, 1> {};
 
   void set_name(const String& value) const;
   void set_user_name(const String& value) const;
@@ -1891,6 +1898,12 @@
   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 @@
   // 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 @@
   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 @@
   // 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 @@
 };
 
 // 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 @@
   //  - 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 @@
   //  - 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 @@
   friend class Class;
 };
 
+class FinalizerBase;
 class FinalizerEntry : public Instance {
  public:
   ObjectPtr value() const { return untag()->value(); }
@@ -12084,11 +12075,31 @@
     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 @@
   }
 
   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 @@
   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 ede344d..66ebd79 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::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 @@
       // 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 59245d6..eab9078 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -1719,6 +1719,21 @@
   // 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 bdbc4a1..056d9e6 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -486,6 +486,15 @@
         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 26bce63..b9ba248 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -56,6 +56,7 @@
   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 @@
   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 888041a..11bda3c 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 @@
 
   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 @@
 
   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 @@
 
   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 @@
 
   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 @@
     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 @@
 
   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 @@
 
 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<bool> 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<uword>(&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<uword>(&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<uword>(&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 @@
   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 82e1bf8..80c1008 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -554,6 +554,7 @@
 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 c21d64a..89dae69 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2846,6 +2846,9 @@
 // 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 @@
   friend class ScavengerVisitorBase;
 };
 
+class UntaggedNativeFinalizer : public UntaggedFinalizerBase {
+  RAW_HEAP_OBJECT_IMPLEMENTATION(NativeFinalizer);
+
+  COMPRESSED_POINTER_FIELD(PointerPtr, callback)
+  VISIT_TO(callback)
+
+  friend class GCMarker;
+  template <bool>
+  friend class MarkingVisitorBase;
+  friend class Scavenger;
+  template <bool>
+  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 @@
   // Only populated during the GC, otherwise null.
   COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, next_seen_by_gc)
 
+  intptr_t external_size_;
+
   template <typename Type, typename PtrType>
   friend class GCLinkedList;
   template <typename GCVisitorType>
@@ -3419,6 +3443,7 @@
   friend class Scavenger;
   template <bool>
   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 7953a1f..ba78ad7 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -137,6 +137,7 @@
   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 @@
   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 @@
   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 5a0b99f..09968d4 100644
--- a/runtime/vm/tagged_pointer.h
+++ b/runtime/vm/tagged_pointer.h
@@ -416,6 +416,7 @@
 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 c897d45..fe904d0 100644
--- a/runtime/vm/type_testing_stubs_test.cc
+++ b/runtime/vm/type_testing_stubs_test.cc
@@ -2303,7 +2303,7 @@
 // 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<X> {}
diff --git a/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart b/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart
index 0edd45c..791bd90 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<T>()` 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 b9bcaf0..83a6a8a 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 e697230..7c7d38d 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: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<NativeFinalizerFunction> callback) =
+      _NativeFinalizer;
+}
+
+@pragma("vm:entry-point")
+class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
+  external Pointer<NativeFinalizerFunction> get _callback;
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
+  external set _callback(Pointer<NativeFinalizerFunction> value);
+
+  _NativeFinalizer(Pointer<NativeFinalizerFunction> callback) {
+    allEntries = <FinalizerEntry>{};
+    _callback = callback;
+    setIsolate();
+    isolateRegisterFinalizer();
+  }
+
+  void attach(
+    Finalizable value,
+    Pointer<Void> 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] ??= <FinalizerEntry>{}).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 ca47a83..d62f0a0 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 174cc69..be50104 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<Type, int> _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 00279f3..3c83583 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 a9db853..417344f 100644
--- a/sdk/lib/_internal/vm/lib/finalizer_patch.dart
+++ b/sdk/lib/_internal/vm/lib/finalizer_patch.dart
@@ -43,15 +43,7 @@
       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 547835c..c02b2f8 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 @@
 /// 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 @@
   @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 @@
   @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 @@
   @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<Void Function(Handle, IntPtr)>('FinalizerEntry_SetExternalSize')
+  external void setExternalSize(int externalSize);
 }
diff --git a/sdk/lib/ffi/native_finalizer.dart b/sdk/lib/ffi/native_finalizer.dart
index 49fc667..14074cf 100644
--- a/sdk/lib/ffi/native_finalizer.dart
+++ b/sdk/lib/ffi/native_finalizer.dart
@@ -221,3 +221,77 @@
 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<Void Function(Pointer<Void> 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<NativeFinalizerFunction> 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<Void> 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 6b2b940..d1ff9fc 100644
--- a/sdk/lib/isolate/isolate.dart
+++ b/sdk/lib/isolate/isolate.dart
@@ -723,7 +723,9 @@
   ///     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 e22df5b..361a252 100644
--- a/tests/ffi/ffi.status
+++ b/tests/ffi/ffi.status
@@ -20,6 +20,7 @@
 [ $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 0c598ec..92dc3c7 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 @@
 final DynamicLibrary ffiTestFunctions =
     dlopenPlatformSpecific("ffi_test_functions");
 
-final triggerGc = ffiTestFunctions
-    .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
-
 final collectOnNthAllocation = ffiTestFunctions
     .lookupFunction<NativeUnaryOp, UnaryOpVoid>("CollectOnNthAllocation");
 
 extension PointerOffsetBy<T extends NativeType> on Pointer<T> {
   Pointer<T> 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<IntPtr> token) {
+  final myFinalizable = MyFinalizable();
+  setTokenFinalizer.attach(myFinalizable, token.cast());
+}
+
+final setTokenTo42Ptr =
+    ffiTestFunctions.lookup<NativeFinalizerFunction>("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 0000000..41fb3bd
--- /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<IntPtr>();
+    createAndLoseFinalizable(token);
+    doGC();
+    Expect.equals(42, token.value);
+  });
+}
+
+void createAndLoseFinalizable(Pointer<IntPtr> token) {
+  final myFinalizable = MyFinalizable();
+  setTokenFinalizer.attach(myFinalizable, token.cast());
+  Expect.equals(0, token.value);
+}
+
+void testFinalizerDetach() {
+  using((Arena allocator) {
+    final token = allocator<IntPtr>();
+    attachAndDetach(token);
+    doGC();
+    Expect.equals(0, token.value);
+  });
+}
+
+class Detach {
+  String identifier;
+
+  Detach(this.identifier);
+}
+
+void attachAndDetach(Pointer<IntPtr> 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<IntPtr>();
+    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<IntPtr>().cast<Void>();
+    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 0000000..fb90ef1
--- /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<String> args, int? address) async {
+  if (address != null) {
+    await mainHelper(args, address);
+  } else {
+    await testFinalizerRunsOnIsolateGroupShutdown();
+  }
+}
+
+Future mainHelper(List<String> args, int address) async {
+  final token = Pointer<IntPtr>.fromAddress(address);
+  createAndLoseFinalizable(token);
+  print('Isolate done.');
+}
+
+Future<void> testFinalizerRunsOnIsolateGroupShutdown() async {
+  await using((Arena allocator) async {
+    final token = allocator<IntPtr>();
+    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 0000000..27ad4e0
--- /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<IntPtr>.fromAddress(address);
+  createAndLoseFinalizable(token);
+  print('Isolate done.');
+}
+
+Future<void> testFinalizerRunsOnIsolateShutdown() async {
+  await using((Arena allocator) async {
+    final token = allocator<IntPtr>();
+    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<void> 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<ArgumentError>(result);
+}
+
+Future<void> 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<ArgumentError>(result);
+}
diff --git a/tests/ffi/vmspecific_native_finalizer_test.dart b/tests/ffi/vmspecific_native_finalizer_test.dart
new file mode 100644
index 0000000..ad315fb
--- /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<Void> 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<Void> Function(IntPtr num, IntPtr size);
+typedef PosixCalloc = Pointer<Void> Function(int num, int size);
+final PosixCalloc calloc =
+    stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
+
+typedef PosixFreeNative = Void Function(Pointer<Void>);
+final freePtr = stdlib.lookup<NativeFunction<PosixFreeNative>>('free');
+final free = freePtr.asFunction<void Function(Pointer<Void>)>();
+
+final freeFinalizer = NativeFinalizer(freePtr);
diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart
index ba7f5ab..47df844 100644
--- a/tests/ffi/vmspecific_static_checks_test.dart
+++ b/tests/ffi/vmspecific_static_checks_test.dart
@@ -880,3 +880,9 @@
 {
   const AbiSpecificInteger4();
 }
+
+class MyFinalizableStruct extends Struct
+    implements Finalizable //# 2000: compile-time error
+{
+  external Pointer<Void> field;
+}
diff --git a/tests/ffi_2/ffi_2.status b/tests/ffi_2/ffi_2.status
index e22df5b..361a252 100644
--- a/tests/ffi_2/ffi_2.status
+++ b/tests/ffi_2/ffi_2.status
@@ -20,6 +20,7 @@
 [ $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 aa2ed86..9573a5c 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 @@
 final DynamicLibrary ffiTestFunctions =
     dlopenPlatformSpecific("ffi_test_functions");
 
-final triggerGc = ffiTestFunctions
-    .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
-
 final collectOnNthAllocation = ffiTestFunctions
     .lookupFunction<NativeUnaryOp, UnaryOpVoid>("CollectOnNthAllocation");
 
 extension PointerOffsetBy<T extends NativeType> on Pointer<T> {
   Pointer<T> 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<IntPtr> token) {
+  final myFinalizable = MyFinalizable();
+  setTokenFinalizer.attach(myFinalizable, token.cast());
+}
+
+final setTokenTo42Ptr =
+    ffiTestFunctions.lookup<NativeFinalizerFunction>("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 0000000..4f651db1
--- /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<IntPtr>();
+    createAndLoseFinalizable(token);
+    doGC();
+    Expect.equals(42, token.value);
+  });
+}
+
+void createAndLoseFinalizable(Pointer<IntPtr> token) {
+  final myFinalizable = MyFinalizable();
+  setTokenFinalizer.attach(myFinalizable, token.cast());
+  Expect.equals(0, token.value);
+}
+
+void testFinalizerDetach() {
+  using((Arena allocator) {
+    final token = allocator<IntPtr>();
+    attachAndDetach(token);
+    doGC();
+    Expect.equals(0, token.value);
+  });
+}
+
+class Detach {
+  String identifier;
+
+  Detach(this.identifier);
+}
+
+void attachAndDetach(Pointer<IntPtr> 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<IntPtr>();
+    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<IntPtr>().cast<Void>();
+    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 0000000..c68f4a3
--- /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<String> args, int address) async {
+  if (address != null) {
+    await mainHelper(args, address);
+  } else {
+    await testFinalizerRunsOnIsolateGroupShutdown();
+  }
+}
+
+Future mainHelper(List<String> args, int address) async {
+  final token = Pointer<IntPtr>.fromAddress(address);
+  createAndLoseFinalizable(token);
+  print('Isolate done.');
+}
+
+Future<void> testFinalizerRunsOnIsolateGroupShutdown() async {
+  await using((Arena allocator) async {
+    final token = allocator<IntPtr>();
+    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 0000000..50eadfb
--- /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<IntPtr>.fromAddress(address);
+  createAndLoseFinalizable(token);
+  print('Isolate done.');
+}
+
+Future<void> testFinalizerRunsOnIsolateShutdown() async {
+  await using((Arena allocator) async {
+    final token = allocator<IntPtr>();
+    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<void> 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<ArgumentError>(result);
+}
+
+Future<void> 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<ArgumentError>(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 0000000..f596609
--- /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<Void> 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<Void> Function(IntPtr num, IntPtr size);
+typedef PosixCalloc = Pointer<Void> Function(int num, int size);
+final PosixCalloc calloc =
+    stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
+
+typedef PosixFreeNative = Void Function(Pointer<Void>);
+final freePtr = stdlib.lookup<NativeFunction<PosixFreeNative>>('free');
+final free = freePtr.asFunction<void Function(Pointer<Void>)>();
+
+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 ecb15cb..cd0feaf 100644
--- a/tests/ffi_2/vmspecific_static_checks_test.dart
+++ b/tests/ffi_2/vmspecific_static_checks_test.dart
@@ -879,3 +879,10 @@
 {
   const AbiSpecificInteger4();
 }
+
+class MyFinalizableStruct extends Struct
+    implements Finalizable //# 2000: compile-time error
+{
+   Pointer<Void> field;
+}
+
diff --git a/tools/VERSION b/tools/VERSION
index cc9989e..e5b7204 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 243
+PRERELEASE 244
 PRERELEASE_PATCH 0
\ No newline at end of file