[vm/ffi] `address` of operator for FFI leaf calls

During FFI leaf calls, the Dart GC will not run. This means that we
can pass pointers into `TypedData` to FFI calls that take `Pointer`
arguments.

After this CL, we have three types of arguments that can flow into
`Pointer` argument in an FFI call:
* `Pointer`.
* `TypedData`: Any typed data including views.
* `_Compound`: A TypedData/Pointer and an offset in bytes.

The is only possible for `@Native external` functions, `asFunction`
does not support passing in `TypedData`. (See related GitHub issues
for discussion. TLDR: FFIgen should generate bindings without config.)

`.address` expressions on `TypedData` and `Array` elements do _not_
introduce bounds checks, even though `TypedData` and `Array` have
bounds information. E.g. `ffiNative(Uint8List(10)[20].address)` does
not throw.

Implementation details:

The CFE analyzes call-sites to `@Native external` functions. If the
arguments are `.address` expressions, it transforms the call site to
pass the compound or `TypedData`. If an additional offset needs to be
applied, the CFE constructs a new `_Compound` with the correct offset
in bytes.

The CFE then also creates a new `@Native external` function which have
`TypedData`s and `_Compound`s parameters. To avoid name clashes, these
functions are postfixed with `#` and `P`, `T`, or `C` for each Pointer
parameter.

TEST=pkg/vm/testcases/transformations/ffi/address_of_*

In the VM, `TypedData` arguments are passed as tagged values, and the
address is loaded inside the `FfiCallInstr`. `_Compound` arguments
turn into two IL definitions, one for the `TypedDataBase` (tagged),
and one for the offset in bytes (unboxed). The address is then loaded
inside the `FfiCallInstr` and the offset in bytes is applied.

Adding the offset in bytes required an extra temp register for ia32.
Also, it uncovered that the temp register in arm32 was conflicting
with the argument registers. However, TMP should suffice instead.

TEST=tests/ffi/address_of_array_generated_test.dart
TEST=tests/ffi/address_of_struct_generated_test.dart
TEST=tests/ffi/address_of_typeddata_generated_test.dart

Closes: https://github.com/dart-lang/sdk/issues/44589
Closes: https://github.com/dart-lang/sdk/issues/54771

CoreLibraryReviewExempt: VM only, unsupported in dart2wasm
Change-Id: I01fb428cfd6f9096a34689c2819c124a8003cb6b
Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64c-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/360882
Reviewed-by: Jens Johansen <jensj@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
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 f1dd247..9087939 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -5901,6 +5901,28 @@
 );
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeFfiAddressPosition = messageFfiAddressPosition;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageFfiAddressPosition = const MessageCode(
+  "FfiAddressPosition",
+  problemMessage:
+      r"""The '.address' expression can only be used as argument to a leaf native external call.""",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeFfiAddressReceiver = messageFfiAddressReceiver;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageFfiAddressReceiver = const MessageCode(
+  "FfiAddressReceiver",
+  problemMessage:
+      r"""The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field.""",
+  correctionMessage:
+      r"""Change the receiver of '.address' to one of the allowed kinds.""",
+);
+
+// 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)>(
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 94d53a1..a3706d3 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
@@ -1743,6 +1743,12 @@
 FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED:
   status: noFix
   since: ~2.16
+FfiCode.ADDRESS_POSITION:
+  status: needsEvaluation
+  since: ~3.5
+FfiCode.ADDRESS_RECEIVER:
+  status: needsEvaluation
+  since: ~3.5
 FfiCode.ANNOTATION_ON_POINTER_FIELD:
   status: needsFix
 FfiCode.ARGUMENT_MUST_BE_A_CONSTANT:
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 5303d1e..f8e88f5 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
@@ -66,6 +66,23 @@
   );
 
   ///  No parameters.
+  static const FfiCode ADDRESS_POSITION = FfiCode(
+    'ADDRESS_POSITION',
+    "The '.address' expression can only be used as argument to a leaf native "
+        "external call.",
+  );
+
+  ///  No parameters.
+  static const FfiCode ADDRESS_RECEIVER = FfiCode(
+    'ADDRESS_RECEIVER',
+    "The receiver of '.address' must be a concrete 'TypedData', a concrete "
+        "'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a "
+        "Union field.",
+    correctionMessage:
+        "Change the receiver of '.address' to one of the allowed kinds.",
+  );
+
+  ///  No parameters.
   static const FfiCode ANNOTATION_ON_POINTER_FIELD = FfiCode(
     'ANNOTATION_ON_POINTER_FIELD',
     "Fields in a struct class whose type is 'Pointer' shouldn't have any "
diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart
index 1b9f038..3c4e1df 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -590,6 +590,8 @@
   FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_EXTRA,
   FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING,
   FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED,
+  FfiCode.ADDRESS_POSITION,
+  FfiCode.ADDRESS_RECEIVER,
   FfiCode.ANNOTATION_ON_POINTER_FIELD,
   FfiCode.ARGUMENT_MUST_BE_A_CONSTANT,
   FfiCode.ARGUMENT_MUST_BE_NATIVE,
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 90ccd16..c0169e0 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -35,6 +35,37 @@
   static const _nativeCallable = 'NativeCallable';
   static const _opaqueClassName = 'Opaque';
 
+  static const _addressOfExtensionNames = {
+    ..._addressOfCompoundExtensionNames,
+    ..._addressOfPrimitiveExtensionNames,
+    ..._addressOfTypedDataExtensionNames,
+  };
+
+  static const _addressOfCompoundExtensionNames = {
+    'ArrayAddress',
+    'StructAddress',
+    'UnionAddress',
+  };
+
+  static const _addressOfPrimitiveExtensionNames = {
+    'BoolAddress',
+    'DoubleAddress',
+    'IntAddress',
+  };
+
+  static const _addressOfTypedDataExtensionNames = {
+    'Float32ListAddress',
+    'Float64ListAddress',
+    'Int16ListAddress',
+    'Int32ListAddress',
+    'Int64ListAddress',
+    'Int8ListAddress',
+    'Uint16ListAddress',
+    'Uint32ListAddress',
+    'Uint64ListAddress',
+    'Uint8ListAddress',
+  };
+
   static const Set<String> _primitiveIntegerNativeTypesFixedSize = {
     'Int8',
     'Int16',
@@ -346,6 +377,10 @@
         if (element.name == 'ref') {
           _validateRefPrefixedIdentifier(node);
         }
+      } else if (enclosingElement.isAddressOfExtension) {
+        if (element.name == 'address') {
+          _validateAddressPrefixedIdentifier(node);
+        }
       }
     }
     super.visitPrefixedIdentifier(node);
@@ -361,6 +396,10 @@
         if (element.name == 'ref') {
           _validateRefPropertyAccess(node);
         }
+      } else if (enclosingElement.isAddressOfExtension) {
+        if (element.name == 'address') {
+          _validateAddressPropertyAccess(node);
+        }
       }
     }
     super.visitPropertyAccess(node);
@@ -986,6 +1025,84 @@
     }
   }
 
+  /// Check that .address is only used in argument lists passed to native leaf
+  /// calls.
+  void _validateAddressPosition(Expression node, AstNode errorNode) {
+    final parent = node.parent;
+    final grandParent = parent?.parent;
+    if (parent is! ArgumentList ||
+        grandParent is! MethodInvocation ||
+        !grandParent.isNativeLeafInvocation) {
+      _errorReporter.atNode(
+        errorNode,
+        FfiCode.ADDRESS_POSITION,
+      );
+    }
+  }
+
+  void _validateAddressPrefixedIdentifier(PrefixedIdentifier node) {
+    final errorNode = node.identifier;
+    _validateAddressPosition(node, errorNode);
+    final extensionName = node.staticElement?.enclosingElement?.name;
+    final receiver = node.prefix;
+    _validateAddressReceiver(node, extensionName, receiver, errorNode);
+  }
+
+  void _validateAddressPropertyAccess(PropertyAccess node) {
+    final errorNode = node.propertyName;
+    _validateAddressPosition(node, errorNode);
+    final extensionName =
+        node.propertyName.staticElement?.enclosingElement?.name;
+    final receiver = node.target;
+    _validateAddressReceiver(node, extensionName, receiver, errorNode);
+  }
+
+  void _validateAddressReceiver(
+    Expression node,
+    String? extensionName,
+    Expression? receiver,
+    AstNode errorNode,
+  ) {
+    if (_addressOfCompoundExtensionNames.contains(extensionName) ||
+        _addressOfTypedDataExtensionNames.contains(extensionName)) {
+      return; // Only primitives need their reciever checked.
+    }
+    if (receiver == null) {
+      return;
+    }
+    switch (receiver) {
+      case IndexExpression _:
+        // Array or TypedData element.
+        final arrayOrTypedData = receiver.target;
+        final type = arrayOrTypedData?.staticType;
+        if (type?.isArray ?? false) {
+          return;
+        }
+        if (type?.isTypedData ?? false) {
+          return;
+        }
+      case PrefixedIdentifier _:
+        // Struct or Union field.
+        final compound = receiver.prefix;
+        final type = compound.staticType;
+        if (type?.isCompoundSubtype ?? false) {
+          return;
+        }
+      case PropertyAccess _:
+        // Struct or Union field.
+        final compound = receiver.target;
+        final type = compound?.staticType;
+        if (type?.isCompoundSubtype ?? false) {
+          return;
+        }
+      default:
+    }
+    _errorReporter.atNode(
+      errorNode,
+      FfiCode.ADDRESS_RECEIVER,
+    );
+  }
+
   void _validateAllocate(FunctionExpressionInvocation node) {
     var typeArgumentTypes = node.typeArgumentTypes;
     if (typeArgumentTypes == null || typeArgumentTypes.length != 1) {
@@ -1959,6 +2076,22 @@
     // forwarding factory instead of the forwarded constructor.
   }
 
+  /// @Native(isLeaf: true)
+  bool get isNativeLeaf {
+    final annotationValue = computeConstantValue();
+    final annotationType = annotationValue?.type; // Native<T>
+    if (annotationValue == null || annotationType is! InterfaceType) {
+      return false;
+    }
+    if (!annotationValue.isNative) {
+      return false;
+    }
+    return annotationValue
+            .getField(FfiVerifier._isLeafParamName)
+            ?.toBoolValue() ??
+        false;
+  }
+
   bool get isPacked {
     final element = this.element;
     return element is ConstructorElement &&
@@ -1973,6 +2106,44 @@
   }
 }
 
+extension on FunctionElement {
+  /// @Native(isLeaf: true) external function.
+  bool get isNativeLeaf {
+    for (final annotation in metadata) {
+      if (annotation.isNativeLeaf) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
+extension on MethodElement {
+  /// @Native(isLeaf: true) external function.
+  bool get isNativeLeaf {
+    for (final annotation in metadata) {
+      if (annotation.isNativeLeaf) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
+extension on MethodInvocation {
+  /// Calls @Native(isLeaf: true) external function.
+  bool get isNativeLeafInvocation {
+    final element = methodName.staticElement;
+    if (element is FunctionElement) {
+      return element.isNativeLeaf;
+    }
+    if (element is MethodElement) {
+      return element.isNativeLeaf;
+    }
+    return false;
+  }
+}
+
 extension on DartObject {
   bool get isDefaultAsset {
     return switch (type) {
@@ -2017,6 +2188,13 @@
     return element is ClassElement && element.supertype.isAbiSpecificInteger;
   }
 
+  bool get isAddressOfExtension {
+    final element = this;
+    return element is ExtensionElement &&
+        element.isFfiExtension &&
+        FfiVerifier._addressOfExtensionNames.contains(element.name);
+  }
+
   /// Return `true` if this represents the extension `AllocatorAlloc`.
   bool get isAllocatorExtension {
     var element = this;
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index d84470d..c3b667d 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -18697,6 +18697,15 @@
         const C();
       }
       ```
+  ADDRESS_POSITION:
+    problemMessage: "The '.address' expression can only be used as argument to a leaf native external call."
+    hasPublishedDocs: false
+    comment: No parameters.
+  ADDRESS_RECEIVER:
+    problemMessage: "The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field."
+    correctionMessage: Change the receiver of '.address' to one of the allowed kinds.
+    hasPublishedDocs: false
+    comment: No parameters.
   ANNOTATION_ON_POINTER_FIELD:
     problemMessage: "Fields in a struct class whose type is 'Pointer' shouldn't have any annotations."
     correctionMessage: Try removing the annotation.
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index f1cfa2f..8f78087 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -380,6 +380,8 @@
 FastaUsageShort/example: Fail
 FfiAbiSpecificIntegerInvalid/analyzerCode: Fail
 FfiAbiSpecificIntegerMappingInvalid/analyzerCode: Fail
+FfiAddressPosition/analyzerCode: Fail
+FfiAddressReceiver/analyzerCode: Fail
 FfiCompoundImplementsFinalizable/analyzerCode: Fail
 FfiCreateOfStructOrUnion/analyzerCode: Fail
 FfiDartTypeMismatch/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 1b33bae..d373ccf 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -5039,6 +5039,17 @@
   problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size."
   external: test/ffi_test.dart
 
+FfiAddressPosition:
+  # Used by dart:ffi
+  problemMessage: "The '.address' expression can only be used as argument to a leaf native external call."
+  external: test/ffi_test.dart
+
+FfiAddressReceiver:
+  # Used by dart:ffi
+  problemMessage: "The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field."
+  correctionMessage: "Change the receiver of '.address' to one of the allowed kinds."
+  external: test/ffi_test.dart
+
 FfiCreateOfStructOrUnion:
   # Used by dart:ffi
   problemMessage: "Subclasses of 'Struct' and 'Union' are backed by native memory, and can't be instantiated by a generative constructor. Try allocating it via allocation, or load from a 'Pointer'."
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index 08f4e15..92d5410 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -143,6 +143,7 @@
 trusttypes
 type's
 type3.#name
+typeddata
 typeof
 u
 unavailable
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 24141ae..e4155ef 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
@@ -55,8 +55,10 @@
   ffi::Allocator,
   ffi::AllocatorAlloc,
   ffi::Array,
+  ffi::ArrayAddress,
   ffi::ArrayArray,
   ffi::Bool,
+  ffi::BoolAddress,
   ffi::BoolArray,
   ffi::BoolPointer,
   ffi::Char,
@@ -65,28 +67,36 @@
   ffi::Dart_NativeMessageHandler,
   ffi::DefaultAsset,
   ffi::Double,
+  ffi::DoubleAddress,
   ffi::DoubleArray,
   ffi::DoublePointer,
   ffi::DynamicLibrary,
   ffi::DynamicLibraryExtension,
   ffi::Finalizable,
   ffi::Float,
+  ffi::Float32ListAddress,
+  ffi::Float64ListAddress,
   ffi::FloatArray,
   ffi::FloatPointer,
   ffi::Handle,
   ffi::Int,
   ffi::Int16,
   ffi::Int16Array,
+  ffi::Int16ListAddress,
   ffi::Int16Pointer,
   ffi::Int32,
   ffi::Int32Array,
+  ffi::Int32ListAddress,
   ffi::Int32Pointer,
   ffi::Int64,
   ffi::Int64Array,
+  ffi::Int64ListAddress,
   ffi::Int64Pointer,
   ffi::Int8,
   ffi::Int8Array,
+  ffi::Int8ListAddress,
   ffi::Int8Pointer,
+  ffi::IntAddress,
   ffi::IntPtr,
   ffi::Long,
   ffi::LongLong,
@@ -109,22 +119,28 @@
   ffi::Size,
   ffi::SizedNativeType,
   ffi::Struct,
+  ffi::StructAddress,
   ffi::StructArray,
   ffi::StructPointer,
   ffi::Uint16,
   ffi::Uint16Array,
+  ffi::Uint16ListAddress,
   ffi::Uint16Pointer,
   ffi::Uint32,
   ffi::Uint32Array,
+  ffi::Uint32ListAddress,
   ffi::Uint32Pointer,
   ffi::Uint64,
   ffi::Uint64Array,
+  ffi::Uint64ListAddress,
   ffi::Uint64Pointer,
   ffi::Uint8,
   ffi::Uint8Array,
+  ffi::Uint8ListAddress,
   ffi::Uint8Pointer,
   ffi::UintPtr,
   ffi::Union,
+  ffi::UnionAddress,
   ffi::UnionArray,
   ffi::UnionPointer,
   ffi::UnsignedChar,
@@ -150,8 +166,10 @@
   ffi::Allocator,
   ffi::AllocatorAlloc,
   ffi::Array,
+  ffi::ArrayAddress,
   ffi::ArrayArray,
   ffi::Bool,
+  ffi::BoolAddress,
   ffi::BoolArray,
   ffi::BoolPointer,
   ffi::Char,
@@ -160,28 +178,36 @@
   ffi::Dart_NativeMessageHandler,
   ffi::DefaultAsset,
   ffi::Double,
+  ffi::DoubleAddress,
   ffi::DoubleArray,
   ffi::DoublePointer,
   ffi::DynamicLibrary,
   ffi::DynamicLibraryExtension,
   ffi::Finalizable,
   ffi::Float,
+  ffi::Float32ListAddress,
+  ffi::Float64ListAddress,
   ffi::FloatArray,
   ffi::FloatPointer,
   ffi::Handle,
   ffi::Int,
   ffi::Int16,
   ffi::Int16Array,
+  ffi::Int16ListAddress,
   ffi::Int16Pointer,
   ffi::Int32,
   ffi::Int32Array,
+  ffi::Int32ListAddress,
   ffi::Int32Pointer,
   ffi::Int64,
   ffi::Int64Array,
+  ffi::Int64ListAddress,
   ffi::Int64Pointer,
   ffi::Int8,
   ffi::Int8Array,
+  ffi::Int8ListAddress,
   ffi::Int8Pointer,
+  ffi::IntAddress,
   ffi::IntPtr,
   ffi::Long,
   ffi::LongLong,
@@ -204,22 +230,28 @@
   ffi::Size,
   ffi::SizedNativeType,
   ffi::Struct,
+  ffi::StructAddress,
   ffi::StructArray,
   ffi::StructPointer,
   ffi::Uint16,
   ffi::Uint16Array,
+  ffi::Uint16ListAddress,
   ffi::Uint16Pointer,
   ffi::Uint32,
   ffi::Uint32Array,
+  ffi::Uint32ListAddress,
   ffi::Uint32Pointer,
   ffi::Uint64,
   ffi::Uint64Array,
+  ffi::Uint64ListAddress,
   ffi::Uint64Pointer,
   ffi::Uint8,
   ffi::Uint8Array,
+  ffi::Uint8ListAddress,
   ffi::Uint8Pointer,
   ffi::UintPtr,
   ffi::Union,
+  ffi::UnionAddress,
   ffi::UnionArray,
   ffi::UnionPointer,
   ffi::UnsignedChar,
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 35f769d..12fdbe8 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
@@ -55,8 +55,10 @@
   ffi::Allocator,
   ffi::AllocatorAlloc,
   ffi::Array,
+  ffi::ArrayAddress,
   ffi::ArrayArray,
   ffi::Bool,
+  ffi::BoolAddress,
   ffi::BoolArray,
   ffi::BoolPointer,
   ffi::Char,
@@ -65,28 +67,36 @@
   ffi::Dart_NativeMessageHandler,
   ffi::DefaultAsset,
   ffi::Double,
+  ffi::DoubleAddress,
   ffi::DoubleArray,
   ffi::DoublePointer,
   ffi::DynamicLibrary,
   ffi::DynamicLibraryExtension,
   ffi::Finalizable,
   ffi::Float,
+  ffi::Float32ListAddress,
+  ffi::Float64ListAddress,
   ffi::FloatArray,
   ffi::FloatPointer,
   ffi::Handle,
   ffi::Int,
   ffi::Int16,
   ffi::Int16Array,
+  ffi::Int16ListAddress,
   ffi::Int16Pointer,
   ffi::Int32,
   ffi::Int32Array,
+  ffi::Int32ListAddress,
   ffi::Int32Pointer,
   ffi::Int64,
   ffi::Int64Array,
+  ffi::Int64ListAddress,
   ffi::Int64Pointer,
   ffi::Int8,
   ffi::Int8Array,
+  ffi::Int8ListAddress,
   ffi::Int8Pointer,
+  ffi::IntAddress,
   ffi::IntPtr,
   ffi::Long,
   ffi::LongLong,
@@ -109,22 +119,28 @@
   ffi::Size,
   ffi::SizedNativeType,
   ffi::Struct,
+  ffi::StructAddress,
   ffi::StructArray,
   ffi::StructPointer,
   ffi::Uint16,
   ffi::Uint16Array,
+  ffi::Uint16ListAddress,
   ffi::Uint16Pointer,
   ffi::Uint32,
   ffi::Uint32Array,
+  ffi::Uint32ListAddress,
   ffi::Uint32Pointer,
   ffi::Uint64,
   ffi::Uint64Array,
+  ffi::Uint64ListAddress,
   ffi::Uint64Pointer,
   ffi::Uint8,
   ffi::Uint8Array,
+  ffi::Uint8ListAddress,
   ffi::Uint8Pointer,
   ffi::UintPtr,
   ffi::Union,
+  ffi::UnionAddress,
   ffi::UnionArray,
   ffi::UnionPointer,
   ffi::UnsignedChar,
@@ -150,8 +166,10 @@
   ffi::Allocator,
   ffi::AllocatorAlloc,
   ffi::Array,
+  ffi::ArrayAddress,
   ffi::ArrayArray,
   ffi::Bool,
+  ffi::BoolAddress,
   ffi::BoolArray,
   ffi::BoolPointer,
   ffi::Char,
@@ -160,28 +178,36 @@
   ffi::Dart_NativeMessageHandler,
   ffi::DefaultAsset,
   ffi::Double,
+  ffi::DoubleAddress,
   ffi::DoubleArray,
   ffi::DoublePointer,
   ffi::DynamicLibrary,
   ffi::DynamicLibraryExtension,
   ffi::Finalizable,
   ffi::Float,
+  ffi::Float32ListAddress,
+  ffi::Float64ListAddress,
   ffi::FloatArray,
   ffi::FloatPointer,
   ffi::Handle,
   ffi::Int,
   ffi::Int16,
   ffi::Int16Array,
+  ffi::Int16ListAddress,
   ffi::Int16Pointer,
   ffi::Int32,
   ffi::Int32Array,
+  ffi::Int32ListAddress,
   ffi::Int32Pointer,
   ffi::Int64,
   ffi::Int64Array,
+  ffi::Int64ListAddress,
   ffi::Int64Pointer,
   ffi::Int8,
   ffi::Int8Array,
+  ffi::Int8ListAddress,
   ffi::Int8Pointer,
+  ffi::IntAddress,
   ffi::IntPtr,
   ffi::Long,
   ffi::LongLong,
@@ -204,22 +230,28 @@
   ffi::Size,
   ffi::SizedNativeType,
   ffi::Struct,
+  ffi::StructAddress,
   ffi::StructArray,
   ffi::StructPointer,
   ffi::Uint16,
   ffi::Uint16Array,
+  ffi::Uint16ListAddress,
   ffi::Uint16Pointer,
   ffi::Uint32,
   ffi::Uint32Array,
+  ffi::Uint32ListAddress,
   ffi::Uint32Pointer,
   ffi::Uint64,
   ffi::Uint64Array,
+  ffi::Uint64ListAddress,
   ffi::Uint64Pointer,
   ffi::Uint8,
   ffi::Uint8Array,
+  ffi::Uint8ListAddress,
   ffi::Uint8Pointer,
   ffi::UintPtr,
   ffi::Union,
+  ffi::UnionAddress,
   ffi::UnionArray,
   ffi::UnionPointer,
   ffi::UnsignedChar,
diff --git a/pkg/vm/lib/modular/transformations/ffi/common.dart b/pkg/vm/lib/modular/transformations/ffi/common.dart
index b77d3af..2b512b2 100644
--- a/pkg/vm/lib/modular/transformations/ffi/common.dart
+++ b/pkg/vm/lib/modular/transformations/ffi/common.dart
@@ -137,6 +137,37 @@
   NativeType.kDouble,
 ];
 
+const List<String> addressOfExtensionsTypedData = [
+  'Float32List',
+  'Float64List',
+  'Int16List',
+  'Int32List',
+  'Int64List',
+  'Int8List',
+  'Uint16List',
+  'Uint32List',
+  'Uint64List',
+  'Uint8List',
+];
+
+const List<String> addressOfExtensionsCompound = [
+  'Array',
+  'Struct',
+  'Union',
+];
+
+const List<String> addressOfExtensionsPrimitive = [
+  'Bool',
+  'Double',
+  'Int',
+];
+
+const List<String> addressOfExtensions = [
+  ...addressOfExtensionsCompound,
+  ...addressOfExtensionsPrimitive,
+  ...addressOfExtensionsTypedData,
+];
+
 enum FfiTypeCheckDirection {
   // Passing a value from native code to Dart code.
   nativeToDart,
@@ -258,6 +289,7 @@
   final Procedure arrayNestedDimensionsRest;
   final Procedure structCreate;
   final Procedure unionCreate;
+  final Constructor compoundFromTypedDataBase;
   final Constructor structFromTypedDataBase;
   final Constructor unionFromTypedDataBase;
   final Constructor structFromTypedData;
@@ -301,8 +333,13 @@
   final Field nativeCallablePointerField;
   final Procedure nativeAddressOf;
   final Procedure nativePrivateAddressOf;
+  final List<Procedure> addressOfMethods;
+  final List<Procedure> addressOfMethodsCompound;
+  final List<Procedure> addressOfMethodsPrimitive;
+  final List<Procedure> addressOfMethodsTypedData;
   final Class ffiCallClass;
   final Field ffiCallIsLeafField;
+  final Field nativeIsLeafField;
 
   late final InterfaceType nativeFieldWrapperClass1Type;
   late final InterfaceType voidType;
@@ -311,6 +348,7 @@
   late final InterfaceType nativeTypeType;
   // The Pointer type when instantiated to bounds.
   late final InterfaceType pointerNativeTypeType;
+  late final InterfaceType compoundType;
 
   /// Classes corresponding to [NativeType], indexed by [NativeType].
   final Map<NativeType, Class> nativeTypesClasses;
@@ -426,6 +464,8 @@
             'dart:ffi', 'Array', 'get:_nestedDimensionsRest'),
         structCreate = index.getProcedure('dart:ffi', 'Struct', 'create'),
         unionCreate = index.getProcedure('dart:ffi', 'Union', 'create'),
+        compoundFromTypedDataBase =
+            index.getConstructor('dart:ffi', '_Compound', '_fromTypedDataBase'),
         structFromTypedDataBase =
             index.getConstructor('dart:ffi', 'Struct', '_fromTypedDataBase'),
         unionFromTypedDataBase =
@@ -591,8 +631,25 @@
             index.getMember('dart:ffi', 'Native', 'addressOf') as Procedure,
         nativePrivateAddressOf =
             index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure,
+        addressOfMethods = [
+          for (final name in addressOfExtensions)
+            index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
+        ],
+        addressOfMethodsPrimitive = [
+          for (final name in addressOfExtensionsPrimitive)
+            index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
+        ],
+        addressOfMethodsCompound = [
+          for (final name in addressOfExtensionsCompound)
+            index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
+        ],
+        addressOfMethodsTypedData = [
+          for (final name in addressOfExtensionsTypedData)
+            index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
+        ],
         ffiCallClass = index.getClass('dart:ffi', '_FfiCall'),
-        ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf') {
+        ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf'),
+        nativeIsLeafField = index.getField('dart:ffi', 'Native', 'isLeaf') {
     nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
         coreTypes, Nullability.nonNullable);
     voidType = nativeTypesClasses[NativeType.kVoid]!
@@ -606,6 +663,11 @@
     intptrNativeTypeCfe =
         NativeTypeCfe(this, InterfaceType(intptrClass, Nullability.nonNullable))
             as AbiSpecificNativeTypeCfe;
+    compoundType = InterfaceType(
+      compoundClass,
+      Nullability.nonNullable,
+      const <DartType>[],
+    );
   }
 
   @override
diff --git a/pkg/vm/lib/modular/transformations/ffi/native.dart b/pkg/vm/lib/modular/transformations/ffi/native.dart
index 6c46423..1a4eb3f 100644
--- a/pkg/vm/lib/modular/transformations/ffi/native.dart
+++ b/pkg/vm/lib/modular/transformations/ffi/native.dart
@@ -62,7 +62,6 @@
   final Field assetAssetField;
   final Field nativeSymbolField;
   final Field nativeAssetField;
-  final Field nativeIsLeafField;
   final Field resolverField;
 
   StringConstant? currentAsset;
@@ -83,7 +82,6 @@
         assetAssetField = index.getField('dart:ffi', 'DefaultAsset', 'id'),
         nativeSymbolField = index.getField('dart:ffi', 'Native', 'symbol'),
         nativeAssetField = index.getField('dart:ffi', 'Native', 'assetId'),
-        nativeIsLeafField = index.getField('dart:ffi', 'Native', 'isLeaf'),
         resolverField = index.getField('dart:ffi', 'Native', '_ffi_resolver'),
         super(index, coreTypes, hierarchy, diagnosticReporter,
             referenceFromIndex);
diff --git a/pkg/vm/lib/modular/transformations/ffi/use_sites.dart b/pkg/vm/lib/modular/transformations/ffi/use_sites.dart
index b9a0008..ae5fb34 100644
--- a/pkg/vm/lib/modular/transformations/ffi/use_sites.dart
+++ b/pkg/vm/lib/modular/transformations/ffi/use_sites.dart
@@ -5,11 +5,13 @@
 import 'package:front_end/src/fasta/codes/fasta_codes.dart'
     show
         messageFfiAddressOfMustBeNative,
+        messageFfiAddressPosition,
+        messageFfiAddressReceiver,
         messageFfiCreateOfStructOrUnion,
         messageFfiExceptionalReturnNull,
         messageFfiExpectedConstant,
-        templateFfiNativeCallableListenerReturnVoid,
         templateFfiDartTypeMismatch,
+        templateFfiNativeCallableListenerReturnVoid,
         templateFfiExpectedConstantArg,
         templateFfiExpectedExceptionalReturn,
         templateFfiExpectedNoExceptionalReturn,
@@ -17,6 +19,7 @@
         templateFfiNotStatic;
 import 'package:kernel/ast.dart';
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/clone.dart';
 import 'package:kernel/constructor_tearoff_lowering.dart';
 import 'package:kernel/core_types.dart';
 import 'package:kernel/kernel.dart';
@@ -138,6 +141,7 @@
     final target = node.target;
     if (hierarchy.isSubclassOf(target.enclosingClass, compoundClass) &&
         target.enclosingClass != arrayClass &&
+        target.enclosingClass != compoundClass &&
         target.name != Name("#fromTypedDataBase") &&
         target.name != Name("#fromTypedData")) {
       diagnosticReporter.report(messageFfiCreateOfStructOrUnion,
@@ -171,9 +175,9 @@
       return ConstructorInvocation(
         constructors.firstWhere((c) => c.name == Name("#fromTypedData")),
         Arguments([
-          (defaultExpression(node.arguments.positional.first) as Expression),
+          node.arguments.positional.first,
           (positionalArguments.length >= 2
-              ? defaultExpression(positionalArguments[1]) as Expression
+              ? positionalArguments[1]
               : ConstantExpression(IntConstant(0))),
           // Length in bytes to check the typedData against.
           sizeOfExpression,
@@ -525,6 +529,18 @@
         return _transformCompoundCreate(node);
       } else if (target == nativeAddressOf) {
         return _replaceNativeAddressOf(node);
+      } else if (addressOfMethods.contains(target)) {
+        // The AST is visited recursively down. Handling of native invocations
+        // will inspect arguments for `<expr>.address` invocations. Any
+        // remaining invocations occur are places where `<expr>.address` is
+        // disallowed, so issue an error.
+        diagnosticReporter.report(
+            messageFfiAddressPosition, node.fileOffset, 1, node.location?.file);
+      } else {
+        final nativeAnnotation = memberGetNativeAnnotation(target);
+        if (nativeAnnotation != null && _isLeaf(nativeAnnotation)) {
+          return _replaceNativeCall(node, nativeAnnotation);
+        }
       }
     } on FfiStaticTypeError {
       // It's OK to swallow the exception because the diagnostics issued will
@@ -535,6 +551,12 @@
     return node;
   }
 
+  bool _isLeaf(InstanceConstant native) {
+    return (native.fieldValues[nativeIsLeafField.fieldReference]
+            as BoolConstant)
+        .value;
+  }
+
   /// Create Dart function which calls native code.
   ///
   /// Adds a native effect invoking a compound constructors if this is used
@@ -1340,24 +1362,8 @@
       StaticGet(:var targetReference) => targetReference.asMember,
       _ => null,
     };
-    Constant? nativeAnnotation;
-
-    if (potentiallyNativeTarget != null) {
-      for (final annotation in potentiallyNativeTarget.annotations) {
-        if (annotation
-            case ConstantExpression(constant: final InstanceConstant c)) {
-          if (c.classNode == coreTypes.pragmaClass) {
-            final name = c.fieldValues[coreTypes.pragmaName.fieldReference];
-            if (name is StringConstant &&
-                name.value == native.FfiNativeTransformer.nativeMarker) {
-              nativeAnnotation =
-                  c.fieldValues[coreTypes.pragmaOptions.fieldReference]!;
-              break;
-            }
-          }
-        }
-      }
-    }
+    Constant? nativeAnnotation =
+        memberGetNativeAnnotation(potentiallyNativeTarget);
 
     if (nativeAnnotation == null) {
       diagnosticReporter.report(messageFfiAddressOfMustBeNative, arg.fileOffset,
@@ -1376,6 +1382,347 @@
       Arguments([ConstantExpression(nativeAnnotation)], types: [nativeType]),
     )..fileOffset = arg.fileOffset;
   }
+
+  InstanceConstant? memberGetNativeAnnotation(Member? member) {
+    if (member == null) {
+      return null;
+    }
+    for (final annotation in member.annotations) {
+      if (annotation
+          case ConstantExpression(constant: final InstanceConstant c)) {
+        if (c.classNode == coreTypes.pragmaClass) {
+          final name = c.fieldValues[coreTypes.pragmaName.fieldReference];
+          if (name is StringConstant &&
+              name.value == native.FfiNativeTransformer.nativeMarker) {
+            return c.fieldValues[coreTypes.pragmaOptions.fieldReference]
+                as InstanceConstant;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  StaticInvocation _replaceNativeCall(
+    StaticInvocation node,
+    InstanceConstant targetNativeAnnotation,
+  ) {
+    final target = node.target;
+    if (targetNativeAnnotation.typeArguments.length != 1) {
+      return node;
+    }
+    final annotationType = targetNativeAnnotation.typeArguments.single;
+    if (annotationType is! FunctionType) {
+      return node;
+    }
+    final parameterTypes = [
+      for (final varDecl in target.function.positionalParameters) varDecl.type,
+    ];
+    final numParams = parameterTypes.length;
+    String methodPostfix = '';
+    final newArguments = <Expression>[];
+    final newParameters = <VariableDeclaration>[];
+    bool isTransformed = false;
+    for (int i = 0; i < numParams; i++) {
+      final parameter = target.function.positionalParameters[i];
+      final parameterType = parameterTypes[i];
+      final argument = node.arguments.positional[i];
+      final (
+        postFix,
+        newType,
+        newArgument,
+      ) = _replaceNativeCallParameterAndArgument(
+        parameter,
+        parameterType,
+        argument,
+        node.fileOffset,
+      );
+      methodPostfix += postFix;
+      if (postFix == 'C' || postFix == 'E' || postFix == 'T') {
+        isTransformed = true;
+      }
+      newParameters.add(VariableDeclaration(
+        parameter.name,
+        type: newType,
+      ));
+      newArguments.add(newArgument);
+    }
+
+    if (!isTransformed) {
+      return node;
+    }
+
+    final newName = '${target.name.text}#$methodPostfix';
+    final Procedure newTarget;
+    final parent = target.parent;
+    final members = switch (parent) {
+      Library _ => parent.members,
+      Class _ => parent.members,
+      _ => throw UnimplementedError('Unexpected parent: ${parent}'),
+    };
+
+    final existingNewTarget = members
+        .whereType<Procedure>()
+        .where((element) => element.name.text == newName)
+        .firstOrNull;
+    if (existingNewTarget != null) {
+      newTarget = existingNewTarget;
+    } else {
+      final cloner = CloneProcedureWithoutBody();
+      newTarget = cloner.cloneProcedure(target, null);
+      newTarget.name = Name(newName);
+      newTarget.function.positionalParameters = newParameters;
+      setParents(newParameters, newTarget.function);
+      switch (parent) {
+        case Library _:
+          parent.addProcedure(newTarget);
+        case Class _:
+          parent.addProcedure(newTarget);
+      }
+    }
+
+    return StaticInvocation(
+      newTarget,
+      Arguments(newArguments),
+    );
+  }
+
+  /// Converts a single parameter with argument for [_replaceNativeCall].
+  (
+    /// '' for non-Pointer.
+    /// 'P' for Pointer.
+    /// 'T' for TypedData.
+    /// 'C' for _Compound (TypedData/Pointer and offset in bytes).
+    /// 'E' for errors.
+    String methodPostFix,
+    DartType parameterType,
+    Expression argument,
+  ) _replaceNativeCallParameterAndArgument(
+    VariableDeclaration parameter,
+    DartType parameterType,
+    Expression argument,
+    int fileOffset,
+  ) {
+    if (parameterType is! InterfaceType ||
+        parameterType.classNode != pointerClass) {
+      // Parameter is non-pointer. Keep unchanged.
+      return ('', parameterType, argument);
+    }
+
+    if (argument is! StaticInvocation ||
+        !addressOfMethods.contains(argument.target)) {
+      // The argument has type Pointer, but it's not produced by any of the
+      // `.address` getters.
+      // Argument must be a Pointer object. Keep unchanged.
+      return ('P', parameterType, argument);
+    }
+
+    if (addressOfMethodsTypedData.contains(argument.target)) {
+      final subExpression = argument.arguments.positional.single;
+      // Argument is `typedData.address`.
+      final typedDataType = InterfaceType(
+        typedDataClass,
+        Nullability.nonNullable,
+        const <DartType>[],
+      );
+      return ('T', typedDataType, subExpression);
+    }
+
+    final subExpression = argument.arguments.positional.single;
+
+    if (addressOfMethodsCompound.contains(argument.target)) {
+      // Argument is `structOrUnionOrArray.address`.
+      return ('C', compoundType, subExpression);
+    }
+
+    assert(addressOfMethodsPrimitive.contains(argument.target));
+    // Argument is an `expr.address` where `expr` is typed `bool`, `int`, or
+    // `double`. Analyze `expr` further.
+    switch (subExpression) {
+      case InstanceGet _:
+        // Look for `structOrUnion.member.address`.
+        final interfaceTarget = subExpression.interfaceTarget;
+        final enclosingClass = interfaceTarget.enclosingClass!;
+        final targetSuperClass = enclosingClass.superclass;
+        if (targetSuperClass == unionClass) {
+          // `expr` is a union member access.
+          // Union members have no additional offset. Pass in unchanged.
+          return ('C', compoundType, subExpression.receiver);
+        }
+        if (targetSuperClass == structClass) {
+          final getterName = interfaceTarget.name.text;
+          final offsetOfName = '$getterName#offsetOf';
+          final offsetGetter = enclosingClass.procedures
+              .firstWhere((e) => e.name.text == offsetOfName);
+          // `expr` is a struct member access. Struct members have an offset.
+          // Pass in a newly constructed `_Compound`, with adjusted offset.
+          return (
+            'C',
+            compoundType,
+            _generateCompoundOffsetBy(
+              subExpression.receiver,
+              StaticGet(offsetGetter),
+              fileOffset,
+              variableName: "${parameter.name}#value",
+            ),
+          );
+        }
+      // Error, unrecognized getter.
+
+      case StaticInvocation _:
+        // Look for `array[index].address`.
+        // Extensions have already been desugared, so no enclosing extension.
+        final target = subExpression.target;
+        if (!target.isExtensionMember) break;
+        final positionalParameters = target.function.positionalParameters;
+        if (positionalParameters.length != 2) break;
+        final firstParamType = positionalParameters.first.type;
+        if (firstParamType is! InterfaceType) break;
+        if (firstParamType.classNode != arrayClass) break;
+        // Extensions have already been desugared, so original name is lost.
+        if (!target.name.text.endsWith('|[]')) break;
+        final DartType arrayElementType;
+        if (subExpression.arguments.types.isNotEmpty) {
+          // AbiSpecificInteger.
+          arrayElementType = subExpression.arguments.types.single;
+        } else {
+          arrayElementType = firstParamType.typeArguments.single;
+        }
+        final arrayElementSize =
+            inlineSizeOf(arrayElementType as InterfaceType)!;
+        // Array element. Pass in a newly constructed `_Compound`, with
+        // adjusted offset.
+        return (
+          'C',
+          compoundType,
+          _generateCompoundOffsetBy(
+            subExpression.arguments.positional[0],
+            multiply(
+              arrayElementSize,
+              subExpression.arguments.positional[1], // index.
+            ),
+            fileOffset,
+            variableName: "${parameter.name}#value",
+          ),
+        );
+
+      case InstanceInvocation _:
+        // Look for `typedData[index].address`
+        final receiverType =
+            staticTypeContext!.getExpressionType(subExpression.receiver);
+        final implementsTypedData = TypeEnvironment(coreTypes, hierarchy)
+            .isSubtypeOf(
+                receiverType,
+                InterfaceType(typedDataClass, Nullability.nonNullable),
+                SubtypeCheckMode.withNullabilities);
+        if (!implementsTypedData) break;
+        if (receiverType is! InterfaceType) break;
+        final classNode = receiverType.classNode;
+        final elementSizeInBytes = _typedDataElementSizeInBytes(classNode);
+        if (elementSizeInBytes == null) break;
+
+        // Typed Data element off. Pass in new _Compound with extra
+        // offset.
+        return (
+          'C',
+          compoundType,
+          ConstructorInvocation(
+            compoundFromTypedDataBase,
+            Arguments([
+              subExpression.receiver,
+              multiply(
+                ConstantExpression(IntConstant(elementSizeInBytes)),
+                subExpression.arguments.positional.first, // index.
+              ),
+            ]),
+          ),
+        );
+      default:
+    }
+
+    diagnosticReporter.report(
+      messageFfiAddressReceiver,
+      argument.fileOffset,
+      1,
+      argument.location?.file,
+    );
+    // Pass nullptr to prevent cascading error messages.
+    return (
+      'E', // Error.
+      pointerVoidType,
+      StaticInvocation(
+        fromAddressInternal,
+        Arguments(
+          <Expression>[ConstantExpression(IntConstant(0))],
+          types: <DartType>[voidType],
+        ),
+      ),
+    );
+  }
+
+  int? _typedDataElementSizeInBytes(Class classNode) {
+    final name = classNode.name;
+    if (name.contains('8')) {
+      return 1;
+    } else if (name.contains('16')) {
+      return 2;
+    } else if (name.contains('32')) {
+      return 4;
+    } else if (name.contains('64')) {
+      return 8;
+    }
+    return null;
+  }
+
+  /// Returns:
+  ///
+  /// ```
+  /// _Compound._fromTypedDataBase(
+  ///   compound._typedDataBase,
+  ///   compound._offsetInBytes + offsetInBytes,
+  /// )
+  /// ```
+  Expression _generateCompoundOffsetBy(
+    Expression compound,
+    Expression offsetInBytes,
+    int fileOffset, {
+    String variableName = "#compoundOffset",
+  }) {
+    final compoundType = InterfaceType(
+      compoundClass,
+      Nullability.nonNullable,
+      const <DartType>[],
+    );
+
+    final valueVar = VariableDeclaration(
+      variableName,
+      initializer: compound,
+      type: compoundType,
+      isSynthesized: true,
+    )..fileOffset = fileOffset;
+    final newArgument = BlockExpression(
+      Block([
+        valueVar,
+      ]),
+      ConstructorInvocation(
+        compoundFromTypedDataBase,
+        Arguments([
+          getCompoundTypedDataBaseField(
+            VariableGet(valueVar),
+            fileOffset,
+          ),
+          add(
+            getCompoundOffsetInBytesField(
+              VariableGet(valueVar),
+              fileOffset,
+            ),
+            offsetInBytes,
+          ),
+        ]),
+      ),
+    );
+    return newArgument;
+  }
 }
 
 extension<T extends Object> on List<T> {
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_struct.dart b/pkg/vm/testcases/transformations/ffi/address_of_struct.dart
new file mode 100644
index 0000000..9cc5170
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_struct.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2024, 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.
+
+// @dart=3.5
+
+import 'dart:ffi';
+
+void main() {
+  // All of these pass a `_Compound`.
+  final myStruct = Struct.create<MyStruct>();
+  myNative(myStruct.address);
+
+  final myUnion = Union.create<MyUnion>();
+  myNative2(myUnion.address);
+
+  myNative3(myStruct.a.address);
+}
+
+@Native<Void Function(Pointer<MyStruct>)>(isLeaf: true)
+external void myNative(Pointer<MyStruct> pointer);
+
+final class MyStruct extends Struct {
+  @Array(10)
+  external Array<Int8> a;
+}
+
+@Native<Void Function(Pointer<MyUnion>)>(isLeaf: true)
+external void myNative2(Pointer<MyUnion> pointer);
+
+final class MyUnion extends Union {
+  @Int8()
+  external int a;
+}
+
+@Native<Void Function(Pointer<Int8>)>(isLeaf: true)
+external void myNative3(Pointer<Int8> pointer);
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_struct.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/address_of_struct.dart.aot.expect
new file mode 100644
index 0000000..80f1235
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_struct.dart.aot.expect
@@ -0,0 +1,92 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+
+import "dart:ffi";
+
+@#C8
+final class MyStruct extends ffi::Struct {
+  constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyStruct
+    : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+    ;
+
+  [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
+  get a() → ffi::Array<ffi::Int8>
+    return new ffi::Array::_<ffi::Int8>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::MyStruct::a#offsetOf.{core::num::+}([@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C9);
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C11
+  static get a#offsetOf() → core::int
+    return #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C11
+  static get #sizeOf() → core::int*
+    return #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C17
+final class MyUnion extends ffi::Union {
+  constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyUnion
+    : super ffi::Union::_fromTypedDataBase(#typedDataBase)
+    ;
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C11
+  static get #sizeOf() → core::int*
+    return #C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+static method main() → void {
+  final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 10)] self::MyStruct::#sizeOf));
+  self::myNative#C(myStruct);
+  final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::MyUnion::#sizeOf));
+  self::myNative2#C(myUnion);
+  self::myNative3#C([@vm.direct-call.metadata=#lib::MyStruct.a] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::Int8>] myStruct.{self::MyStruct::a}{ffi::Array<ffi::Int8>});
+}
+@#C25
+@#C27
+external static method myNative#C([@vm.inferred-arg-type.metadata=#lib::MyStruct] ffi::_Compound pointer) → void;
+@#C30
+@#C31
+external static method myNative2#C([@vm.inferred-arg-type.metadata=#lib::MyUnion] ffi::_Compound pointer) → void;
+@#C34
+@#C35
+external static method myNative3#C([@vm.inferred-arg-type.metadata=dart.ffi::Array<dart.ffi::Int8>] ffi::_Compound pointer) → void;
+constants  {
+  #C1 = "vm:ffi:struct-fields"
+  #C2 = TypeLiteralConstant(ffi::Int8)
+  #C3 = 10
+  #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
+  #C5 = <core::Type>[#C4]
+  #C6 = null
+  #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+  #C8 = core::pragma {name:#C1, options:#C7}
+  #C9 = <core::int*>[]
+  #C10 = "vm:prefer-inline"
+  #C11 = core::pragma {name:#C10, options:#C6}
+  #C12 = 0
+  #C13 = <core::int*>[#C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12]
+  #C14 = <core::int*>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
+  #C15 = <core::Type>[#C2]
+  #C16 = ffi::_FfiStructLayout {fieldTypes:#C15, packing:#C6}
+  #C17 = core::pragma {name:#C1, options:#C16}
+  #C18 = 1
+  #C19 = <core::int*>[#C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18]
+  #C20 = "cfe:ffi:native-marker"
+  #C21 = "myNative"
+  #C22 = "#lib"
+  #C23 = true
+  #C24 = ffi::Native<(ffi::Pointer<self::MyStruct>) → ffi::Void> {symbol:#C21, assetId:#C22, isLeaf:#C23}
+  #C25 = core::pragma {name:#C20, options:#C24}
+  #C26 = "vm:ffi:native"
+  #C27 = core::pragma {name:#C26, options:#C24}
+  #C28 = "myNative2"
+  #C29 = ffi::Native<(ffi::Pointer<self::MyUnion>) → ffi::Void> {symbol:#C28, assetId:#C22, isLeaf:#C23}
+  #C30 = core::pragma {name:#C20, options:#C29}
+  #C31 = core::pragma {name:#C26, options:#C29}
+  #C32 = "myNative3"
+  #C33 = ffi::Native<(ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C32, assetId:#C22, isLeaf:#C23}
+  #C34 = core::pragma {name:#C20, options:#C33}
+  #C35 = core::pragma {name:#C26, options:#C33}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_struct.dart.expect b/pkg/vm/testcases/transformations/ffi/address_of_struct.dart.expect
new file mode 100644
index 0000000..a1af983
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_struct.dart.expect
@@ -0,0 +1,120 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+
+import "dart:ffi";
+
+@#C8
+final class MyStruct extends ffi::Struct {
+  synthetic constructor •() → self::MyStruct
+    : super ffi::Struct::•()
+    ;
+  constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyStruct
+    : super ffi::Struct::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
+    ;
+  constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyStruct
+    : super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
+    ;
+  @#C9
+  get a() → ffi::Array<ffi::Int8>
+    return new ffi::Array::_<ffi::Int8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C10);
+  @#C9
+  set a(synthesized ffi::Array<ffi::Int8> #externalFieldValue) → void
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+  @#C13
+  static get a#offsetOf() → core::int
+    return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C13
+  static get #sizeOf() → core::int*
+    return #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C18
+final class MyUnion extends ffi::Union {
+  synthetic constructor •() → self::MyUnion
+    : super ffi::Union::•()
+    ;
+  constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyUnion
+    : super ffi::Union::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
+    ;
+  constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyUnion
+    : super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
+    ;
+  @#C19
+  get a() → core::int
+    return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
+  @#C19
+  set a(synthesized core::int #externalFieldValue) → void
+    return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
+  @#C13
+  static get a#offsetOf() → core::int
+    return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C13
+  static get #sizeOf() → core::int*
+    return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+static method main() → void {
+  final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase(typ::Uint8List::•(self::MyStruct::#sizeOf), #C14);
+  self::myNative#C(myStruct);
+  final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C14);
+  self::myNative2#C(myUnion);
+  self::myNative3#C(myStruct.{self::MyStruct::a}{ffi::Array<ffi::Int8>});
+}
+@#C27
+@#C29
+external static method myNative(ffi::Pointer<self::MyStruct> pointer) → void;
+@#C32
+@#C33
+external static method myNative2(ffi::Pointer<self::MyUnion> pointer) → void;
+@#C36
+@#C37
+external static method myNative3(ffi::Pointer<ffi::Int8> pointer) → void;
+@#C27
+@#C29
+external static method myNative#C(ffi::_Compound pointer) → void;
+@#C32
+@#C33
+external static method myNative2#C(ffi::_Compound pointer) → void;
+@#C36
+@#C37
+external static method myNative3#C(ffi::_Compound pointer) → void;
+constants  {
+  #C1 = "vm:ffi:struct-fields"
+  #C2 = TypeLiteralConstant(ffi::Int8)
+  #C3 = 10
+  #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
+  #C5 = <core::Type>[#C4]
+  #C6 = null
+  #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+  #C8 = core::pragma {name:#C1, options:#C7}
+  #C9 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
+  #C10 = <core::int*>[]
+  #C11 = <core::int*>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
+  #C12 = "vm:prefer-inline"
+  #C13 = core::pragma {name:#C12, options:#C6}
+  #C14 = 0
+  #C15 = <core::int*>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
+  #C16 = <core::Type>[#C2]
+  #C17 = ffi::_FfiStructLayout {fieldTypes:#C16, packing:#C6}
+  #C18 = core::pragma {name:#C1, options:#C17}
+  #C19 = ffi::Int8 {}
+  #C20 = 1
+  #C21 = <core::int*>[#C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20]
+  #C22 = "cfe:ffi:native-marker"
+  #C23 = "myNative"
+  #C24 = "#lib"
+  #C25 = true
+  #C26 = ffi::Native<(ffi::Pointer<self::MyStruct>) → ffi::Void> {symbol:#C23, assetId:#C24, isLeaf:#C25}
+  #C27 = core::pragma {name:#C22, options:#C26}
+  #C28 = "vm:ffi:native"
+  #C29 = core::pragma {name:#C28, options:#C26}
+  #C30 = "myNative2"
+  #C31 = ffi::Native<(ffi::Pointer<self::MyUnion>) → ffi::Void> {symbol:#C30, assetId:#C24, isLeaf:#C25}
+  #C32 = core::pragma {name:#C22, options:#C31}
+  #C33 = core::pragma {name:#C28, options:#C31}
+  #C34 = "myNative3"
+  #C35 = ffi::Native<(ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C34, assetId:#C24, isLeaf:#C25}
+  #C36 = core::pragma {name:#C22, options:#C35}
+  #C37 = core::pragma {name:#C28, options:#C35}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart b/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart
new file mode 100644
index 0000000..d4912e7
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2024, 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.
+
+// @dart=3.5
+
+import 'dart:ffi';
+
+void main() {
+  // This should create a `_Compound` with the right offset.
+  final myStruct = Struct.create<MyStruct>();
+  myNative(
+    myStruct.a.address,
+    myStruct.b.address,
+  );
+
+  // Unions do not need to create a view with an offset.
+  final myUnion = Union.create<MyUnion>();
+  myNative(
+    myUnion.a.address,
+    myUnion.b.address,
+  );
+
+  // This should create a `_Compound` with the right offset.
+  myNative(
+    myStruct.array[3].address,
+    myStruct.array[4].address,
+  );
+
+  // This should create a `_Compound` with the right offset.
+  myNative(
+    myStruct.array2[3].address,
+    myStruct.array2[4].address,
+  );
+}
+
+@Native<
+    Void Function(
+      Pointer<Int8>,
+      Pointer<Int8>,
+    )>(isLeaf: true)
+external void myNative(
+  Pointer<Int8> pointer,
+  Pointer<Int8> pointer2,
+);
+
+final class MyStruct extends Struct {
+  @Int8()
+  external int a;
+
+  @Int8()
+  external int b;
+
+  @Array(10)
+  external Array<Int8> array;
+
+  @Array(10)
+  external Array<UnsignedLong> array2;
+}
+
+final class MyUnion extends Union {
+  @Int8()
+  external int a;
+
+  @Int8()
+  external int b;
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart.aot.expect
new file mode 100644
index 0000000..456a432
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart.aot.expect
@@ -0,0 +1,119 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+
+import "dart:ffi";
+
+@#C10
+final class MyStruct extends ffi::Struct {
+  constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyStruct
+    : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+    ;
+
+  [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
+  get array() → ffi::Array<ffi::Int8>
+    return new ffi::Array::_<ffi::Int8>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi (value: 2)] self::MyStruct::array#offsetOf.{core::num::+}([@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C11);
+
+  [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2]
+  get array2() → ffi::Array<ffi::UnsignedLong>
+    return new ffi::Array::_<ffi::UnsignedLong>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] self::MyStruct::array2#offsetOf.{core::num::+}([@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C11);
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C13
+  static get a#offsetOf() → core::int
+    return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C13
+  static get b#offsetOf() → core::int
+    return #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C13
+  static get array#offsetOf() → core::int
+    return #C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C13
+  static get array2#offsetOf() → core::int
+    return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C13
+  static get #sizeOf() → core::int*
+    return #C25.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C28
+final class MyUnion extends ffi::Union {
+  constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyUnion
+    : super ffi::Union::_fromTypedDataBase(#typedDataBase)
+    ;
+
+  [@vm.unboxing-info.metadata=()->i]
+  @#C13
+  static get #sizeOf() → core::int*
+    return #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+static method main() → void {
+  final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi] self::MyStruct::#sizeOf));
+  self::myNative#CC( block {
+    synthesized ffi::_Compound pointer#value = myStruct;
+  } =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::MyStruct::a#offsetOf){(core::num) → core::num}), block {
+    synthesized ffi::_Compound pointer2#value = myStruct;
+  } =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::MyStruct::b#offsetOf){(core::num) → core::num}));
+  final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::MyUnion::#sizeOf));
+  self::myNative#CC(myUnion, myUnion);
+  self::myNative#CC( block {
+    synthesized ffi::_Compound pointer#value = [@vm.direct-call.metadata=#lib::MyStruct.array] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::Int8>] myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
+  } =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C16.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
+    synthesized ffi::_Compound pointer2#value = [@vm.direct-call.metadata=#lib::MyStruct.array] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::Int8>] myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
+  } =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C16.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
+  self::myNative#CC( block {
+    synthesized ffi::_Compound pointer#value = [@vm.direct-call.metadata=#lib::MyStruct.array2] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::UnsignedLong>] myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
+  } =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] ffi::UnsignedLong::#sizeOf.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
+    synthesized ffi::_Compound pointer2#value = [@vm.direct-call.metadata=#lib::MyStruct.array2] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::UnsignedLong>] myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
+  } =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] ffi::UnsignedLong::#sizeOf.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
+}
+@#C34
+@#C36
+external static method myNative#CC([@vm.inferred-arg-type.metadata=!] ffi::_Compound pointer, [@vm.inferred-arg-type.metadata=!] ffi::_Compound pointer2) → void;
+constants  {
+  #C1 = "vm:ffi:struct-fields"
+  #C2 = TypeLiteralConstant(ffi::Int8)
+  #C3 = 10
+  #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
+  #C5 = TypeLiteralConstant(ffi::UnsignedLong)
+  #C6 = ffi::_FfiInlineArray {elementType:#C5, length:#C3}
+  #C7 = <core::Type>[#C2, #C2, #C4, #C6]
+  #C8 = null
+  #C9 = ffi::_FfiStructLayout {fieldTypes:#C7, packing:#C8}
+  #C10 = core::pragma {name:#C1, options:#C9}
+  #C11 = <core::int*>[]
+  #C12 = "vm:prefer-inline"
+  #C13 = core::pragma {name:#C12, options:#C8}
+  #C14 = 0
+  #C15 = <core::int*>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
+  #C16 = 1
+  #C17 = <core::int*>[#C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16]
+  #C18 = 2
+  #C19 = <core::int*>[#C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18]
+  #C20 = 12
+  #C21 = 16
+  #C22 = <core::int*>[#C20, #C21, #C20, #C21, #C21, #C21, #C21, #C21, #C20, #C21, #C21, #C20, #C21, #C20, #C21, #C20, #C21, #C21, #C21, #C20, #C20, #C20]
+  #C23 = 52
+  #C24 = 96
+  #C25 = <core::int*>[#C23, #C24, #C23, #C24, #C24, #C24, #C24, #C24, #C23, #C24, #C24, #C23, #C24, #C23, #C24, #C23, #C24, #C24, #C24, #C23, #C23, #C23]
+  #C26 = <core::Type>[#C2, #C2]
+  #C27 = ffi::_FfiStructLayout {fieldTypes:#C26, packing:#C8}
+  #C28 = core::pragma {name:#C1, options:#C27}
+  #C29 = "cfe:ffi:native-marker"
+  #C30 = "myNative"
+  #C31 = "#lib"
+  #C32 = true
+  #C33 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C30, assetId:#C31, isLeaf:#C32}
+  #C34 = core::pragma {name:#C29, options:#C33}
+  #C35 = "vm:ffi:native"
+  #C36 = core::pragma {name:#C35, options:#C33}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart.expect b/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart.expect
new file mode 100644
index 0000000..3362514
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_struct_element.dart.expect
@@ -0,0 +1,162 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+
+import "dart:ffi";
+
+@#C10
+final class MyStruct extends ffi::Struct {
+  synthetic constructor •() → self::MyStruct
+    : super ffi::Struct::•()
+    ;
+  constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyStruct
+    : super ffi::Struct::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
+    ;
+  constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyStruct
+    : super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
+    ;
+  @#C11
+  get a() → core::int
+    return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
+  @#C11
+  set a(synthesized core::int #externalFieldValue) → void
+    return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
+  @#C11
+  get b() → core::int
+    return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
+  @#C11
+  set b(synthesized core::int #externalFieldValue) → void
+    return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
+  @#C12
+  get array() → ffi::Array<ffi::Int8>
+    return new ffi::Array::_<ffi::Int8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C13);
+  @#C12
+  set array(synthesized ffi::Array<ffi::Int8> #externalFieldValue) → void
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+  @#C12
+  get array2() → ffi::Array<ffi::UnsignedLong>
+    return new ffi::Array::_<ffi::UnsignedLong>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array2#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C13);
+  @#C12
+  set array2(synthesized ffi::Array<ffi::UnsignedLong> #externalFieldValue) → void
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array2#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+  @#C19
+  static get a#offsetOf() → core::int
+    return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C19
+  static get b#offsetOf() → core::int
+    return #C23.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C19
+  static get array#offsetOf() → core::int
+    return #C25.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C19
+  static get array2#offsetOf() → core::int
+    return #C28.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C19
+  static get #sizeOf() → core::int*
+    return #C31.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C34
+final class MyUnion extends ffi::Union {
+  synthetic constructor •() → self::MyUnion
+    : super ffi::Union::•()
+    ;
+  constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyUnion
+    : super ffi::Union::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
+    ;
+  constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyUnion
+    : super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
+    ;
+  @#C11
+  get a() → core::int
+    return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
+  @#C11
+  set a(synthesized core::int #externalFieldValue) → void
+    return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
+  @#C11
+  get b() → core::int
+    return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
+  @#C11
+  set b(synthesized core::int #externalFieldValue) → void
+    return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
+  @#C19
+  static get a#offsetOf() → core::int
+    return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C19
+  static get b#offsetOf() → core::int
+    return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+  @#C19
+  static get #sizeOf() → core::int*
+    return #C23.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+static method main() → void {
+  final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase(typ::Uint8List::•(self::MyStruct::#sizeOf), #C20);
+  self::myNative#CC( block {
+    synthesized ffi::_Compound pointer#value = myStruct;
+  } =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(self::MyStruct::a#offsetOf){(core::num) → core::num}), block {
+    synthesized ffi::_Compound pointer2#value = myStruct;
+  } =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(self::MyStruct::b#offsetOf){(core::num) → core::num}));
+  final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C20);
+  self::myNative#CC(myUnion, myUnion);
+  self::myNative#CC( block {
+    synthesized ffi::_Compound pointer#value = myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
+  } =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#C22.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
+    synthesized ffi::_Compound pointer2#value = myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
+  } =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#C22.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
+  self::myNative#CC( block {
+    synthesized ffi::_Compound pointer#value = myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
+  } =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(ffi::UnsignedLong::#sizeOf.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
+    synthesized ffi::_Compound pointer2#value = myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
+  } =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(ffi::UnsignedLong::#sizeOf.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
+}
+@#C40
+@#C42
+external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2) → void;
+@#C40
+@#C42
+external static method myNative#CC(ffi::_Compound pointer, ffi::_Compound pointer2) → void;
+constants  {
+  #C1 = "vm:ffi:struct-fields"
+  #C2 = TypeLiteralConstant(ffi::Int8)
+  #C3 = 10
+  #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
+  #C5 = TypeLiteralConstant(ffi::UnsignedLong)
+  #C6 = ffi::_FfiInlineArray {elementType:#C5, length:#C3}
+  #C7 = <core::Type>[#C2, #C2, #C4, #C6]
+  #C8 = null
+  #C9 = ffi::_FfiStructLayout {fieldTypes:#C7, packing:#C8}
+  #C10 = core::pragma {name:#C1, options:#C9}
+  #C11 = ffi::Int8 {}
+  #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C8, dimension3:#C8, dimension4:#C8, dimension5:#C8, dimensions:#C8}
+  #C13 = <core::int*>[]
+  #C14 = <core::int*>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
+  #C15 = 40
+  #C16 = 80
+  #C17 = <core::int*>[#C15, #C16, #C15, #C16, #C16, #C16, #C16, #C16, #C15, #C16, #C16, #C15, #C16, #C15, #C16, #C15, #C16, #C16, #C16, #C15, #C15, #C15]
+  #C18 = "vm:prefer-inline"
+  #C19 = core::pragma {name:#C18, options:#C8}
+  #C20 = 0
+  #C21 = <core::int*>[#C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20]
+  #C22 = 1
+  #C23 = <core::int*>[#C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22]
+  #C24 = 2
+  #C25 = <core::int*>[#C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24]
+  #C26 = 12
+  #C27 = 16
+  #C28 = <core::int*>[#C26, #C27, #C26, #C27, #C27, #C27, #C27, #C27, #C26, #C27, #C27, #C26, #C27, #C26, #C27, #C26, #C27, #C27, #C27, #C26, #C26, #C26]
+  #C29 = 52
+  #C30 = 96
+  #C31 = <core::int*>[#C29, #C30, #C29, #C30, #C30, #C30, #C30, #C30, #C29, #C30, #C30, #C29, #C30, #C29, #C30, #C29, #C30, #C30, #C30, #C29, #C29, #C29]
+  #C32 = <core::Type>[#C2, #C2]
+  #C33 = ffi::_FfiStructLayout {fieldTypes:#C32, packing:#C8}
+  #C34 = core::pragma {name:#C1, options:#C33}
+  #C35 = "cfe:ffi:native-marker"
+  #C36 = "myNative"
+  #C37 = "#lib"
+  #C38 = true
+  #C39 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C36, assetId:#C37, isLeaf:#C38}
+  #C40 = core::pragma {name:#C35, options:#C39}
+  #C41 = "vm:ffi:native"
+  #C42 = core::pragma {name:#C41, options:#C39}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart b/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart
new file mode 100644
index 0000000..338b02c
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2024, 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.
+
+// @dart=3.5
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+void main() {
+  // Invocation with no `.address`, should use the original native.
+  final pointer = nullptr.cast<Int8>();
+  myNative(
+    pointer,
+    pointer,
+    1,
+  );
+
+  // Invocations with `.address` should invoke a copy, but the same copy.
+  final typedData = Int8List(20);
+  myNative(
+    typedData.address,
+    typedData.address,
+    2,
+  );
+  myNative(
+    Int8List.sublistView(typedData, 4).address,
+    typedData.address,
+    3,
+  );
+
+  // And invocations with different arguments being TypedDataBase should
+  // have a different copy.
+  myNative(
+    pointer,
+    typedData.address,
+    4,
+  );
+}
+
+@Native<
+    Int8 Function(
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Int8,
+    )>(isLeaf: true)
+external int myNative(
+  Pointer<Int8> pointer,
+  Pointer<Int8> pointer2,
+  int nonPointer,
+);
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart.aot.expect
new file mode 100644
index 0000000..b45fda9
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart.aot.expect
@@ -0,0 +1,42 @@
+library #lib;
+import self as self;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:core" as core;
+
+import "dart:ffi";
+import "dart:typed_data";
+
+static method main() → void {
+  final ffi::Pointer<ffi::Int8> pointer = [@vm.direct-call.metadata=dart.ffi::Pointer.cast] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::nullptr.{ffi::Pointer::cast}<ffi::Int8>(){() → ffi::Pointer<ffi::Int8>};
+  self::myNative(pointer, pointer, 1);
+  final typ::Int8List typedData = [@vm.inferred-type.metadata=dart.typed_data::_Int8List] typ::Int8List::•(20);
+  self::myNative#TT(typedData, typedData, 2);
+  self::myNative#TT([@vm.inferred-type.metadata=dart.typed_data::_Int8ArrayView] typ::Int8List::sublistView(typedData), typedData, 3);
+  self::myNative#PT(pointer, typedData, 4);
+}
+
+[@vm.unboxing-info.metadata=(b,b,i)->i]
+@#C6
+@#C8
+external static method myNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Int8> pointer, [@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Int8> pointer2, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 1)] core::int nonPointer) → core::int;
+
+[@vm.unboxing-info.metadata=(b,b,i)->i]
+@#C6
+@#C8
+external static method myNative#TT([@vm.inferred-arg-type.metadata=!] typ::TypedData pointer, [@vm.inferred-arg-type.metadata=dart.typed_data::_Int8List] typ::TypedData pointer2, [@vm.inferred-arg-type.metadata=dart.core::_Smi] core::int nonPointer) → core::int;
+
+[@vm.unboxing-info.metadata=(b,b,i)->i]
+@#C6
+@#C8
+external static method myNative#PT([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Int8> pointer, [@vm.inferred-arg-type.metadata=dart.typed_data::_Int8List] typ::TypedData pointer2, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 4)] core::int nonPointer) → core::int;
+constants  {
+  #C1 = "cfe:ffi:native-marker"
+  #C2 = "myNative"
+  #C3 = "#lib"
+  #C4 = true
+  #C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>, ffi::Int8) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
+  #C6 = core::pragma {name:#C1, options:#C5}
+  #C7 = "vm:ffi:native"
+  #C8 = core::pragma {name:#C7, options:#C5}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart.expect b/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart.expect
new file mode 100644
index 0000000..bdc0754
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_typeddata.dart.expect
@@ -0,0 +1,36 @@
+library #lib;
+import self as self;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:core" as core;
+
+import "dart:ffi";
+import "dart:typed_data";
+
+static method main() → void {
+  final ffi::Pointer<ffi::Int8> pointer = ffi::nullptr.{ffi::Pointer::cast}<ffi::Int8>(){() → ffi::Pointer<ffi::Int8>};
+  self::myNative(pointer, pointer, 1);
+  final typ::Int8List typedData = typ::Int8List::•(20);
+  self::myNative#TT(typedData, typedData, 2);
+  self::myNative#TT(typ::Int8List::sublistView(typedData, 4), typedData, 3);
+  self::myNative#PT(pointer, typedData, 4);
+}
+@#C6
+@#C8
+external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2, core::int nonPointer) → core::int;
+@#C6
+@#C8
+external static method myNative#TT(typ::TypedData pointer, typ::TypedData pointer2, core::int nonPointer) → core::int;
+@#C6
+@#C8
+external static method myNative#PT(ffi::Pointer<ffi::Int8> pointer, typ::TypedData pointer2, core::int nonPointer) → core::int;
+constants  {
+  #C1 = "cfe:ffi:native-marker"
+  #C2 = "myNative"
+  #C3 = "#lib"
+  #C4 = true
+  #C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>, ffi::Int8) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
+  #C6 = core::pragma {name:#C1, options:#C5}
+  #C7 = "vm:ffi:native"
+  #C8 = core::pragma {name:#C7, options:#C5}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart b/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart
new file mode 100644
index 0000000..2f74b34
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2024, 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.
+
+// @dart=3.5
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+void main() {
+  // This needs to create a view on the underlying typed data.
+  // Or, create a `_Compound` with an offset.
+  final typedData = Int8List(20);
+  SomeClass.myNative(
+    typedData[3].address,
+    typedData[8].address,
+  );
+}
+
+final class SomeClass {
+  @Native<
+      Int8 Function(
+        Pointer<Int8>,
+        Pointer<Int8>,
+      )>(isLeaf: true)
+  external static int myNative(
+    Pointer<Int8> pointer,
+    Pointer<Int8> pointer2,
+  );
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart.aot.expect
new file mode 100644
index 0000000..3d42a21
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart.aot.expect
@@ -0,0 +1,31 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+
+import "dart:ffi";
+import "dart:typed_data";
+
+abstract final class SomeClass extends core::Object {
+
+  [@vm.unboxing-info.metadata=(b,b)->i]
+  @#C6
+  @#C8
+  external static method myNative#CC([@vm.inferred-arg-type.metadata=dart.ffi::_Compound] ffi::_Compound pointer, [@vm.inferred-arg-type.metadata=dart.ffi::_Compound] ffi::_Compound pointer2) → core::int;
+}
+static method main() → void {
+  final typ::Int8List typedData = [@vm.inferred-type.metadata=dart.typed_data::_Int8List] typ::Int8List::•(20);
+  self::SomeClass::myNative#CC(new ffi::_Compound::_fromTypedDataBase(typedData, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C9.{core::num::*}(3){(core::num) → core::num}), new ffi::_Compound::_fromTypedDataBase(typedData, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C9.{core::num::*}(8){(core::num) → core::num}));
+}
+constants  {
+  #C1 = "cfe:ffi:native-marker"
+  #C2 = "myNative"
+  #C3 = "#lib"
+  #C4 = true
+  #C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
+  #C6 = core::pragma {name:#C1, options:#C5}
+  #C7 = "vm:ffi:native"
+  #C8 = core::pragma {name:#C7, options:#C5}
+  #C9 = 1
+}
diff --git a/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart.expect b/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart.expect
new file mode 100644
index 0000000..1d2f616
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/address_of_typeddata_element.dart.expect
@@ -0,0 +1,35 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+
+import "dart:ffi";
+import "dart:typed_data";
+
+final class SomeClass extends core::Object {
+  synthetic constructor •() → self::SomeClass
+    : super core::Object::•()
+    ;
+  @#C6
+  @#C8
+  external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2) → core::int;
+  @#C6
+  @#C8
+  external static method myNative#CC(ffi::_Compound pointer, ffi::_Compound pointer2) → core::int;
+}
+static method main() → void {
+  final typ::Int8List typedData = typ::Int8List::•(20);
+  self::SomeClass::myNative#CC(new ffi::_Compound::_fromTypedDataBase(typedData, #C9.{core::num::*}(3){(core::num) → core::num}), new ffi::_Compound::_fromTypedDataBase(typedData, #C9.{core::num::*}(8){(core::num) → core::num}));
+}
+constants  {
+  #C1 = "cfe:ffi:native-marker"
+  #C2 = "myNative"
+  #C3 = "#lib"
+  #C4 = true
+  #C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
+  #C6 = core::pragma {name:#C1, options:#C5}
+  #C7 = "vm:ffi:native"
+  #C8 = core::pragma {name:#C7, options:#C5}
+  #C9 = 1
+}
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index 44ee5e5..531f63e 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -1087,6 +1087,7 @@
     "ffi_test/ffi_test_fields.c",
     "ffi_test/ffi_test_functions.cc",
     "ffi_test/ffi_test_functions_generated.cc",
+    "ffi_test/ffi_test_functions_generated_2.cc",
     "ffi_test/ffi_test_functions_vmspecific.cc",
   ]
   if (is_win && current_cpu == "x64") {
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated_2.cc b/runtime/bin/ffi_test/ffi_test_functions_generated_2.cc
new file mode 100644
index 0000000..df9911c
--- /dev/null
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated_2.cc
@@ -0,0 +1,1023 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <iostream>
+#include <limits>
+
+#if defined(_WIN32)
+#define DART_EXPORT extern "C" __declspec(dllexport)
+#else
+#define DART_EXPORT                                                            \
+  extern "C" __attribute__((visibility("default"))) __attribute((used))
+#endif
+
+namespace dart {
+
+#define CHECK(X)                                                               \
+  if (!(X)) {                                                                  \
+    fprintf(stderr, "%s\n", "Check failed: " #X);                              \
+    return 1;                                                                  \
+  }
+
+#define CHECK_EQ(X, Y) CHECK((X) == (Y))
+
+// Works for positive, negative and zero.
+#define CHECK_APPROX(EXPECTED, ACTUAL)                                         \
+  CHECK(((EXPECTED * 0.99) <= (ACTUAL) && (EXPECTED * 1.01) >= (ACTUAL)) ||    \
+        ((EXPECTED * 0.99) >= (ACTUAL) && (EXPECTED * 1.01) <= (ACTUAL)))
+
+DART_EXPORT int8_t TakeInt8Pointer(int8_t* data, size_t length) {
+  int8_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << static_cast<int>(data[i]) << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT int8_t TakeInt8PointerMany(int8_t* data0,
+                                       int8_t* data1,
+                                       int8_t* data2,
+                                       int8_t* data3,
+                                       int8_t* data4,
+                                       int8_t* data5,
+                                       int8_t* data6,
+                                       int8_t* data7,
+                                       int8_t* data8,
+                                       int8_t* data9,
+                                       int8_t* data10,
+                                       int8_t* data11,
+                                       int8_t* data12,
+                                       int8_t* data13,
+                                       int8_t* data14,
+                                       int8_t* data15,
+                                       int8_t* data16,
+                                       int8_t* data17,
+                                       int8_t* data18,
+                                       int8_t* data19) {
+  int8_t result = 0;
+  std::cout << "data0[0] = " << static_cast<int>(data0[0]) << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << static_cast<int>(data1[0]) << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << static_cast<int>(data2[0]) << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << static_cast<int>(data3[0]) << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << static_cast<int>(data4[0]) << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << static_cast<int>(data5[0]) << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << static_cast<int>(data6[0]) << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << static_cast<int>(data7[0]) << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << static_cast<int>(data8[0]) << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << static_cast<int>(data9[0]) << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << static_cast<int>(data10[0]) << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << static_cast<int>(data11[0]) << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << static_cast<int>(data12[0]) << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << static_cast<int>(data13[0]) << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << static_cast<int>(data14[0]) << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << static_cast<int>(data15[0]) << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << static_cast<int>(data16[0]) << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << static_cast<int>(data17[0]) << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << static_cast<int>(data18[0]) << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << static_cast<int>(data19[0]) << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT int16_t TakeInt16Pointer(int16_t* data, size_t length) {
+  int16_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT int16_t TakeInt16PointerMany(int16_t* data0,
+                                         int16_t* data1,
+                                         int16_t* data2,
+                                         int16_t* data3,
+                                         int16_t* data4,
+                                         int16_t* data5,
+                                         int16_t* data6,
+                                         int16_t* data7,
+                                         int16_t* data8,
+                                         int16_t* data9,
+                                         int16_t* data10,
+                                         int16_t* data11,
+                                         int16_t* data12,
+                                         int16_t* data13,
+                                         int16_t* data14,
+                                         int16_t* data15,
+                                         int16_t* data16,
+                                         int16_t* data17,
+                                         int16_t* data18,
+                                         int16_t* data19) {
+  int16_t result = 0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT int32_t TakeInt32Pointer(int32_t* data, size_t length) {
+  int32_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT int32_t TakeInt32PointerMany(int32_t* data0,
+                                         int32_t* data1,
+                                         int32_t* data2,
+                                         int32_t* data3,
+                                         int32_t* data4,
+                                         int32_t* data5,
+                                         int32_t* data6,
+                                         int32_t* data7,
+                                         int32_t* data8,
+                                         int32_t* data9,
+                                         int32_t* data10,
+                                         int32_t* data11,
+                                         int32_t* data12,
+                                         int32_t* data13,
+                                         int32_t* data14,
+                                         int32_t* data15,
+                                         int32_t* data16,
+                                         int32_t* data17,
+                                         int32_t* data18,
+                                         int32_t* data19) {
+  int32_t result = 0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT int64_t TakeInt64Pointer(int64_t* data, size_t length) {
+  int64_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT int64_t TakeInt64PointerMany(int64_t* data0,
+                                         int64_t* data1,
+                                         int64_t* data2,
+                                         int64_t* data3,
+                                         int64_t* data4,
+                                         int64_t* data5,
+                                         int64_t* data6,
+                                         int64_t* data7,
+                                         int64_t* data8,
+                                         int64_t* data9,
+                                         int64_t* data10,
+                                         int64_t* data11,
+                                         int64_t* data12,
+                                         int64_t* data13,
+                                         int64_t* data14,
+                                         int64_t* data15,
+                                         int64_t* data16,
+                                         int64_t* data17,
+                                         int64_t* data18,
+                                         int64_t* data19) {
+  int64_t result = 0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT uint8_t TakeUint8Pointer(uint8_t* data, size_t length) {
+  uint8_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << static_cast<int>(data[i]) << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT uint8_t TakeUint8PointerMany(uint8_t* data0,
+                                         uint8_t* data1,
+                                         uint8_t* data2,
+                                         uint8_t* data3,
+                                         uint8_t* data4,
+                                         uint8_t* data5,
+                                         uint8_t* data6,
+                                         uint8_t* data7,
+                                         uint8_t* data8,
+                                         uint8_t* data9,
+                                         uint8_t* data10,
+                                         uint8_t* data11,
+                                         uint8_t* data12,
+                                         uint8_t* data13,
+                                         uint8_t* data14,
+                                         uint8_t* data15,
+                                         uint8_t* data16,
+                                         uint8_t* data17,
+                                         uint8_t* data18,
+                                         uint8_t* data19) {
+  uint8_t result = 0;
+  std::cout << "data0[0] = " << static_cast<int>(data0[0]) << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << static_cast<int>(data1[0]) << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << static_cast<int>(data2[0]) << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << static_cast<int>(data3[0]) << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << static_cast<int>(data4[0]) << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << static_cast<int>(data5[0]) << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << static_cast<int>(data6[0]) << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << static_cast<int>(data7[0]) << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << static_cast<int>(data8[0]) << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << static_cast<int>(data9[0]) << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << static_cast<int>(data10[0]) << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << static_cast<int>(data11[0]) << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << static_cast<int>(data12[0]) << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << static_cast<int>(data13[0]) << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << static_cast<int>(data14[0]) << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << static_cast<int>(data15[0]) << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << static_cast<int>(data16[0]) << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << static_cast<int>(data17[0]) << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << static_cast<int>(data18[0]) << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << static_cast<int>(data19[0]) << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT uint16_t TakeUint16Pointer(uint16_t* data, size_t length) {
+  uint16_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT uint16_t TakeUint16PointerMany(uint16_t* data0,
+                                           uint16_t* data1,
+                                           uint16_t* data2,
+                                           uint16_t* data3,
+                                           uint16_t* data4,
+                                           uint16_t* data5,
+                                           uint16_t* data6,
+                                           uint16_t* data7,
+                                           uint16_t* data8,
+                                           uint16_t* data9,
+                                           uint16_t* data10,
+                                           uint16_t* data11,
+                                           uint16_t* data12,
+                                           uint16_t* data13,
+                                           uint16_t* data14,
+                                           uint16_t* data15,
+                                           uint16_t* data16,
+                                           uint16_t* data17,
+                                           uint16_t* data18,
+                                           uint16_t* data19) {
+  uint16_t result = 0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT uint32_t TakeUint32Pointer(uint32_t* data, size_t length) {
+  uint32_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT uint32_t TakeUint32PointerMany(uint32_t* data0,
+                                           uint32_t* data1,
+                                           uint32_t* data2,
+                                           uint32_t* data3,
+                                           uint32_t* data4,
+                                           uint32_t* data5,
+                                           uint32_t* data6,
+                                           uint32_t* data7,
+                                           uint32_t* data8,
+                                           uint32_t* data9,
+                                           uint32_t* data10,
+                                           uint32_t* data11,
+                                           uint32_t* data12,
+                                           uint32_t* data13,
+                                           uint32_t* data14,
+                                           uint32_t* data15,
+                                           uint32_t* data16,
+                                           uint32_t* data17,
+                                           uint32_t* data18,
+                                           uint32_t* data19) {
+  uint32_t result = 0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT uint64_t TakeUint64Pointer(uint64_t* data, size_t length) {
+  uint64_t result = 0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT uint64_t TakeUint64PointerMany(uint64_t* data0,
+                                           uint64_t* data1,
+                                           uint64_t* data2,
+                                           uint64_t* data3,
+                                           uint64_t* data4,
+                                           uint64_t* data5,
+                                           uint64_t* data6,
+                                           uint64_t* data7,
+                                           uint64_t* data8,
+                                           uint64_t* data9,
+                                           uint64_t* data10,
+                                           uint64_t* data11,
+                                           uint64_t* data12,
+                                           uint64_t* data13,
+                                           uint64_t* data14,
+                                           uint64_t* data15,
+                                           uint64_t* data16,
+                                           uint64_t* data17,
+                                           uint64_t* data18,
+                                           uint64_t* data19) {
+  uint64_t result = 0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT float TakeFloatPointer(float* data, size_t length) {
+  float result = 0.0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT float TakeFloatPointerMany(float* data0,
+                                       float* data1,
+                                       float* data2,
+                                       float* data3,
+                                       float* data4,
+                                       float* data5,
+                                       float* data6,
+                                       float* data7,
+                                       float* data8,
+                                       float* data9,
+                                       float* data10,
+                                       float* data11,
+                                       float* data12,
+                                       float* data13,
+                                       float* data14,
+                                       float* data15,
+                                       float* data16,
+                                       float* data17,
+                                       float* data18,
+                                       float* data19) {
+  float result = 0.0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT double TakeDoublePointer(double* data, size_t length) {
+  double result = 0.0;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << data[i] << "\n";
+    result += data[i];
+  }
+  return result;
+}
+
+DART_EXPORT double TakeDoublePointerMany(double* data0,
+                                         double* data1,
+                                         double* data2,
+                                         double* data3,
+                                         double* data4,
+                                         double* data5,
+                                         double* data6,
+                                         double* data7,
+                                         double* data8,
+                                         double* data9,
+                                         double* data10,
+                                         double* data11,
+                                         double* data12,
+                                         double* data13,
+                                         double* data14,
+                                         double* data15,
+                                         double* data16,
+                                         double* data17,
+                                         double* data18,
+                                         double* data19) {
+  double result = 0.0;
+  std::cout << "data0[0] = " << data0[0] << "\n";
+  result += data0[0];
+  std::cout << "data1[0] = " << data1[0] << "\n";
+  result += data1[0];
+  std::cout << "data2[0] = " << data2[0] << "\n";
+  result += data2[0];
+  std::cout << "data3[0] = " << data3[0] << "\n";
+  result += data3[0];
+  std::cout << "data4[0] = " << data4[0] << "\n";
+  result += data4[0];
+  std::cout << "data5[0] = " << data5[0] << "\n";
+  result += data5[0];
+  std::cout << "data6[0] = " << data6[0] << "\n";
+  result += data6[0];
+  std::cout << "data7[0] = " << data7[0] << "\n";
+  result += data7[0];
+  std::cout << "data8[0] = " << data8[0] << "\n";
+  result += data8[0];
+  std::cout << "data9[0] = " << data9[0] << "\n";
+  result += data9[0];
+  std::cout << "data10[0] = " << data10[0] << "\n";
+  result += data10[0];
+  std::cout << "data11[0] = " << data11[0] << "\n";
+  result += data11[0];
+  std::cout << "data12[0] = " << data12[0] << "\n";
+  result += data12[0];
+  std::cout << "data13[0] = " << data13[0] << "\n";
+  result += data13[0];
+  std::cout << "data14[0] = " << data14[0] << "\n";
+  result += data14[0];
+  std::cout << "data15[0] = " << data15[0] << "\n";
+  result += data15[0];
+  std::cout << "data16[0] = " << data16[0] << "\n";
+  result += data16[0];
+  std::cout << "data17[0] = " << data17[0] << "\n";
+  result += data17[0];
+  std::cout << "data18[0] = " << data18[0] << "\n";
+  result += data18[0];
+  std::cout << "data19[0] = " << data19[0] << "\n";
+  result += data19[0];
+  return result;
+}
+
+DART_EXPORT bool TakeBoolPointer(bool* data, size_t length) {
+  bool result = false;
+  if (length > 100) {
+    std::cout << "Mangled arguments\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << static_cast<int>(data[i]) << "\n";
+    result ^= static_cast<int>(data[i]);
+  }
+  return result;
+}
+
+DART_EXPORT bool TakeBoolPointerMany(bool* data0,
+                                     bool* data1,
+                                     bool* data2,
+                                     bool* data3,
+                                     bool* data4,
+                                     bool* data5,
+                                     bool* data6,
+                                     bool* data7,
+                                     bool* data8,
+                                     bool* data9,
+                                     bool* data10,
+                                     bool* data11,
+                                     bool* data12,
+                                     bool* data13,
+                                     bool* data14,
+                                     bool* data15,
+                                     bool* data16,
+                                     bool* data17,
+                                     bool* data18,
+                                     bool* data19) {
+  bool result = false;
+  std::cout << "data0[0] = " << static_cast<int>(data0[0]) << "\n";
+  result ^= static_cast<int>(data0[0]);
+  std::cout << "data1[0] = " << static_cast<int>(data1[0]) << "\n";
+  result ^= static_cast<int>(data1[0]);
+  std::cout << "data2[0] = " << static_cast<int>(data2[0]) << "\n";
+  result ^= static_cast<int>(data2[0]);
+  std::cout << "data3[0] = " << static_cast<int>(data3[0]) << "\n";
+  result ^= static_cast<int>(data3[0]);
+  std::cout << "data4[0] = " << static_cast<int>(data4[0]) << "\n";
+  result ^= static_cast<int>(data4[0]);
+  std::cout << "data5[0] = " << static_cast<int>(data5[0]) << "\n";
+  result ^= static_cast<int>(data5[0]);
+  std::cout << "data6[0] = " << static_cast<int>(data6[0]) << "\n";
+  result ^= static_cast<int>(data6[0]);
+  std::cout << "data7[0] = " << static_cast<int>(data7[0]) << "\n";
+  result ^= static_cast<int>(data7[0]);
+  std::cout << "data8[0] = " << static_cast<int>(data8[0]) << "\n";
+  result ^= static_cast<int>(data8[0]);
+  std::cout << "data9[0] = " << static_cast<int>(data9[0]) << "\n";
+  result ^= static_cast<int>(data9[0]);
+  std::cout << "data10[0] = " << static_cast<int>(data10[0]) << "\n";
+  result ^= static_cast<int>(data10[0]);
+  std::cout << "data11[0] = " << static_cast<int>(data11[0]) << "\n";
+  result ^= static_cast<int>(data11[0]);
+  std::cout << "data12[0] = " << static_cast<int>(data12[0]) << "\n";
+  result ^= static_cast<int>(data12[0]);
+  std::cout << "data13[0] = " << static_cast<int>(data13[0]) << "\n";
+  result ^= static_cast<int>(data13[0]);
+  std::cout << "data14[0] = " << static_cast<int>(data14[0]) << "\n";
+  result ^= static_cast<int>(data14[0]);
+  std::cout << "data15[0] = " << static_cast<int>(data15[0]) << "\n";
+  result ^= static_cast<int>(data15[0]);
+  std::cout << "data16[0] = " << static_cast<int>(data16[0]) << "\n";
+  result ^= static_cast<int>(data16[0]);
+  std::cout << "data17[0] = " << static_cast<int>(data17[0]) << "\n";
+  result ^= static_cast<int>(data17[0]);
+  std::cout << "data18[0] = " << static_cast<int>(data18[0]) << "\n";
+  result ^= static_cast<int>(data18[0]);
+  std::cout << "data19[0] = " << static_cast<int>(data19[0]) << "\n";
+  result ^= static_cast<int>(data19[0]);
+  return result;
+}
+
+struct Struct2BytesInt {
+  int16_t a0;
+};
+
+DART_EXPORT int16_t TakeStruct2BytesIntPointerMany(Struct2BytesInt* data0,
+                                                   Struct2BytesInt* data1,
+                                                   Struct2BytesInt* data2,
+                                                   Struct2BytesInt* data3,
+                                                   Struct2BytesInt* data4,
+                                                   Struct2BytesInt* data5,
+                                                   Struct2BytesInt* data6,
+                                                   Struct2BytesInt* data7,
+                                                   Struct2BytesInt* data8,
+                                                   Struct2BytesInt* data9,
+                                                   Struct2BytesInt* data10,
+                                                   Struct2BytesInt* data11,
+                                                   Struct2BytesInt* data12,
+                                                   Struct2BytesInt* data13,
+                                                   Struct2BytesInt* data14,
+                                                   Struct2BytesInt* data15,
+                                                   Struct2BytesInt* data16,
+                                                   Struct2BytesInt* data17,
+                                                   Struct2BytesInt* data18,
+                                                   Struct2BytesInt* data19) {
+  int16_t result = 0;
+  std::cout << "data0->a0 = " << data0->a0 << "\n";
+  result += data0->a0;
+  std::cout << "data1->a0 = " << data1->a0 << "\n";
+  result += data1->a0;
+  std::cout << "data2->a0 = " << data2->a0 << "\n";
+  result += data2->a0;
+  std::cout << "data3->a0 = " << data3->a0 << "\n";
+  result += data3->a0;
+  std::cout << "data4->a0 = " << data4->a0 << "\n";
+  result += data4->a0;
+  std::cout << "data5->a0 = " << data5->a0 << "\n";
+  result += data5->a0;
+  std::cout << "data6->a0 = " << data6->a0 << "\n";
+  result += data6->a0;
+  std::cout << "data7->a0 = " << data7->a0 << "\n";
+  result += data7->a0;
+  std::cout << "data8->a0 = " << data8->a0 << "\n";
+  result += data8->a0;
+  std::cout << "data9->a0 = " << data9->a0 << "\n";
+  result += data9->a0;
+  std::cout << "data10->a0 = " << data10->a0 << "\n";
+  result += data10->a0;
+  std::cout << "data11->a0 = " << data11->a0 << "\n";
+  result += data11->a0;
+  std::cout << "data12->a0 = " << data12->a0 << "\n";
+  result += data12->a0;
+  std::cout << "data13->a0 = " << data13->a0 << "\n";
+  result += data13->a0;
+  std::cout << "data14->a0 = " << data14->a0 << "\n";
+  result += data14->a0;
+  std::cout << "data15->a0 = " << data15->a0 << "\n";
+  result += data15->a0;
+  std::cout << "data16->a0 = " << data16->a0 << "\n";
+  result += data16->a0;
+  std::cout << "data17->a0 = " << data17->a0 << "\n";
+  result += data17->a0;
+  std::cout << "data18->a0 = " << data18->a0 << "\n";
+  result += data18->a0;
+  std::cout << "data19->a0 = " << data19->a0 << "\n";
+  result += data19->a0;
+  return result;
+}
+
+union Union2BytesInt {
+  int16_t a0;
+};
+
+DART_EXPORT int16_t TakeUnion2BytesIntPointerMany(Union2BytesInt* data0,
+                                                  Union2BytesInt* data1,
+                                                  Union2BytesInt* data2,
+                                                  Union2BytesInt* data3,
+                                                  Union2BytesInt* data4,
+                                                  Union2BytesInt* data5,
+                                                  Union2BytesInt* data6,
+                                                  Union2BytesInt* data7,
+                                                  Union2BytesInt* data8,
+                                                  Union2BytesInt* data9,
+                                                  Union2BytesInt* data10,
+                                                  Union2BytesInt* data11,
+                                                  Union2BytesInt* data12,
+                                                  Union2BytesInt* data13,
+                                                  Union2BytesInt* data14,
+                                                  Union2BytesInt* data15,
+                                                  Union2BytesInt* data16,
+                                                  Union2BytesInt* data17,
+                                                  Union2BytesInt* data18,
+                                                  Union2BytesInt* data19) {
+  int16_t result = 0;
+  std::cout << "data0->a0 = " << data0->a0 << "\n";
+  result += data0->a0;
+  std::cout << "data1->a0 = " << data1->a0 << "\n";
+  result += data1->a0;
+  std::cout << "data2->a0 = " << data2->a0 << "\n";
+  result += data2->a0;
+  std::cout << "data3->a0 = " << data3->a0 << "\n";
+  result += data3->a0;
+  std::cout << "data4->a0 = " << data4->a0 << "\n";
+  result += data4->a0;
+  std::cout << "data5->a0 = " << data5->a0 << "\n";
+  result += data5->a0;
+  std::cout << "data6->a0 = " << data6->a0 << "\n";
+  result += data6->a0;
+  std::cout << "data7->a0 = " << data7->a0 << "\n";
+  result += data7->a0;
+  std::cout << "data8->a0 = " << data8->a0 << "\n";
+  result += data8->a0;
+  std::cout << "data9->a0 = " << data9->a0 << "\n";
+  result += data9->a0;
+  std::cout << "data10->a0 = " << data10->a0 << "\n";
+  result += data10->a0;
+  std::cout << "data11->a0 = " << data11->a0 << "\n";
+  result += data11->a0;
+  std::cout << "data12->a0 = " << data12->a0 << "\n";
+  result += data12->a0;
+  std::cout << "data13->a0 = " << data13->a0 << "\n";
+  result += data13->a0;
+  std::cout << "data14->a0 = " << data14->a0 << "\n";
+  result += data14->a0;
+  std::cout << "data15->a0 = " << data15->a0 << "\n";
+  result += data15->a0;
+  std::cout << "data16->a0 = " << data16->a0 << "\n";
+  result += data16->a0;
+  std::cout << "data17->a0 = " << data17->a0 << "\n";
+  result += data17->a0;
+  std::cout << "data18->a0 = " << data18->a0 << "\n";
+  result += data18->a0;
+  std::cout << "data19->a0 = " << data19->a0 << "\n";
+  result += data19->a0;
+  return result;
+}
+
+}  // namespace dart
diff --git a/runtime/tools/ffi/sdk_lib_ffi_generator.dart b/runtime/tools/ffi/sdk_lib_ffi_generator.dart
index d760b42..937d51a 100644
--- a/runtime/tools/ffi/sdk_lib_ffi_generator.dart
+++ b/runtime/tools/ffi/sdk_lib_ffi_generator.dart
@@ -47,9 +47,15 @@
     Version(2, 13),
   );
 
+  static const typedData = Container(
+    'TypedData',
+    Version(3, 5),
+  );
+
   static const values = [
     pointer,
     array,
+    typedData,
   ];
 }
 
@@ -294,6 +300,41 @@
 }
 
 """);
+    case Container.typedData:
+      if (typedListType != kDoNotEmit) {
+        buffer.write("""
+$since extension ${typedListType}Address on $typedListType {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<$nativeType>)>(isLeaf: true)
+  /// external void myFunction(Pointer<$nativeType> pointer);
+  ///
+  /// void main() {
+  ///   final list = $typedListType(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<$nativeType> get address;
+}
+
+""");
+      }
   }
 }
 
@@ -375,6 +416,8 @@
 }
 
 """);
+    case Container.typedData:
+    // Nothing to do, all rewritten in the CFE.
   }
 }
 
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index e8ee6f4..e8f5613 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -7351,7 +7351,7 @@
     // All input handles are passed as tagged values to FfiCallInstr and
     // are given stack locations. FfiCallInstr then passes an untagged pointer
     // to the handle on the stack (Dart_Handle) to the C function.
-    if (marshaller_.IsHandle(marshaller_.ArgumentIndex(idx))) {
+    if (marshaller_.IsHandleCType(marshaller_.ArgumentIndex(idx))) {
       return kTagged;
     }
     return marshaller_.RepInFfiCall(idx);
@@ -7396,13 +7396,14 @@
       marshaller_.contains_varargs()
           ? R13
           : CallingConventions::kFirstNonArgumentRegister;  // RAX
+#else
+  const Register target_address = CallingConventions::kFirstNonArgumentRegister;
+#endif
+#define R(r) (1 << r)
+  ASSERT_EQUAL(temps & R(target_address), 0x0);
+#undef R
   summary->set_in(TargetAddressIndex(),
                   Location::RegisterLocation(target_address));
-#else
-  summary->set_in(TargetAddressIndex(),
-                  Location::RegisterLocation(
-                      CallingConventions::kFirstNonArgumentRegister));
-#endif
   for (intptr_t i = 0, n = marshaller_.NumArgumentDefinitions(); i < n; ++i) {
     summary->set_in(i, marshaller_.LocInFfiCall(i));
   }
@@ -7458,7 +7459,9 @@
     // First deal with moving all individual definitions passed in to the
     // FfiCall to the right native location based on calling convention.
     for (intptr_t i = 0; i < num_defs; i++) {
-      if (arg_target.IsPointerToMemory() && i == 1) {
+      if ((arg_target.IsPointerToMemory() ||
+           marshaller_.IsCompoundPointer(arg_index)) &&
+          i == 1) {
         // The offset_in_bytes is not an argument for C, so don't move it.
         // It is used as offset_in_bytes_loc below and moved there if
         // necessary.
@@ -7466,7 +7469,7 @@
         continue;
       }
       __ Comment("  def_index %" Pd, def_index);
-      const Location origin = rebase.Rebase(locs()->in(def_index));
+      Location origin = rebase.Rebase(locs()->in(def_index));
       const Representation origin_rep = RequiredInputRepresentation(def_index);
 
       // Find the native location where this individual definition should be
@@ -7482,17 +7485,21 @@
       ConstantTemporaryAllocator temp_alloc(temp0);
       if (origin.IsConstant()) {
         __ Comment("origin.IsConstant()");
-        ASSERT(!marshaller_.IsHandle(arg_index));
+        ASSERT(!marshaller_.IsHandleCType(arg_index));
+        ASSERT(!marshaller_.IsTypedDataPointer(arg_index));
+        ASSERT(!marshaller_.IsCompoundPointer(arg_index));
         compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
       } else if (origin.IsPairLocation() &&
                  (origin.AsPairLocation()->At(0).IsConstant() ||
                   origin.AsPairLocation()->At(1).IsConstant())) {
         // Note: half of the pair can be constant.
         __ Comment("origin.IsPairLocation() and constant");
-        ASSERT(!marshaller_.IsHandle(arg_index));
+        ASSERT(!marshaller_.IsHandleCType(arg_index));
+        ASSERT(!marshaller_.IsTypedDataPointer(arg_index));
+        ASSERT(!marshaller_.IsCompoundPointer(arg_index));
         compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
-      } else if (marshaller_.IsHandle(arg_index)) {
-        __ Comment("marshaller_.IsHandle(arg_index)");
+      } else if (marshaller_.IsHandleCType(arg_index)) {
+        __ Comment("marshaller_.IsHandleCType(arg_index)");
         // Handles are passed into FfiCalls as Tagged values on the stack, and
         // then we pass pointers to these handles to the native function here.
         ASSERT(origin_rep == kTagged);
@@ -7529,6 +7536,35 @@
                  marshaller_.RequiredStackSpaceInBytes());
         }
 #endif
+        if (marshaller_.IsTypedDataPointer(arg_index) ||
+            marshaller_.IsCompoundPointer(arg_index)) {
+          // Unwrap typed data before move to native location.
+          __ Comment("Load typed data base address");
+          if (origin.IsStackSlot()) {
+            compiler->EmitMove(Location::RegisterLocation(temp0), origin,
+                               &temp_alloc);
+            origin = Location::RegisterLocation(temp0);
+          }
+          ASSERT(origin.IsRegister());
+          __ LoadFromSlot(origin.reg(), origin.reg(), Slot::PointerBase_data());
+          if (marshaller_.IsCompoundPointer(arg_index)) {
+            __ Comment("Load offset in bytes");
+            const intptr_t offset_in_bytes_def_index = def_index + 1;
+            const Location offset_in_bytes_loc =
+                rebase.Rebase(locs()->in(offset_in_bytes_def_index));
+            Register offset_in_bytes_reg = kNoRegister;
+            if (offset_in_bytes_loc.IsRegister()) {
+              offset_in_bytes_reg = offset_in_bytes_loc.reg();
+            } else {
+              offset_in_bytes_reg = temp1;
+              NoTemporaryAllocator no_temp;
+              compiler->EmitMove(
+                  Location::RegisterLocation(offset_in_bytes_reg),
+                  offset_in_bytes_loc, &no_temp);
+            }
+            __ AddRegisters(origin.reg(), offset_in_bytes_reg);
+          }
+        }
         compiler->EmitMoveToNative(def_target, origin, origin_rep, &temp_alloc);
       }
       def_index++;
@@ -7910,7 +7946,7 @@
     // Don't care, we're discarding the value.
     return kTagged;
   }
-  if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
+  if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
     // The call returns a Dart_Handle, from which we need to extract the
     // tagged pointer using LoadField with an appropriate slot.
     return kUntagged;
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 9fb5058c..c34f586 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -1748,9 +1748,11 @@
 
 LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
                                                    bool is_optimizing) const {
+  COMPILE_ASSERT(R(CallingConventions::kFfiAnyNonAbiRegister) <
+                 R(CallingConventions::kSecondNonArgumentRegister));
   return MakeLocationSummaryInternal(
       zone, is_optimizing,
-      (R(R0) | R(CallingConventions::kFfiAnyNonAbiRegister) |
+      (R(CallingConventions::kFfiAnyNonAbiRegister) |
        R(CallingConventions::kSecondNonArgumentRegister)));
 }
 
@@ -1760,13 +1762,12 @@
   const Register branch = locs()->in(TargetAddressIndex()).reg();
 
   // The temps are indexed according to their register number.
-  const Register temp2 = locs()->temp(0).reg();
   // For regular calls, this holds the FP for rebasing the original locations
   // during EmitParamMoves.
   // For leaf calls, this holds the SP used to restore the pre-aligned SP after
   // the call.
-  const Register saved_fp_or_sp = locs()->temp(1).reg();
-  const Register temp1 = locs()->temp(2).reg();
+  const Register saved_fp_or_sp = locs()->temp(0).reg();
+  const Register temp1 = locs()->temp(1).reg();
 
   // Ensure these are callee-saved register and are preserved across the call.
   ASSERT(IsCalleeSavedRegister(saved_fp_or_sp));
@@ -1791,7 +1792,7 @@
   UNIMPLEMENTED();
 #endif
 
-  EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, temp2);
+  EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, TMP);
 
   if (compiler::Assembler::EmittingComments()) {
     __ Comment(is_leaf_ ? "Leaf Call" : "Call");
@@ -1843,7 +1844,7 @@
                   "NOTFP should be a reserved register");
     __ blx(temp1);
 
-    if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
+    if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
       __ Comment("Check Dart_Handle for Error.");
       compiler::Label not_error;
       ASSERT(temp1 != CallingConventions::kReturnReg);
@@ -1882,7 +1883,7 @@
     }
   }
 
-  EmitReturnMoves(compiler, temp1, temp2);
+  EmitReturnMoves(compiler, temp1, TMP);
 
   if (is_leaf_) {
     // Restore the pre-aligned SP.
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index c215950..5bf3d21 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -1593,7 +1593,7 @@
     ASSERT(branch == R9);
     __ blr(temp1);
 
-    if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
+    if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
       __ Comment("Check Dart_Handle for Error.");
       compiler::Label not_error;
       __ ldr(temp1,
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 4c65fc3..441fc0a 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -1198,9 +1198,11 @@
 
 LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
                                                    bool is_optimizing) const {
+  COMPILE_ASSERT(R(CallingConventions::kSecondNonArgumentRegister) < R(EDX));
+  COMPILE_ASSERT(R(EDX) < R(CallingConventions::kFfiAnyNonAbiRegister));
   return MakeLocationSummaryInternal(
       zone, is_optimizing,
-      (R(CallingConventions::kSecondNonArgumentRegister) |
+      (R(CallingConventions::kSecondNonArgumentRegister) | R(EDX) |
        R(CallingConventions::kFfiAnyNonAbiRegister)));
 }
 
@@ -1215,7 +1217,7 @@
   // during EmitParamMoves.
   // For leaf calls, this holds the SP used to restore the pre-aligned SP after
   // the call.
-  const Register saved_fp_or_sp = locs()->temp(1).reg();
+  const Register saved_fp_or_sp = locs()->temp(2).reg();
 
   // Ensure these are callee-saved register and are preserved across the call.
   ASSERT(IsCalleeSavedRegister(saved_fp_or_sp));
@@ -1243,9 +1245,8 @@
   UNIMPLEMENTED();
 #endif
 
-  // No second temp: PointerToMemoryLocation is not used for arguments in ia32.
   EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp,
-                 kNoRegister);
+                 locs()->temp(1).reg());
 
   if (is_leaf_) {
     // We store the pre-align SP at a fixed offset from the final SP.
@@ -1306,7 +1307,7 @@
     ASSERT(branch == EAX);
     __ call(temp);
 
-    if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
+    if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
       __ Comment("Check Dart_Handle for Error.");
       compiler::Label not_error;
       __ movl(temp,
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index 68a22f0..dd0203a 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -1749,7 +1749,7 @@
       __ jalr(temp1);
     }
 
-    if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
+    if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
       __ Comment("Check Dart_Handle for Error.");
       ASSERT(temp1 != CallingConventions::kReturnReg);
       ASSERT(temp2 != CallingConventions::kReturnReg);
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index d4c7095..04a4b35 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1541,7 +1541,7 @@
       __ call(temp);
     }
 
-    if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
+    if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
       __ Comment("Check Dart_Handle for Error.");
       compiler::Label not_error;
       __ movq(temp,
diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc
index 4213d49..c9ebc81 100644
--- a/runtime/vm/compiler/compiler_state.cc
+++ b/runtime/vm/compiler/compiler_state.cc
@@ -92,23 +92,6 @@
   }
   return *interpolate_;
 }
-
-const Class& CompilerState::TypedListClass() {
-  if (typed_list_class_ == nullptr) {
-    Thread* thread = Thread::Current();
-    Zone* zone = thread->zone();
-
-    const Library& lib = Library::Handle(zone, Library::TypedDataLibrary());
-    const Class& cls = Class::ZoneHandle(
-        zone, lib.LookupClassAllowPrivate(Symbols::_TypedList()));
-    ASSERT(!cls.IsNull());
-    const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
-    ASSERT(error.IsNull());
-    typed_list_class_ = &cls;
-  }
-  return *typed_list_class_;
-}
-
 #define DEFINE_TYPED_LIST_NATIVE_FUNCTION_GETTER(Upper, Lower)                 \
   const Function& CompilerState::TypedListGet##Upper() {                       \
     if (typed_list_get_##Lower##_ == nullptr) {                                \
@@ -141,21 +124,30 @@
 
 #undef DEFINE_TYPED_LIST_NATIVE_FUNCTION_GETTER
 
-const Class& CompilerState::CompoundClass() {
-  if (compound_class_ == nullptr) {
-    Thread* thread = Thread::Current();
-    Zone* zone = thread->zone();
-
-    const auto& lib_ffi = Library::Handle(zone, Library::FfiLibrary());
-    const auto& cls = Class::Handle(
-        zone, lib_ffi.LookupClassAllowPrivate(Symbols::Compound()));
-    ASSERT(!cls.IsNull());
-    const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
-    ASSERT(error.IsNull());
-    compound_class_ = &cls;
+#define DEFINE_CLASS_GETTER(Lib, Upper, Lower, Symbol)                         \
+  const Class& CompilerState::Upper##Class() {                                 \
+    if (Lower##_class_ == nullptr) {                                           \
+      Thread* thread = Thread::Current();                                      \
+      Zone* zone = thread->zone();                                             \
+      const auto& lib = Library::Handle(zone, Library::Lib##Library());        \
+      const auto& cls =                                                        \
+          Class::Handle(zone, lib.LookupClassAllowPrivate(Symbols::Symbol())); \
+      ASSERT(!cls.IsNull());                                                   \
+      const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread)); \
+      ASSERT(error.IsNull());                                                  \
+      Lower##_class_ = &cls;                                                   \
+    }                                                                          \
+    return *Lower##_class_;                                                    \
   }
-  return *compound_class_;
-}
+
+DEFINE_CLASS_GETTER(Ffi, Array, array, Array)
+DEFINE_CLASS_GETTER(Ffi, Compound, compound, Compound)
+DEFINE_CLASS_GETTER(Ffi, Struct, struct, Struct)
+DEFINE_CLASS_GETTER(Ffi, Union, union, Union)
+DEFINE_CLASS_GETTER(TypedData, TypedData, typed_data, TypedData)
+DEFINE_CLASS_GETTER(TypedData, TypedList, typed_list, _TypedList)
+
+#undef DEFINE_CLASS_GETTER
 
 const Field& CompilerState::CompoundOffsetInBytesField() {
   if (compound_offset_in_bytes_field_ == nullptr) {
diff --git a/runtime/vm/compiler/compiler_state.h b/runtime/vm/compiler/compiler_state.h
index 1029993..bcc2377 100644
--- a/runtime/vm/compiler/compiler_state.h
+++ b/runtime/vm/compiler/compiler_state.h
@@ -105,7 +105,12 @@
   const Function& TypedListGetFloat64x2();
   const Function& TypedListSetFloat64x2();
 
+  const Class& ArrayClass();
   const Class& CompoundClass();
+  const Class& StructClass();
+  const Class& TypedDataClass();
+  const Class& UnionClass();
+
   const Field& CompoundOffsetInBytesField();
   const Field& CompoundTypedDataBaseField();
 
@@ -147,7 +152,11 @@
   const Function* interpolate_ = nullptr;
   const Function* interpolate_single_ = nullptr;
   const Class* typed_list_class_ = nullptr;
+  const Class* array_class_ = nullptr;
   const Class* compound_class_ = nullptr;
+  const Class* struct_class_ = nullptr;
+  const Class* typed_data_class_ = nullptr;
+  const Class* union_class_ = nullptr;
   const Field* compound_offset_in_bytes_field_ = nullptr;
   const Field* compound_typed_data_base_field_ = nullptr;
   const Function* typed_list_get_float32_ = nullptr;
diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc
index 96779c8..dc854b7 100644
--- a/runtime/vm/compiler/ffi/marshaller.cc
+++ b/runtime/vm/compiler/ffi/marshaller.cc
@@ -7,6 +7,7 @@
 #include "platform/assert.h"
 #include "platform/globals.h"
 #include "vm/class_id.h"
+#include "vm/compiler/compiler_state.h"
 #include "vm/compiler/ffi/frame_rebase.h"
 #include "vm/compiler/ffi/native_calling_convention.h"
 #include "vm/compiler/ffi/native_location.h"
@@ -18,6 +19,7 @@
 #include "vm/raw_object.h"
 #include "vm/stack_frame.h"
 #include "vm/symbols.h"
+#include "vm/tagged_pointer.h"
 
 namespace dart {
 
@@ -143,30 +145,101 @@
   return c_signature_.ParameterTypeAt(real_arg_index);
 }
 
+AbstractTypePtr BaseMarshaller::DartType(intptr_t arg_index) const {
+  if (arg_index == kResultIndex) {
+    return dart_signature_.result_type();
+  }
+  const intptr_t real_arg_index = arg_index + dart_signature_params_start_at_;
+  ASSERT(!Array::Handle(dart_signature_.parameter_types()).IsNull());
+  ASSERT(real_arg_index <
+         Array::Handle(dart_signature_.parameter_types()).Length());
+  ASSERT(!AbstractType::Handle(dart_signature_.ParameterTypeAt(real_arg_index))
+              .IsNull());
+  return dart_signature_.ParameterTypeAt(real_arg_index);
+}
+
+bool BaseMarshaller::IsPointerCType(intptr_t arg_index) const {
+  return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
+         kPointerCid;
+}
+
+bool BaseMarshaller::IsPointerDartType(intptr_t arg_index) const {
+  return AbstractType::Handle(zone_, DartType(arg_index)).type_class_id() ==
+         kPointerCid;
+}
+
+bool BaseMarshaller::IsPointerPointer(intptr_t arg_index) const {
+  if (dart_signature_.parameter_types() == Array::null()) {
+    // TODO(https://dartbug.com/54173): BuildGraphOfSyncFfiCallback provides a
+    // function object with its type arguments not initialized.
+    return IsPointerCType(arg_index);
+  }
+  return IsPointerDartType(arg_index) && IsPointerCType(arg_index);
+}
+
+bool BaseMarshaller::IsTypedDataPointer(intptr_t arg_index) const {
+  if (!IsPointerCType(arg_index)) {
+    return false;
+  }
+  if (IsHandleCType(arg_index)) {
+    return false;
+  }
+
+  if (dart_signature_.parameter_types() == Array::null()) {
+    // TODO(https://dartbug.com/54173): BuildGraphOfSyncFfiCallback provides a
+    // function object with its type arguments not initialized. Change this
+    // to an assert when addressing that issue.
+    return false;
+  }
+
+  const auto& type = AbstractType::Handle(zone_, DartType(arg_index));
+  return type.type_class() ==
+         Thread::Current()->compiler_state().TypedDataClass().ptr();
+}
+
+static bool IsCompound(Zone* zone, const AbstractType& type) {
+  auto& compiler_state = Thread::Current()->compiler_state();
+  auto& cls = Class::Handle(zone, type.type_class());
+  if (cls.id() == compiler_state.CompoundClass().id() ||
+      cls.id() == compiler_state.ArrayClass().id()) {
+    return true;
+  }
+  cls ^= cls.SuperClass();
+  if (cls.id() == compiler_state.StructClass().id() ||
+      cls.id() == compiler_state.UnionClass().id()) {
+    return true;
+  }
+  return false;
+}
+
+bool BaseMarshaller::IsCompoundPointer(intptr_t arg_index) const {
+  if (!IsPointerCType(arg_index)) {
+    return false;
+  }
+  if (dart_signature_.parameter_types() == Array::null()) {
+    // TODO(https://dartbug.com/54173): BuildGraphOfSyncFfiCallback provides a
+    // function object with its type arguments not initialized.
+    return false;
+  }
+
+  const auto& dart_type = AbstractType::Handle(zone_, DartType(arg_index));
+  return IsCompound(this->zone_, dart_type);
+}
+
+bool BaseMarshaller::IsHandleCType(intptr_t arg_index) const {
+  return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
+         kFfiHandleCid;
+}
+
+bool BaseMarshaller::IsBool(intptr_t arg_index) const {
+  return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
+         kFfiBoolCid;
+}
+
 // Keep consistent with Function::FfiCSignatureReturnsStruct.
-bool BaseMarshaller::IsCompound(intptr_t arg_index) const {
-  const auto& type = AbstractType::Handle(zone_, CType(arg_index));
-  if (IsFfiTypeClassId(type.type_class_id())) {
-    return false;
-  }
-  const auto& cls = Class::Handle(this->zone_, type.type_class());
-  const auto& superClass = Class::Handle(this->zone_, cls.SuperClass());
-  const bool is_abi_specific_int =
-      String::Handle(this->zone_, superClass.UserVisibleName())
-          .Equals(Symbols::AbiSpecificInteger());
-  if (is_abi_specific_int) {
-    return false;
-  }
-#ifdef DEBUG
-  const bool is_struct =
-      String::Handle(this->zone_, superClass.UserVisibleName())
-          .Equals(Symbols::Struct());
-  const bool is_union =
-      String::Handle(this->zone_, superClass.UserVisibleName())
-          .Equals(Symbols::Union());
-  ASSERT(is_struct || is_union);
-#endif
-  return true;
+bool BaseMarshaller::IsCompoundCType(intptr_t arg_index) const {
+  const auto& c_type = AbstractType::Handle(zone_, CType(arg_index));
+  return IsCompound(this->zone_, c_type);
 }
 
 bool BaseMarshaller::ContainsHandles() const {
@@ -189,6 +262,10 @@
   const auto& loc = Location(arg_index);
   const auto& type = loc.payload_type();
 
+  if (IsCompoundPointer(arg_index)) {
+    // typed data base and offset.
+    return 2;
+  }
   if (type.IsPrimitive()) {
     // All non-struct arguments are 1 definition in IL. Even 64 bit values
     // on 32 bit architectures.
@@ -357,8 +434,8 @@
 Representation BaseMarshaller::RepInDart(intptr_t arg_index) const {
   // This should never be called on Pointers or Handles, which are specially
   // handled during marshalling/unmarshalling.
-  ASSERT(!IsHandle(arg_index));
-  ASSERT(!IsPointer(arg_index));
+  ASSERT(!IsHandleCType(arg_index));
+  ASSERT(!IsPointerPointer(arg_index));
   return Location(arg_index).payload_type().AsRepresentationOverApprox(zone_);
 }
 
@@ -368,12 +445,12 @@
   intptr_t arg_index = ArgumentIndex(def_index_global);
 
   // Handled appropriately in the subclasses.
-  ASSERT(!IsHandle(arg_index));
+  ASSERT(!IsHandleCType(arg_index));
 
   // The IL extracts the address stored in the Pointer object as an untagged
   // pointer before passing it to C, and creates a new Pointer object to store
   // the received untagged pointer when receiving a pointer from C.
-  if (IsPointer(arg_index)) return kUntagged;
+  if (IsPointerPointer(arg_index)) return kUntagged;
 
   const auto& location = Location(arg_index);
   if (location.container_type().IsPrimitive()) {
@@ -402,7 +479,7 @@
 
 Representation CallMarshaller::RepInFfiCall(intptr_t def_index_global) const {
   intptr_t arg_index = ArgumentIndex(def_index_global);
-  if (IsHandle(arg_index)) {
+  if (IsHandleCType(arg_index)) {
     // For FfiCall arguments, the FfiCall instruction takes a tagged pointer
     // from the IL. (It then creates a handle on the stack and passes a
     // pointer to the newly allocated handle to C.)
@@ -420,7 +497,8 @@
     return kTagged;
   }
   const auto& location = Location(arg_index);
-  if (location.IsPointerToMemory()) {
+  if (location.IsPointerToMemory() || IsCompoundPointer(arg_index) ||
+      IsTypedDataPointer(arg_index)) {
     // For arguments, the compound data being passed as a pointer is first
     // collected into a TypedData object by the IL, and that object is what is
     // passed to the FfiCall instruction. (The machine code generated by
@@ -435,6 +513,7 @@
       return kTagged;
     } else {
       ASSERT_EQUAL(def_index_in_arg, kOffsetInBytesIndex);
+      ASSERT(!IsTypedDataPointer(arg_index));
       return kUnboxedUword;
     }
   }
@@ -444,7 +523,7 @@
 Representation CallbackMarshaller::RepInFfiCall(
     intptr_t def_index_global) const {
   intptr_t arg_index = ArgumentIndex(def_index_global);
-  if (IsHandle(arg_index)) {
+  if (IsHandleCType(arg_index)) {
     // Dart objects are passed to C as untagged pointers to newly created
     // handles in the IL, and the ptr field of untagged pointers to handles are
     // extracted when the IL receives handles from C code.
@@ -533,7 +612,7 @@
   }
 
   // Force all handles to be Stack locations.
-  if (IsHandle(arg_index)) {
+  if (IsHandleCType(arg_index)) {
     return Location::RequiresStack();
   }
 
@@ -571,6 +650,16 @@
     }
   }
 
+  if (IsCompoundPointer(arg_index)) {
+    const intptr_t def_index_in_arg =
+        def_index_global - FirstDefinitionIndex(arg_index);
+    if (def_index_in_arg == kOffsetInBytesIndex) {
+      // The typed data is passed in the location from the calling convention.
+      // The offset in bytes can be passed in any location.
+      return Location::Any();
+    }
+  }
+
   if (loc.IsStack()) {
     return ConvertToAnyLocation(loc.AsStack(), RepInFfiCall(def_index_global));
   }
@@ -589,7 +678,7 @@
 }
 
 bool CallMarshaller::ReturnsCompound() const {
-  return IsCompound(compiler::ffi::kResultIndex);
+  return IsCompoundCType(compiler::ffi::kResultIndex);
 }
 
 intptr_t CallMarshaller::CompoundReturnSizeInBytes() const {
diff --git a/runtime/vm/compiler/ffi/marshaller.h b/runtime/vm/compiler/ffi/marshaller.h
index 1147434..2723658 100644
--- a/runtime/vm/compiler/ffi/marshaller.h
+++ b/runtime/vm/compiler/ffi/marshaller.h
@@ -106,30 +106,47 @@
   // Recurses into VarArgs if needed.
   AbstractTypePtr CType(intptr_t arg_index) const;
 
+  AbstractTypePtr DartType(intptr_t arg_index) const;
+
+ protected:
+  bool IsPointerDartType(intptr_t arg_index) const;
+  bool IsPointerCType(intptr_t arg_index) const;
+
+ public:
   // The Dart and C Type is Pointer.
   //
   // Requires boxing or unboxing the Pointer object to int.
-  bool IsPointer(intptr_t arg_index) const {
-    if (IsHandle(arg_index)) {
-      return false;
-    }
-    return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
-           kPointerCid;
-  }
+  bool IsPointerPointer(intptr_t arg_index) const;
 
-  // The C type is Handle.
+  // The Dart type is TypedData and the C type is Pointer.
+  //
+  // Requires passing the typed data base in as tagged pointer.
+  //
+  // TODO(https://dartbug.com/55444): The typed data address load could be
+  // done in IL.
+  bool IsTypedDataPointer(intptr_t arg_index) const;
+
+  // The Dart type is a compound (for example an Array or a TypedData+offset),
+  // and the C type is Pointer.
+  //
+  // Requires passing in two definitions in IL: TypedDataBase + offset.
+  //
+  // TODO(https://dartbug.com/55444): The typed data address load could be
+  // done in IL.
+  bool IsCompoundPointer(intptr_t arg_index) const;
+
+  // The C type is Handle, the Dart type can be anything.
   //
   // Requires passing the pointer to the Dart object in a handle.
-  bool IsHandle(intptr_t arg_index) const {
-    return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
-           kFfiHandleCid;
-  }
-  bool IsBool(intptr_t arg_index) const {
-    return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
-           kFfiBoolCid;
-  }
+  bool IsHandleCType(intptr_t arg_index) const;
 
-  bool IsCompound(intptr_t arg_index) const;
+  // The Dart and C Types are boolean.
+  //
+  // Requires converting the boolean into an int in IL.
+  bool IsBool(intptr_t arg_index) const;
+
+  // The Dart and C Types are compound (pass by value).
+  bool IsCompoundCType(intptr_t arg_index) const;
 
   // Treated as a null constant in Dart.
   bool IsVoid(intptr_t arg_index) const {
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 93bada8..152560f 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -5178,10 +5178,10 @@
 Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
     const compiler::ffi::BaseMarshaller& marshaller,
     intptr_t arg_index) {
-  ASSERT(!marshaller.IsCompound(arg_index));
+  ASSERT(!marshaller.IsCompoundCType(arg_index));
 
   Fragment body;
-  if (marshaller.IsPointer(arg_index)) {
+  if (marshaller.IsPointerPointer(arg_index)) {
     Class& result_class =
         Class::ZoneHandle(Z, IG->object_store()->ffi_pointer_class());
     // This class might only be instantiated as a return type of ffi calls.
@@ -5208,7 +5208,11 @@
                              StoreFieldInstr::Kind::kInitializing);
     body += DropTemporary(&address);  // address
     body += LoadLocal(result);
-  } else if (marshaller.IsHandle(arg_index)) {
+  } else if (marshaller.IsTypedDataPointer(arg_index)) {
+    UNREACHABLE();  // Only supported for FFI call arguments.
+  } else if (marshaller.IsCompoundPointer(arg_index)) {
+    UNREACHABLE();  // Only supported for FFI call arguments.
+  } else if (marshaller.IsHandleCType(arg_index)) {
     // The top of the stack is a Dart_Handle, so retrieve the tagged pointer
     // out of it.
     body += LoadNativeField(Slot::LocalHandle_ptr());
@@ -5235,15 +5239,24 @@
 
 Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
     const compiler::ffi::BaseMarshaller& marshaller,
-    intptr_t arg_index) {
-  ASSERT(!marshaller.IsCompound(arg_index));
+    intptr_t arg_index,
+    LocalVariable* variable) {
+  ASSERT(!marshaller.IsCompoundCType(arg_index));
 
   Fragment body;
-  if (marshaller.IsPointer(arg_index)) {
+  if (marshaller.IsPointerPointer(arg_index)) {
     // This can only be Pointer, so it is safe to load the data field.
     body += LoadNativeField(Slot::PointerBase_data(),
                             InnerPointerAccess::kCannotBeInnerPointer);
-  } else if (marshaller.IsHandle(arg_index)) {
+  } else if (marshaller.IsTypedDataPointer(arg_index)) {
+    // Nothing to do. Unwrap in `FfiCallInstr::EmitNativeCode`.
+  } else if (marshaller.IsCompoundPointer(arg_index)) {
+    ASSERT(variable != nullptr);
+    body += LoadTypedDataBaseFromCompound();
+    body += LoadLocal(variable);  // User-defined struct.
+    body += LoadOffsetInBytesFromCompound();
+    body += UnboxTruncate(kUnboxedWord);
+  } else if (marshaller.IsHandleCType(arg_index)) {
     // FfiCallInstr specifies all handle locations as Stack, and will pass a
     // pointer to the stack slot as the native handle argument. Therefore the
     // only handles that need wrapping are function results.
@@ -5430,7 +5443,7 @@
   // catch our own null errors.
   const intptr_t num_args = marshaller.num_args();
   for (intptr_t i = 0; i < num_args; i++) {
-    if (marshaller.IsHandle(i)) {
+    if (marshaller.IsHandleCType(i)) {
       continue;
     }
     body += LoadLocal(parsed_function_->ParameterVariable(
@@ -5480,7 +5493,7 @@
 
   // Unbox and push the arguments.
   for (intptr_t i = 0; i < marshaller.num_args(); i++) {
-    if (marshaller.IsCompound(i)) {
+    if (marshaller.IsCompoundCType(i)) {
       body += FfiCallConvertCompoundArgumentToNative(
           parsed_function_->ParameterVariable(first_argument_parameter_offset +
                                               i),
@@ -5491,8 +5504,11 @@
       // FfiCallInstr specifies all handle locations as Stack, and will pass a
       // pointer to the stack slot as the native handle argument.
       // Therefore we do not need to wrap handles.
-      if (!marshaller.IsHandle(i)) {
-        body += FfiConvertPrimitiveToNative(marshaller, i);
+      if (!marshaller.IsHandleCType(i)) {
+        body += FfiConvertPrimitiveToNative(
+            marshaller, i,
+            parsed_function_->ParameterVariable(
+                first_argument_parameter_offset + i));
       }
     }
   }
@@ -5516,7 +5532,7 @@
     body += DropTemporary(&def);
   }
 
-  if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
+  if (marshaller.IsCompoundCType(compiler::ffi::kResultIndex)) {
     body += FfiCallConvertCompoundReturnToDart(marshaller,
                                                compiler::ffi::kResultIndex);
   } else {
@@ -5585,7 +5601,7 @@
     defs->Add(def);
   }
 
-  if (marshaller.IsCompound(arg_index)) {
+  if (marshaller.IsCompoundCType(arg_index)) {
     fragment +=
         FfiCallbackConvertCompoundArgumentToDart(marshaller, arg_index, defs);
   } else {
@@ -5665,13 +5681,13 @@
                        ICData::kNoRebind);
   }
 
-  if (!marshaller.IsHandle(compiler::ffi::kResultIndex)) {
+  if (!marshaller.IsHandleCType(compiler::ffi::kResultIndex)) {
     body += CheckNullOptimized(
         String::ZoneHandle(Z, Symbols::New(H.thread(), "return_value")),
         CheckNullInstr::kArgumentError);
   }
 
-  if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
+  if (marshaller.IsCompoundCType(compiler::ffi::kResultIndex)) {
     body += FfiCallbackConvertCompoundReturnToNative(
         marshaller, compiler::ffi::kResultIndex);
   } else {
@@ -5694,16 +5710,16 @@
     // The exceptional return is always null -- return nullptr instead.
     ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
     catch_body += UnboxedIntConstant(0, kUnboxedIntPtr);
-  } else if (marshaller.IsPointer(compiler::ffi::kResultIndex)) {
+  } else if (marshaller.IsPointerPointer(compiler::ffi::kResultIndex)) {
     // The exceptional return is always null -- return nullptr instead.
     ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
     catch_body += UnboxedIntConstant(0, kUnboxedAddress);
     catch_body += ConvertUnboxedToUntagged();
-  } else if (marshaller.IsHandle(compiler::ffi::kResultIndex)) {
+  } else if (marshaller.IsHandleCType(compiler::ffi::kResultIndex)) {
     catch_body += UnhandledException();
     catch_body +=
         FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex);
-  } else if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
+  } else if (marshaller.IsCompoundCType(compiler::ffi::kResultIndex)) {
     ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
     // Manufacture empty result.
     const intptr_t size =
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index aded1e8..1b72ca0 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -335,9 +335,13 @@
   // semantics of FFI argument translation.
   //
   // Works for FFI call arguments, and FFI callback return values.
+  //
+  // If `marshaller.IsCompoundPointer(arg_index)`, then [variable] must point to
+  // a valid LocalVariable.
   Fragment FfiConvertPrimitiveToNative(
       const compiler::ffi::BaseMarshaller& marshaller,
-      intptr_t arg_index);
+      intptr_t arg_index,
+      LocalVariable* variable = nullptr);
 
   // Pops an unboxed native value, and pushes a Dart object, according to the
   // semantics of FFI argument translation.
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 15fc218..1f68845 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -25,6 +25,7 @@
   V(ApiError, "ApiError")                                                      \
   V(ArgDescVar, ":arg_desc")                                                   \
   V(ArgumentError, "ArgumentError")                                            \
+  V(Array, "Array")                                                            \
   V(StateError, "StateError")                                                  \
   V(AssertionError, "_AssertionError")                                         \
   V(AssignIndexToken, "[]=")                                                   \
@@ -254,6 +255,7 @@
   V(Type, "Type")                                                              \
   V(TypeArguments, "TypeArguments")                                            \
   V(TypeArgumentsParameter, ":type_arguments")                                 \
+  V(TypedData, "TypedData")                                                    \
   V(TypeError, "_TypeError")                                                   \
   V(TypeParameters, "TypeParameters")                                          \
   V(TypeQuote, "type '")                                                       \
diff --git a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
index 633513a..9895edb0 100644
--- a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
@@ -9,7 +9,7 @@
 
 @pragma("vm:entry-point")
 @patch
-abstract final class _Compound implements NativeType {}
+final class _Compound implements NativeType {}
 
 @pragma("vm:entry-point")
 @patch
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 9e5ddd2..dbffdfc 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -1202,6 +1202,316 @@
   external void operator []=(int index, bool value);
 }
 
+@Since('3.5')
+extension Int8ListAddress on Int8List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int8> pointer);
+  ///
+  /// void main() {
+  ///   final list = Int8List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Int8> get address;
+}
+
+@Since('3.5')
+extension Int16ListAddress on Int16List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Int16>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int16> pointer);
+  ///
+  /// void main() {
+  ///   final list = Int16List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Int16> get address;
+}
+
+@Since('3.5')
+extension Int32ListAddress on Int32List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Int32>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int32> pointer);
+  ///
+  /// void main() {
+  ///   final list = Int32List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Int32> get address;
+}
+
+@Since('3.5')
+extension Int64ListAddress on Int64List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Int64>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int64> pointer);
+  ///
+  /// void main() {
+  ///   final list = Int64List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Int64> get address;
+}
+
+@Since('3.5')
+extension Uint8ListAddress on Uint8List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Uint8>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Uint8> pointer);
+  ///
+  /// void main() {
+  ///   final list = Uint8List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Uint8> get address;
+}
+
+@Since('3.5')
+extension Uint16ListAddress on Uint16List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Uint16>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Uint16> pointer);
+  ///
+  /// void main() {
+  ///   final list = Uint16List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Uint16> get address;
+}
+
+@Since('3.5')
+extension Uint32ListAddress on Uint32List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Uint32>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Uint32> pointer);
+  ///
+  /// void main() {
+  ///   final list = Uint32List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Uint32> get address;
+}
+
+@Since('3.5')
+extension Uint64ListAddress on Uint64List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Uint64>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Uint64> pointer);
+  ///
+  /// void main() {
+  ///   final list = Uint64List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Uint64> get address;
+}
+
+@Since('3.5')
+extension Float32ListAddress on Float32List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Float>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Float> pointer);
+  ///
+  /// void main() {
+  ///   final list = Float32List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Float> get address;
+}
+
+@Since('3.5')
+extension Float64ListAddress on Float64List {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address`
+  /// can only occurr as an entire argument expression in the invocation of
+  /// a leaf [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Double>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Double> pointer);
+  ///
+  /// void main() {
+  ///   final list = Float64List(10);
+  ///   myFunction(list.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Double> get address;
+}
+
 //
 // End of generated code.
 //
@@ -1471,6 +1781,264 @@
   external void operator []=(int index, int value);
 }
 
+@Since('3.5')
+extension ArrayAddress<T extends NativeType> on Array<T> {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address` can
+  /// only occurr as an entire argument expression in the invocation of a leaf
+  /// [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart
+  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int8> pointer);
+  ///
+  /// final class MyStruct extends Struct {
+  ///   @Array(10)
+  ///   external Array<Int8> array;
+  /// }
+  ///
+  /// void main() {
+  ///   final array = Struct.create<MyStruct>().array;
+  ///   myFunction(array.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<T> get address;
+}
+
+@Since('3.5')
+extension StructAddress<T extends Struct> on T {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address` can
+  /// only occurr as an entire argument expression in the invocation of a leaf
+  /// [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart
+  /// @Native<Void Function(Pointer<MyStruct>)>(isLeaf: true)
+  /// external void myFunction(Pointer<MyStruct> pointer);
+  ///
+  /// final class MyStruct extends Struct {
+  ///   @Int8()
+  ///   external int x;
+  /// }
+  ///
+  /// void main() {
+  ///   final myStruct = Struct.create<MyStruct>();
+  ///   myFunction(myStruct.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<T> get address;
+}
+
+@Since('3.5')
+extension UnionAddress<T extends Union> on T {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address` can
+  /// only occurr as an entire argument expression in the invocation of a leaf
+  /// [Native] external function.
+  ///
+  /// Example:
+  ///
+  /// ```dart
+  /// @Native<Void Function(Pointer<MyUnion>)>(isLeaf: true)
+  /// external void myFunction(Pointer<MyUnion> pointer);
+  ///
+  /// final class MyUnion extends Union {
+  ///   @Int8()
+  ///   external int x;
+  /// }
+  ///
+  /// void main() {
+  ///   final myUnion = Union.create<MyUnion>();
+  ///   myFunction(myUnion.address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<T> get address;
+}
+
+@Since('3.5')
+extension IntAddress on int {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address` can
+  /// only occurr as an entire argument expression in the invocation of a leaf
+  /// [Native] external function.
+  ///
+  /// Can only be used on fields of [Struct] subtypes, fields of [Union]
+  /// subtypes, [Array] elements, or [TypedData] elements. In other words, the
+  /// number whose address is being accessed must itself be acccessed through a
+  /// [Struct], [Union], [Array], or [TypedData].
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int8> pointer);
+  ///
+  /// final class MyStruct extends Struct {
+  ///   @Int8()
+  ///   external int x;
+  ///
+  ///   @Int8()
+  ///   external int y;
+  ///
+  ///   @Array(10)
+  ///   external Array<Int8> array;
+  /// }
+  ///
+  /// void main() {
+  ///   final myStruct = Struct.create<MyStruct>();
+  ///   myFunction(myStruct.y.address);
+  ///   myFunction(myStruct.array[5].address);
+  ///
+  ///   final list = Int8List(10);
+  ///   myFunction(list[5].address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Never> address;
+}
+
+@Since('3.5')
+extension DoubleAddress on double {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address` can
+  /// only occurr as an entire argument expression in the invocation of a leaf
+  /// [Native] external function.
+  ///
+  /// Can only be used on fields of [Struct] subtypes, fields of [Union]
+  /// subtypes, [Array] elements, or [TypedData] elements. In other words, the
+  /// number whose address is being accessed must itself be acccessed through a
+  /// [Struct], [Union], [Array], or [TypedData].
+  ///
+  /// Example:
+  ///
+  /// ```dart import:typed_data
+  /// @Native<Void Function(Pointer<Float>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Float> pointer);
+  ///
+  /// final class MyStruct extends Struct {
+  ///   @Float()
+  ///   external double x;
+  ///
+  ///   @Float()
+  ///   external double y;
+  ///
+  ///   @Array(10)
+  ///   external Array<Float> array;
+  /// }
+  ///
+  /// void main() {
+  ///   final myStruct = Struct.create<MyStruct>();
+  ///   myFunction(myStruct.y.address);
+  ///   myFunction(myStruct.array[5].address);
+  ///
+  ///   final list = Float32List(10);
+  ///   myFunction(list[5].address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Never> address;
+}
+
+@Since('3.5')
+extension BoolAddress on bool {
+  /// The memory address of the underlying data.
+  ///
+  /// An expression of the form `expression.address` denoting this `address` can
+  /// only occurr as an entire argument expression in the invocation of a leaf
+  /// [Native] external function.
+  ///
+  /// Can only be used on fields of [Struct] subtypes, fields of [Union]
+  /// subtypes, or [Array] elements. In other words, the boolean whose address
+  /// is being accessed must itself be acccessed through a [Struct], [Union] or
+  /// [Array].
+  ///
+  /// Example:
+  ///
+  /// ```dart
+  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
+  /// external void myFunction(Pointer<Int8> pointer);
+  ///
+  /// final class MyStruct extends Struct {
+  ///   @Bool()
+  ///   external bool x;
+  ///
+  ///   @Bool()
+  ///   external bool y;
+  ///
+  ///   @Array(10)
+  ///   external Array<Bool> array;
+  /// }
+  ///
+  /// void main() {
+  ///   final myStruct = Struct.create<MyStruct>();
+  ///   myFunction(myStruct.y.address);
+  ///   myFunction(myStruct.array[5].address);
+  /// }
+  /// ```
+  ///
+  /// The expression before `.address` is evaluated like the left-hand-side of
+  /// an assignment, to something that gives access to the storage behind the
+  /// expression, which can be used both for reading and writing. The `.address`
+  /// then gives a native pointer to that storage.
+  ///
+  /// The `.address` is evaluated just before calling into native code when
+  /// invoking a leaf [Native] external function. This ensures the Dart garbage
+  /// collector will not move the object that the address points in to.
+  external Pointer<Never> address;
+}
+
 /// Extension to retrieve the native `Dart_Port` from a [SendPort].
 @Since('2.7')
 extension NativePort on SendPort {
diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart
index 3449f70..9954ae4 100644
--- a/sdk/lib/ffi/struct.dart
+++ b/sdk/lib/ffi/struct.dart
@@ -4,12 +4,15 @@
 
 part of dart.ffi;
 
+/// A memory range, represented by its starting address.
+///
 /// Shared supertype of the FFI compound [Struct], [Union], and [Array] types.
 ///
-/// FFI struct and union types should extend [Struct] and [Union]. For more
-/// information see the documentation on those classes.
+/// This class is not abstract because instances can be created as an anonymous
+/// representation of a memory area, with no structure on top. (In particular,
+/// during the transformation of `.address` in FFI leaf calls.)
 @pragma("wasm:entry-point")
-abstract final class _Compound implements NativeType {
+final class _Compound implements NativeType {
   /// The underlying [TypedData] or [Pointer] that a subtype uses.
   @pragma("vm:entry-point")
   final Object _typedDataBase;
diff --git a/tests/ffi/address_of_array_generated_test.dart b/tests/ffi/address_of_array_generated_test.dart
new file mode 100644
index 0000000..7fdd6ed
--- /dev/null
+++ b/tests/ffi/address_of_array_generated_test.dart
@@ -0,0 +1,594 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+// ignore_for_file: unused_import
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import 'address_of_generated_shared.dart';
+import 'address_of_shared.dart';
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
+
+void main() {
+  // Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
+  dlopenGlobalPlatformSpecific('ffi_test_functions');
+
+  for (int i = 0; i < 100; ++i) {
+    testAddressOfInt8Array();
+    testAddressOfInt8ArrayElementAt();
+    testAddressOfInt16Array();
+    testAddressOfInt16ArrayElementAt();
+    testAddressOfInt32Array();
+    testAddressOfInt32ArrayElementAt();
+    testAddressOfInt64Array();
+    testAddressOfInt64ArrayElementAt();
+    testAddressOfUint8Array();
+    testAddressOfUint8ArrayElementAt();
+    testAddressOfUint16Array();
+    testAddressOfUint16ArrayElementAt();
+    testAddressOfUint32Array();
+    testAddressOfUint32ArrayElementAt();
+    testAddressOfUint64Array();
+    testAddressOfUint64ArrayElementAt();
+    testAddressOfFloatArray();
+    testAddressOfFloatArrayElementAt();
+    testAddressOfDoubleArray();
+    testAddressOfDoubleArrayElementAt();
+    testAddressOfBoolArray();
+    testAddressOfBoolArrayElementAt();
+  }
+}
+
+final class Int8ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Int8> array;
+}
+
+Array<Int8> makeInt8Array(int length) {
+  assert(length == 20);
+  final typedData = makeInt8List(length);
+  final struct = Struct.create<Int8ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfInt8Array() {
+  const length = 20;
+  final array = makeInt8Array(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = takeInt8Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt8ArrayElementAt() {
+  const length = 20;
+  final array = makeInt8Array(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = takeInt8PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Int16ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Int16> array;
+}
+
+Array<Int16> makeInt16Array(int length) {
+  assert(length == 20);
+  final typedData = makeInt16List(length);
+  final struct = Struct.create<Int16ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfInt16Array() {
+  const length = 20;
+  final array = makeInt16Array(length);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeInt16Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt16ArrayElementAt() {
+  const length = 20;
+  final array = makeInt16Array(length);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeInt16PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Int32ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Int32> array;
+}
+
+Array<Int32> makeInt32Array(int length) {
+  assert(length == 20);
+  final typedData = makeInt32List(length);
+  final struct = Struct.create<Int32ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfInt32Array() {
+  const length = 20;
+  final array = makeInt32Array(length);
+  final expectedResult = makeExpectedResultInt32(0, length);
+  final result = takeInt32Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt32ArrayElementAt() {
+  const length = 20;
+  final array = makeInt32Array(length);
+  final expectedResult = makeExpectedResultInt32(0, length);
+  final result = takeInt32PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Int64ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Int64> array;
+}
+
+Array<Int64> makeInt64Array(int length) {
+  assert(length == 20);
+  final typedData = makeInt64List(length);
+  final struct = Struct.create<Int64ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfInt64Array() {
+  const length = 20;
+  final array = makeInt64Array(length);
+  final expectedResult = makeExpectedResultInt64(0, length);
+  final result = takeInt64Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt64ArrayElementAt() {
+  const length = 20;
+  final array = makeInt64Array(length);
+  final expectedResult = makeExpectedResultInt64(0, length);
+  final result = takeInt64PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint8ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Uint8> array;
+}
+
+Array<Uint8> makeUint8Array(int length) {
+  assert(length == 20);
+  final typedData = makeUint8List(length);
+  final struct = Struct.create<Uint8ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfUint8Array() {
+  const length = 20;
+  final array = makeUint8Array(length);
+  final expectedResult = makeExpectedResultUint8(0, length);
+  final result = takeUint8Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint8ArrayElementAt() {
+  const length = 20;
+  final array = makeUint8Array(length);
+  final expectedResult = makeExpectedResultUint8(0, length);
+  final result = takeUint8PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint16ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Uint16> array;
+}
+
+Array<Uint16> makeUint16Array(int length) {
+  assert(length == 20);
+  final typedData = makeUint16List(length);
+  final struct = Struct.create<Uint16ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfUint16Array() {
+  const length = 20;
+  final array = makeUint16Array(length);
+  final expectedResult = makeExpectedResultUint16(0, length);
+  final result = takeUint16Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint16ArrayElementAt() {
+  const length = 20;
+  final array = makeUint16Array(length);
+  final expectedResult = makeExpectedResultUint16(0, length);
+  final result = takeUint16PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint32ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Uint32> array;
+}
+
+Array<Uint32> makeUint32Array(int length) {
+  assert(length == 20);
+  final typedData = makeUint32List(length);
+  final struct = Struct.create<Uint32ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfUint32Array() {
+  const length = 20;
+  final array = makeUint32Array(length);
+  final expectedResult = makeExpectedResultUint32(0, length);
+  final result = takeUint32Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint32ArrayElementAt() {
+  const length = 20;
+  final array = makeUint32Array(length);
+  final expectedResult = makeExpectedResultUint32(0, length);
+  final result = takeUint32PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint64ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Uint64> array;
+}
+
+Array<Uint64> makeUint64Array(int length) {
+  assert(length == 20);
+  final typedData = makeUint64List(length);
+  final struct = Struct.create<Uint64ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfUint64Array() {
+  const length = 20;
+  final array = makeUint64Array(length);
+  final expectedResult = makeExpectedResultUint64(0, length);
+  final result = takeUint64Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint64ArrayElementAt() {
+  const length = 20;
+  final array = makeUint64Array(length);
+  final expectedResult = makeExpectedResultUint64(0, length);
+  final result = takeUint64PointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class FloatArrayStruct extends Struct {
+  @Array(20)
+  external Array<Float> array;
+}
+
+Array<Float> makeFloatArray(int length) {
+  assert(length == 20);
+  final typedData = makeFloat32List(length);
+  final struct = Struct.create<FloatArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfFloatArray() {
+  const length = 20;
+  final array = makeFloatArray(length);
+  final expectedResult = makeExpectedResultFloat(0, length);
+  final result = takeFloatPointer(array.address, length);
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloatArrayElementAt() {
+  const length = 20;
+  final array = makeFloatArray(length);
+  final expectedResult = makeExpectedResultFloat(0, length);
+  final result = takeFloatPointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+final class DoubleArrayStruct extends Struct {
+  @Array(20)
+  external Array<Double> array;
+}
+
+Array<Double> makeDoubleArray(int length) {
+  assert(length == 20);
+  final typedData = makeFloat64List(length);
+  final struct = Struct.create<DoubleArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfDoubleArray() {
+  const length = 20;
+  final array = makeDoubleArray(length);
+  final expectedResult = makeExpectedResultDouble(0, length);
+  final result = takeDoublePointer(array.address, length);
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfDoubleArrayElementAt() {
+  const length = 20;
+  final array = makeDoubleArray(length);
+  final expectedResult = makeExpectedResultDouble(0, length);
+  final result = takeDoublePointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+final class BoolArrayStruct extends Struct {
+  @Array(20)
+  external Array<Bool> array;
+}
+
+Array<Bool> makeBoolArray(int length) {
+  assert(length == 20);
+  final typedData = makeBoolList(length);
+  final struct = Struct.create<BoolArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfBoolArray() {
+  const length = 20;
+  final array = makeBoolArray(length);
+  final expectedResult = makeExpectedResultBool(0, length);
+  final result = takeBoolPointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfBoolArrayElementAt() {
+  const length = 20;
+  final array = makeBoolArray(length);
+  final expectedResult = makeExpectedResultBool(0, length);
+  final result = takeBoolPointerMany(
+    array[0].address,
+    array[1].address,
+    array[2].address,
+    array[3].address,
+    array[4].address,
+    array[5].address,
+    array[6].address,
+    array[7].address,
+    array[8].address,
+    array[9].address,
+    array[10].address,
+    array[11].address,
+    array[12].address,
+    array[13].address,
+    array[14].address,
+    array[15].address,
+    array[16].address,
+    array[17].address,
+    array[18].address,
+    array[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
diff --git a/tests/ffi/address_of_generated_shared.dart b/tests/ffi/address_of_generated_shared.dart
new file mode 100644
index 0000000..29cafb9
--- /dev/null
+++ b/tests/ffi/address_of_generated_shared.dart
@@ -0,0 +1,755 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+// ignore_for_file: unused_import
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import 'address_of_generated_shared.dart';
+import 'address_of_shared.dart';
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
+
+@Native<Int8 Function(Pointer<Int8>, Size)>(
+    symbol: 'TakeInt8Pointer', isLeaf: true)
+external int takeInt8Pointer(Pointer<Int8> pointer, int length);
+
+@Native<
+    Int8 Function(
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+      Pointer<Int8>,
+    )>(symbol: 'TakeInt8PointerMany', isLeaf: true)
+external int takeInt8PointerMany(
+  Pointer<Int8> pointer0,
+  Pointer<Int8> pointer1,
+  Pointer<Int8> pointer2,
+  Pointer<Int8> pointer3,
+  Pointer<Int8> pointer4,
+  Pointer<Int8> pointer5,
+  Pointer<Int8> pointer6,
+  Pointer<Int8> pointer7,
+  Pointer<Int8> pointer8,
+  Pointer<Int8> pointer9,
+  Pointer<Int8> pointer10,
+  Pointer<Int8> pointer11,
+  Pointer<Int8> pointer12,
+  Pointer<Int8> pointer13,
+  Pointer<Int8> pointer14,
+  Pointer<Int8> pointer15,
+  Pointer<Int8> pointer16,
+  Pointer<Int8> pointer17,
+  Pointer<Int8> pointer18,
+  Pointer<Int8> pointer19,
+);
+
+Int8List makeInt8List(int length) {
+  final typedData = Int8List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultInt8(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Int16 Function(Pointer<Int16>, Size)>(
+    symbol: 'TakeInt16Pointer', isLeaf: true)
+external int takeInt16Pointer(Pointer<Int16> pointer, int length);
+
+@Native<
+    Int16 Function(
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+      Pointer<Int16>,
+    )>(symbol: 'TakeInt16PointerMany', isLeaf: true)
+external int takeInt16PointerMany(
+  Pointer<Int16> pointer0,
+  Pointer<Int16> pointer1,
+  Pointer<Int16> pointer2,
+  Pointer<Int16> pointer3,
+  Pointer<Int16> pointer4,
+  Pointer<Int16> pointer5,
+  Pointer<Int16> pointer6,
+  Pointer<Int16> pointer7,
+  Pointer<Int16> pointer8,
+  Pointer<Int16> pointer9,
+  Pointer<Int16> pointer10,
+  Pointer<Int16> pointer11,
+  Pointer<Int16> pointer12,
+  Pointer<Int16> pointer13,
+  Pointer<Int16> pointer14,
+  Pointer<Int16> pointer15,
+  Pointer<Int16> pointer16,
+  Pointer<Int16> pointer17,
+  Pointer<Int16> pointer18,
+  Pointer<Int16> pointer19,
+);
+
+Int16List makeInt16List(int length) {
+  final typedData = Int16List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultInt16(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Int32 Function(Pointer<Int32>, Size)>(
+    symbol: 'TakeInt32Pointer', isLeaf: true)
+external int takeInt32Pointer(Pointer<Int32> pointer, int length);
+
+@Native<
+    Int32 Function(
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+      Pointer<Int32>,
+    )>(symbol: 'TakeInt32PointerMany', isLeaf: true)
+external int takeInt32PointerMany(
+  Pointer<Int32> pointer0,
+  Pointer<Int32> pointer1,
+  Pointer<Int32> pointer2,
+  Pointer<Int32> pointer3,
+  Pointer<Int32> pointer4,
+  Pointer<Int32> pointer5,
+  Pointer<Int32> pointer6,
+  Pointer<Int32> pointer7,
+  Pointer<Int32> pointer8,
+  Pointer<Int32> pointer9,
+  Pointer<Int32> pointer10,
+  Pointer<Int32> pointer11,
+  Pointer<Int32> pointer12,
+  Pointer<Int32> pointer13,
+  Pointer<Int32> pointer14,
+  Pointer<Int32> pointer15,
+  Pointer<Int32> pointer16,
+  Pointer<Int32> pointer17,
+  Pointer<Int32> pointer18,
+  Pointer<Int32> pointer19,
+);
+
+Int32List makeInt32List(int length) {
+  final typedData = Int32List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultInt32(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Int64 Function(Pointer<Int64>, Size)>(
+    symbol: 'TakeInt64Pointer', isLeaf: true)
+external int takeInt64Pointer(Pointer<Int64> pointer, int length);
+
+@Native<
+    Int64 Function(
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+      Pointer<Int64>,
+    )>(symbol: 'TakeInt64PointerMany', isLeaf: true)
+external int takeInt64PointerMany(
+  Pointer<Int64> pointer0,
+  Pointer<Int64> pointer1,
+  Pointer<Int64> pointer2,
+  Pointer<Int64> pointer3,
+  Pointer<Int64> pointer4,
+  Pointer<Int64> pointer5,
+  Pointer<Int64> pointer6,
+  Pointer<Int64> pointer7,
+  Pointer<Int64> pointer8,
+  Pointer<Int64> pointer9,
+  Pointer<Int64> pointer10,
+  Pointer<Int64> pointer11,
+  Pointer<Int64> pointer12,
+  Pointer<Int64> pointer13,
+  Pointer<Int64> pointer14,
+  Pointer<Int64> pointer15,
+  Pointer<Int64> pointer16,
+  Pointer<Int64> pointer17,
+  Pointer<Int64> pointer18,
+  Pointer<Int64> pointer19,
+);
+
+Int64List makeInt64List(int length) {
+  final typedData = Int64List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultInt64(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i % 2 == 0 ? i : -i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Uint8 Function(Pointer<Uint8>, Size)>(
+    symbol: 'TakeUint8Pointer', isLeaf: true)
+external int takeUint8Pointer(Pointer<Uint8> pointer, int length);
+
+@Native<
+    Uint8 Function(
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+      Pointer<Uint8>,
+    )>(symbol: 'TakeUint8PointerMany', isLeaf: true)
+external int takeUint8PointerMany(
+  Pointer<Uint8> pointer0,
+  Pointer<Uint8> pointer1,
+  Pointer<Uint8> pointer2,
+  Pointer<Uint8> pointer3,
+  Pointer<Uint8> pointer4,
+  Pointer<Uint8> pointer5,
+  Pointer<Uint8> pointer6,
+  Pointer<Uint8> pointer7,
+  Pointer<Uint8> pointer8,
+  Pointer<Uint8> pointer9,
+  Pointer<Uint8> pointer10,
+  Pointer<Uint8> pointer11,
+  Pointer<Uint8> pointer12,
+  Pointer<Uint8> pointer13,
+  Pointer<Uint8> pointer14,
+  Pointer<Uint8> pointer15,
+  Pointer<Uint8> pointer16,
+  Pointer<Uint8> pointer17,
+  Pointer<Uint8> pointer18,
+  Pointer<Uint8> pointer19,
+);
+
+Uint8List makeUint8List(int length) {
+  final typedData = Uint8List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultUint8(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Uint16 Function(Pointer<Uint16>, Size)>(
+    symbol: 'TakeUint16Pointer', isLeaf: true)
+external int takeUint16Pointer(Pointer<Uint16> pointer, int length);
+
+@Native<
+    Uint16 Function(
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+      Pointer<Uint16>,
+    )>(symbol: 'TakeUint16PointerMany', isLeaf: true)
+external int takeUint16PointerMany(
+  Pointer<Uint16> pointer0,
+  Pointer<Uint16> pointer1,
+  Pointer<Uint16> pointer2,
+  Pointer<Uint16> pointer3,
+  Pointer<Uint16> pointer4,
+  Pointer<Uint16> pointer5,
+  Pointer<Uint16> pointer6,
+  Pointer<Uint16> pointer7,
+  Pointer<Uint16> pointer8,
+  Pointer<Uint16> pointer9,
+  Pointer<Uint16> pointer10,
+  Pointer<Uint16> pointer11,
+  Pointer<Uint16> pointer12,
+  Pointer<Uint16> pointer13,
+  Pointer<Uint16> pointer14,
+  Pointer<Uint16> pointer15,
+  Pointer<Uint16> pointer16,
+  Pointer<Uint16> pointer17,
+  Pointer<Uint16> pointer18,
+  Pointer<Uint16> pointer19,
+);
+
+Uint16List makeUint16List(int length) {
+  final typedData = Uint16List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultUint16(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Uint32 Function(Pointer<Uint32>, Size)>(
+    symbol: 'TakeUint32Pointer', isLeaf: true)
+external int takeUint32Pointer(Pointer<Uint32> pointer, int length);
+
+@Native<
+    Uint32 Function(
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+      Pointer<Uint32>,
+    )>(symbol: 'TakeUint32PointerMany', isLeaf: true)
+external int takeUint32PointerMany(
+  Pointer<Uint32> pointer0,
+  Pointer<Uint32> pointer1,
+  Pointer<Uint32> pointer2,
+  Pointer<Uint32> pointer3,
+  Pointer<Uint32> pointer4,
+  Pointer<Uint32> pointer5,
+  Pointer<Uint32> pointer6,
+  Pointer<Uint32> pointer7,
+  Pointer<Uint32> pointer8,
+  Pointer<Uint32> pointer9,
+  Pointer<Uint32> pointer10,
+  Pointer<Uint32> pointer11,
+  Pointer<Uint32> pointer12,
+  Pointer<Uint32> pointer13,
+  Pointer<Uint32> pointer14,
+  Pointer<Uint32> pointer15,
+  Pointer<Uint32> pointer16,
+  Pointer<Uint32> pointer17,
+  Pointer<Uint32> pointer18,
+  Pointer<Uint32> pointer19,
+);
+
+Uint32List makeUint32List(int length) {
+  final typedData = Uint32List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultUint32(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Uint64 Function(Pointer<Uint64>, Size)>(
+    symbol: 'TakeUint64Pointer', isLeaf: true)
+external int takeUint64Pointer(Pointer<Uint64> pointer, int length);
+
+@Native<
+    Uint64 Function(
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+      Pointer<Uint64>,
+    )>(symbol: 'TakeUint64PointerMany', isLeaf: true)
+external int takeUint64PointerMany(
+  Pointer<Uint64> pointer0,
+  Pointer<Uint64> pointer1,
+  Pointer<Uint64> pointer2,
+  Pointer<Uint64> pointer3,
+  Pointer<Uint64> pointer4,
+  Pointer<Uint64> pointer5,
+  Pointer<Uint64> pointer6,
+  Pointer<Uint64> pointer7,
+  Pointer<Uint64> pointer8,
+  Pointer<Uint64> pointer9,
+  Pointer<Uint64> pointer10,
+  Pointer<Uint64> pointer11,
+  Pointer<Uint64> pointer12,
+  Pointer<Uint64> pointer13,
+  Pointer<Uint64> pointer14,
+  Pointer<Uint64> pointer15,
+  Pointer<Uint64> pointer16,
+  Pointer<Uint64> pointer17,
+  Pointer<Uint64> pointer18,
+  Pointer<Uint64> pointer19,
+);
+
+Uint64List makeUint64List(int length) {
+  final typedData = Uint64List(length);
+  for (int i = 0; i < length; i++) {
+    final value = i;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+int makeExpectedResultUint64(int start, int end) {
+  int expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = i;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Float Function(Pointer<Float>, Size)>(
+    symbol: 'TakeFloatPointer', isLeaf: true)
+external double takeFloatPointer(Pointer<Float> pointer, int length);
+
+@Native<
+    Float Function(
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+      Pointer<Float>,
+    )>(symbol: 'TakeFloatPointerMany', isLeaf: true)
+external double takeFloatPointerMany(
+  Pointer<Float> pointer0,
+  Pointer<Float> pointer1,
+  Pointer<Float> pointer2,
+  Pointer<Float> pointer3,
+  Pointer<Float> pointer4,
+  Pointer<Float> pointer5,
+  Pointer<Float> pointer6,
+  Pointer<Float> pointer7,
+  Pointer<Float> pointer8,
+  Pointer<Float> pointer9,
+  Pointer<Float> pointer10,
+  Pointer<Float> pointer11,
+  Pointer<Float> pointer12,
+  Pointer<Float> pointer13,
+  Pointer<Float> pointer14,
+  Pointer<Float> pointer15,
+  Pointer<Float> pointer16,
+  Pointer<Float> pointer17,
+  Pointer<Float> pointer18,
+  Pointer<Float> pointer19,
+);
+
+Float32List makeFloat32List(int length) {
+  final typedData = Float32List(length);
+  for (int i = 0; i < length; i++) {
+    final value = (i % 2 == 0 ? i : -i).toDouble();
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+double makeExpectedResultFloat(int start, int end) {
+  double expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = (i % 2 == 0 ? i : -i).toDouble();
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Double Function(Pointer<Double>, Size)>(
+    symbol: 'TakeDoublePointer', isLeaf: true)
+external double takeDoublePointer(Pointer<Double> pointer, int length);
+
+@Native<
+    Double Function(
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+      Pointer<Double>,
+    )>(symbol: 'TakeDoublePointerMany', isLeaf: true)
+external double takeDoublePointerMany(
+  Pointer<Double> pointer0,
+  Pointer<Double> pointer1,
+  Pointer<Double> pointer2,
+  Pointer<Double> pointer3,
+  Pointer<Double> pointer4,
+  Pointer<Double> pointer5,
+  Pointer<Double> pointer6,
+  Pointer<Double> pointer7,
+  Pointer<Double> pointer8,
+  Pointer<Double> pointer9,
+  Pointer<Double> pointer10,
+  Pointer<Double> pointer11,
+  Pointer<Double> pointer12,
+  Pointer<Double> pointer13,
+  Pointer<Double> pointer14,
+  Pointer<Double> pointer15,
+  Pointer<Double> pointer16,
+  Pointer<Double> pointer17,
+  Pointer<Double> pointer18,
+  Pointer<Double> pointer19,
+);
+
+Float64List makeFloat64List(int length) {
+  final typedData = Float64List(length);
+  for (int i = 0; i < length; i++) {
+    final value = (i % 2 == 0 ? i : -i).toDouble();
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+double makeExpectedResultDouble(int start, int end) {
+  double expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = (i % 2 == 0 ? i : -i).toDouble();
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+@Native<Bool Function(Pointer<Bool>, Size)>(
+    symbol: 'TakeBoolPointer', isLeaf: true)
+external bool takeBoolPointer(Pointer<Bool> pointer, int length);
+
+@Native<
+    Bool Function(
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+      Pointer<Bool>,
+    )>(symbol: 'TakeBoolPointerMany', isLeaf: true)
+external bool takeBoolPointerMany(
+  Pointer<Bool> pointer0,
+  Pointer<Bool> pointer1,
+  Pointer<Bool> pointer2,
+  Pointer<Bool> pointer3,
+  Pointer<Bool> pointer4,
+  Pointer<Bool> pointer5,
+  Pointer<Bool> pointer6,
+  Pointer<Bool> pointer7,
+  Pointer<Bool> pointer8,
+  Pointer<Bool> pointer9,
+  Pointer<Bool> pointer10,
+  Pointer<Bool> pointer11,
+  Pointer<Bool> pointer12,
+  Pointer<Bool> pointer13,
+  Pointer<Bool> pointer14,
+  Pointer<Bool> pointer15,
+  Pointer<Bool> pointer16,
+  Pointer<Bool> pointer17,
+  Pointer<Bool> pointer18,
+  Pointer<Bool> pointer19,
+);
diff --git a/tests/ffi/address_of_shared.dart b/tests/ffi/address_of_shared.dart
new file mode 100644
index 0000000..c9e4b29
--- /dev/null
+++ b/tests/ffi/address_of_shared.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+import 'dart:typed_data';
+
+bool makeExpectedResultBool(int start, int end) {
+  bool result = false;
+  for (int i = start; i < end; i++) {
+    final value = _value(i);
+    result ^= value;
+  }
+  return result;
+}
+
+Int8List makeBoolList(int length) {
+  final typedData = Int8List(length);
+  for (int i = 0; i < length; i++) {
+    final value = _value(i) ? 1 : 0;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+bool _value(int index) => index % 2 == 1;
diff --git a/tests/ffi/address_of_struct_generated_test.dart b/tests/ffi/address_of_struct_generated_test.dart
new file mode 100644
index 0000000..e75c371
--- /dev/null
+++ b/tests/ffi/address_of_struct_generated_test.dart
@@ -0,0 +1,1118 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+// ignore_for_file: unused_import
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import 'address_of_generated_shared.dart';
+import 'address_of_shared.dart';
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
+
+void main() {
+  // Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
+  dlopenGlobalPlatformSpecific('ffi_test_functions');
+
+  for (int i = 0; i < 100; ++i) {
+    testAddressOfInt8StructField();
+    testAddressOfInt16StructField();
+    testAddressOfInt32StructField();
+    testAddressOfInt64StructField();
+    testAddressOfUint8StructField();
+    testAddressOfUint16StructField();
+    testAddressOfUint32StructField();
+    testAddressOfUint64StructField();
+    testAddressOfFloatStructField();
+    testAddressOfDoubleStructField();
+    testAddressOfBoolStructField();
+    testAddressOfStructPointerMany();
+  }
+}
+
+final class Int8Struct extends Struct {
+  @Int8()
+  external int a0;
+  @Int8()
+  external int a1;
+  @Int8()
+  external int a2;
+  @Int8()
+  external int a3;
+  @Int8()
+  external int a4;
+  @Int8()
+  external int a5;
+  @Int8()
+  external int a6;
+  @Int8()
+  external int a7;
+  @Int8()
+  external int a8;
+  @Int8()
+  external int a9;
+  @Int8()
+  external int a10;
+  @Int8()
+  external int a11;
+  @Int8()
+  external int a12;
+  @Int8()
+  external int a13;
+  @Int8()
+  external int a14;
+  @Int8()
+  external int a15;
+  @Int8()
+  external int a16;
+  @Int8()
+  external int a17;
+  @Int8()
+  external int a18;
+  @Int8()
+  external int a19;
+}
+
+Int8Struct makeInt8Struct(int length) {
+  assert(length == 20);
+  final typedData = makeInt8List(length);
+  final struct = Struct.create<Int8Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfInt8StructField() {
+  const length = 20;
+  final struct = makeInt8Struct(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = takeInt8PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Int16Struct extends Struct {
+  @Int16()
+  external int a0;
+  @Int16()
+  external int a1;
+  @Int16()
+  external int a2;
+  @Int16()
+  external int a3;
+  @Int16()
+  external int a4;
+  @Int16()
+  external int a5;
+  @Int16()
+  external int a6;
+  @Int16()
+  external int a7;
+  @Int16()
+  external int a8;
+  @Int16()
+  external int a9;
+  @Int16()
+  external int a10;
+  @Int16()
+  external int a11;
+  @Int16()
+  external int a12;
+  @Int16()
+  external int a13;
+  @Int16()
+  external int a14;
+  @Int16()
+  external int a15;
+  @Int16()
+  external int a16;
+  @Int16()
+  external int a17;
+  @Int16()
+  external int a18;
+  @Int16()
+  external int a19;
+}
+
+Int16Struct makeInt16Struct(int length) {
+  assert(length == 20);
+  final typedData = makeInt16List(length);
+  final struct = Struct.create<Int16Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfInt16StructField() {
+  const length = 20;
+  final struct = makeInt16Struct(length);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeInt16PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Int32Struct extends Struct {
+  @Int32()
+  external int a0;
+  @Int32()
+  external int a1;
+  @Int32()
+  external int a2;
+  @Int32()
+  external int a3;
+  @Int32()
+  external int a4;
+  @Int32()
+  external int a5;
+  @Int32()
+  external int a6;
+  @Int32()
+  external int a7;
+  @Int32()
+  external int a8;
+  @Int32()
+  external int a9;
+  @Int32()
+  external int a10;
+  @Int32()
+  external int a11;
+  @Int32()
+  external int a12;
+  @Int32()
+  external int a13;
+  @Int32()
+  external int a14;
+  @Int32()
+  external int a15;
+  @Int32()
+  external int a16;
+  @Int32()
+  external int a17;
+  @Int32()
+  external int a18;
+  @Int32()
+  external int a19;
+}
+
+Int32Struct makeInt32Struct(int length) {
+  assert(length == 20);
+  final typedData = makeInt32List(length);
+  final struct = Struct.create<Int32Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfInt32StructField() {
+  const length = 20;
+  final struct = makeInt32Struct(length);
+  final expectedResult = makeExpectedResultInt32(0, length);
+  final result = takeInt32PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Int64Struct extends Struct {
+  @Int64()
+  external int a0;
+  @Int64()
+  external int a1;
+  @Int64()
+  external int a2;
+  @Int64()
+  external int a3;
+  @Int64()
+  external int a4;
+  @Int64()
+  external int a5;
+  @Int64()
+  external int a6;
+  @Int64()
+  external int a7;
+  @Int64()
+  external int a8;
+  @Int64()
+  external int a9;
+  @Int64()
+  external int a10;
+  @Int64()
+  external int a11;
+  @Int64()
+  external int a12;
+  @Int64()
+  external int a13;
+  @Int64()
+  external int a14;
+  @Int64()
+  external int a15;
+  @Int64()
+  external int a16;
+  @Int64()
+  external int a17;
+  @Int64()
+  external int a18;
+  @Int64()
+  external int a19;
+}
+
+Int64Struct makeInt64Struct(int length) {
+  assert(length == 20);
+  final typedData = makeInt64List(length);
+  final struct = Struct.create<Int64Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfInt64StructField() {
+  const length = 20;
+  final struct = makeInt64Struct(length);
+  final expectedResult = makeExpectedResultInt64(0, length);
+  final result = takeInt64PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint8Struct extends Struct {
+  @Uint8()
+  external int a0;
+  @Uint8()
+  external int a1;
+  @Uint8()
+  external int a2;
+  @Uint8()
+  external int a3;
+  @Uint8()
+  external int a4;
+  @Uint8()
+  external int a5;
+  @Uint8()
+  external int a6;
+  @Uint8()
+  external int a7;
+  @Uint8()
+  external int a8;
+  @Uint8()
+  external int a9;
+  @Uint8()
+  external int a10;
+  @Uint8()
+  external int a11;
+  @Uint8()
+  external int a12;
+  @Uint8()
+  external int a13;
+  @Uint8()
+  external int a14;
+  @Uint8()
+  external int a15;
+  @Uint8()
+  external int a16;
+  @Uint8()
+  external int a17;
+  @Uint8()
+  external int a18;
+  @Uint8()
+  external int a19;
+}
+
+Uint8Struct makeUint8Struct(int length) {
+  assert(length == 20);
+  final typedData = makeUint8List(length);
+  final struct = Struct.create<Uint8Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfUint8StructField() {
+  const length = 20;
+  final struct = makeUint8Struct(length);
+  final expectedResult = makeExpectedResultUint8(0, length);
+  final result = takeUint8PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint16Struct extends Struct {
+  @Uint16()
+  external int a0;
+  @Uint16()
+  external int a1;
+  @Uint16()
+  external int a2;
+  @Uint16()
+  external int a3;
+  @Uint16()
+  external int a4;
+  @Uint16()
+  external int a5;
+  @Uint16()
+  external int a6;
+  @Uint16()
+  external int a7;
+  @Uint16()
+  external int a8;
+  @Uint16()
+  external int a9;
+  @Uint16()
+  external int a10;
+  @Uint16()
+  external int a11;
+  @Uint16()
+  external int a12;
+  @Uint16()
+  external int a13;
+  @Uint16()
+  external int a14;
+  @Uint16()
+  external int a15;
+  @Uint16()
+  external int a16;
+  @Uint16()
+  external int a17;
+  @Uint16()
+  external int a18;
+  @Uint16()
+  external int a19;
+}
+
+Uint16Struct makeUint16Struct(int length) {
+  assert(length == 20);
+  final typedData = makeUint16List(length);
+  final struct = Struct.create<Uint16Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfUint16StructField() {
+  const length = 20;
+  final struct = makeUint16Struct(length);
+  final expectedResult = makeExpectedResultUint16(0, length);
+  final result = takeUint16PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint32Struct extends Struct {
+  @Uint32()
+  external int a0;
+  @Uint32()
+  external int a1;
+  @Uint32()
+  external int a2;
+  @Uint32()
+  external int a3;
+  @Uint32()
+  external int a4;
+  @Uint32()
+  external int a5;
+  @Uint32()
+  external int a6;
+  @Uint32()
+  external int a7;
+  @Uint32()
+  external int a8;
+  @Uint32()
+  external int a9;
+  @Uint32()
+  external int a10;
+  @Uint32()
+  external int a11;
+  @Uint32()
+  external int a12;
+  @Uint32()
+  external int a13;
+  @Uint32()
+  external int a14;
+  @Uint32()
+  external int a15;
+  @Uint32()
+  external int a16;
+  @Uint32()
+  external int a17;
+  @Uint32()
+  external int a18;
+  @Uint32()
+  external int a19;
+}
+
+Uint32Struct makeUint32Struct(int length) {
+  assert(length == 20);
+  final typedData = makeUint32List(length);
+  final struct = Struct.create<Uint32Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfUint32StructField() {
+  const length = 20;
+  final struct = makeUint32Struct(length);
+  final expectedResult = makeExpectedResultUint32(0, length);
+  final result = takeUint32PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Uint64Struct extends Struct {
+  @Uint64()
+  external int a0;
+  @Uint64()
+  external int a1;
+  @Uint64()
+  external int a2;
+  @Uint64()
+  external int a3;
+  @Uint64()
+  external int a4;
+  @Uint64()
+  external int a5;
+  @Uint64()
+  external int a6;
+  @Uint64()
+  external int a7;
+  @Uint64()
+  external int a8;
+  @Uint64()
+  external int a9;
+  @Uint64()
+  external int a10;
+  @Uint64()
+  external int a11;
+  @Uint64()
+  external int a12;
+  @Uint64()
+  external int a13;
+  @Uint64()
+  external int a14;
+  @Uint64()
+  external int a15;
+  @Uint64()
+  external int a16;
+  @Uint64()
+  external int a17;
+  @Uint64()
+  external int a18;
+  @Uint64()
+  external int a19;
+}
+
+Uint64Struct makeUint64Struct(int length) {
+  assert(length == 20);
+  final typedData = makeUint64List(length);
+  final struct = Struct.create<Uint64Struct>(typedData);
+  return struct;
+}
+
+void testAddressOfUint64StructField() {
+  const length = 20;
+  final struct = makeUint64Struct(length);
+  final expectedResult = makeExpectedResultUint64(0, length);
+  final result = takeUint64PointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class FloatStruct extends Struct {
+  @Float()
+  external double a0;
+  @Float()
+  external double a1;
+  @Float()
+  external double a2;
+  @Float()
+  external double a3;
+  @Float()
+  external double a4;
+  @Float()
+  external double a5;
+  @Float()
+  external double a6;
+  @Float()
+  external double a7;
+  @Float()
+  external double a8;
+  @Float()
+  external double a9;
+  @Float()
+  external double a10;
+  @Float()
+  external double a11;
+  @Float()
+  external double a12;
+  @Float()
+  external double a13;
+  @Float()
+  external double a14;
+  @Float()
+  external double a15;
+  @Float()
+  external double a16;
+  @Float()
+  external double a17;
+  @Float()
+  external double a18;
+  @Float()
+  external double a19;
+}
+
+FloatStruct makeFloatStruct(int length) {
+  assert(length == 20);
+  final typedData = makeFloat32List(length);
+  final struct = Struct.create<FloatStruct>(typedData);
+  return struct;
+}
+
+void testAddressOfFloatStructField() {
+  const length = 20;
+  final struct = makeFloatStruct(length);
+  final expectedResult = makeExpectedResultFloat(0, length);
+  final result = takeFloatPointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+final class DoubleStruct extends Struct {
+  @Double()
+  external double a0;
+  @Double()
+  external double a1;
+  @Double()
+  external double a2;
+  @Double()
+  external double a3;
+  @Double()
+  external double a4;
+  @Double()
+  external double a5;
+  @Double()
+  external double a6;
+  @Double()
+  external double a7;
+  @Double()
+  external double a8;
+  @Double()
+  external double a9;
+  @Double()
+  external double a10;
+  @Double()
+  external double a11;
+  @Double()
+  external double a12;
+  @Double()
+  external double a13;
+  @Double()
+  external double a14;
+  @Double()
+  external double a15;
+  @Double()
+  external double a16;
+  @Double()
+  external double a17;
+  @Double()
+  external double a18;
+  @Double()
+  external double a19;
+}
+
+DoubleStruct makeDoubleStruct(int length) {
+  assert(length == 20);
+  final typedData = makeFloat64List(length);
+  final struct = Struct.create<DoubleStruct>(typedData);
+  return struct;
+}
+
+void testAddressOfDoubleStructField() {
+  const length = 20;
+  final struct = makeDoubleStruct(length);
+  final expectedResult = makeExpectedResultDouble(0, length);
+  final result = takeDoublePointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+final class BoolStruct extends Struct {
+  @Bool()
+  external bool a0;
+  @Bool()
+  external bool a1;
+  @Bool()
+  external bool a2;
+  @Bool()
+  external bool a3;
+  @Bool()
+  external bool a4;
+  @Bool()
+  external bool a5;
+  @Bool()
+  external bool a6;
+  @Bool()
+  external bool a7;
+  @Bool()
+  external bool a8;
+  @Bool()
+  external bool a9;
+  @Bool()
+  external bool a10;
+  @Bool()
+  external bool a11;
+  @Bool()
+  external bool a12;
+  @Bool()
+  external bool a13;
+  @Bool()
+  external bool a14;
+  @Bool()
+  external bool a15;
+  @Bool()
+  external bool a16;
+  @Bool()
+  external bool a17;
+  @Bool()
+  external bool a18;
+  @Bool()
+  external bool a19;
+}
+
+BoolStruct makeBoolStruct(int length) {
+  assert(length == 20);
+  final typedData = makeBoolList(length);
+  final struct = Struct.create<BoolStruct>(typedData);
+  return struct;
+}
+
+void testAddressOfBoolStructField() {
+  const length = 20;
+  final struct = makeBoolStruct(length);
+  final expectedResult = makeExpectedResultBool(0, length);
+  final result = takeBoolPointerMany(
+    struct.a0.address,
+    struct.a1.address,
+    struct.a2.address,
+    struct.a3.address,
+    struct.a4.address,
+    struct.a5.address,
+    struct.a6.address,
+    struct.a7.address,
+    struct.a8.address,
+    struct.a9.address,
+    struct.a10.address,
+    struct.a11.address,
+    struct.a12.address,
+    struct.a13.address,
+    struct.a14.address,
+    struct.a15.address,
+    struct.a16.address,
+    struct.a17.address,
+    struct.a18.address,
+    struct.a19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Struct2BytesInt extends Struct {
+  @Int16()
+  external int a0;
+
+  String toString() => "(${a0})";
+}
+
+@Native<
+    Int16 Function(
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+      Pointer<Struct2BytesInt>,
+    )>(symbol: 'TakeStruct2BytesIntPointerMany', isLeaf: true)
+external int takeStruct2BytesIntPointerMany(
+  Pointer<Struct2BytesInt> pointer0,
+  Pointer<Struct2BytesInt> pointer1,
+  Pointer<Struct2BytesInt> pointer2,
+  Pointer<Struct2BytesInt> pointer3,
+  Pointer<Struct2BytesInt> pointer4,
+  Pointer<Struct2BytesInt> pointer5,
+  Pointer<Struct2BytesInt> pointer6,
+  Pointer<Struct2BytesInt> pointer7,
+  Pointer<Struct2BytesInt> pointer8,
+  Pointer<Struct2BytesInt> pointer9,
+  Pointer<Struct2BytesInt> pointer10,
+  Pointer<Struct2BytesInt> pointer11,
+  Pointer<Struct2BytesInt> pointer12,
+  Pointer<Struct2BytesInt> pointer13,
+  Pointer<Struct2BytesInt> pointer14,
+  Pointer<Struct2BytesInt> pointer15,
+  Pointer<Struct2BytesInt> pointer16,
+  Pointer<Struct2BytesInt> pointer17,
+  Pointer<Struct2BytesInt> pointer18,
+  Pointer<Struct2BytesInt> pointer19,
+);
+
+void testAddressOfStructPointerMany() {
+  const length = 20;
+  final typedData = makeInt16List(length);
+  final struct0 = Struct.create<Struct2BytesInt>(typedData, 0);
+  final struct1 = Struct.create<Struct2BytesInt>(typedData, 1);
+  final struct2 = Struct.create<Struct2BytesInt>(typedData, 2);
+  final struct3 = Struct.create<Struct2BytesInt>(typedData, 3);
+  final struct4 = Struct.create<Struct2BytesInt>(typedData, 4);
+  final struct5 = Struct.create<Struct2BytesInt>(typedData, 5);
+  final struct6 = Struct.create<Struct2BytesInt>(typedData, 6);
+  final struct7 = Struct.create<Struct2BytesInt>(typedData, 7);
+  final struct8 = Struct.create<Struct2BytesInt>(typedData, 8);
+  final struct9 = Struct.create<Struct2BytesInt>(typedData, 9);
+  final struct10 = Struct.create<Struct2BytesInt>(typedData, 10);
+  final struct11 = Struct.create<Struct2BytesInt>(typedData, 11);
+  final struct12 = Struct.create<Struct2BytesInt>(typedData, 12);
+  final struct13 = Struct.create<Struct2BytesInt>(typedData, 13);
+  final struct14 = Struct.create<Struct2BytesInt>(typedData, 14);
+  final struct15 = Struct.create<Struct2BytesInt>(typedData, 15);
+  final struct16 = Struct.create<Struct2BytesInt>(typedData, 16);
+  final struct17 = Struct.create<Struct2BytesInt>(typedData, 17);
+  final struct18 = Struct.create<Struct2BytesInt>(typedData, 18);
+  final struct19 = Struct.create<Struct2BytesInt>(typedData, 19);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeStruct2BytesIntPointerMany(
+    struct0.address,
+    struct1.address,
+    struct2.address,
+    struct3.address,
+    struct4.address,
+    struct5.address,
+    struct6.address,
+    struct7.address,
+    struct8.address,
+    struct9.address,
+    struct10.address,
+    struct11.address,
+    struct12.address,
+    struct13.address,
+    struct14.address,
+    struct15.address,
+    struct16.address,
+    struct17.address,
+    struct18.address,
+    struct19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+final class Union2BytesInt extends Union {
+  @Int16()
+  external int a0;
+
+  String toString() => "(${a0})";
+}
+
+@Native<
+    Int16 Function(
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+      Pointer<Union2BytesInt>,
+    )>(symbol: 'TakeUnion2BytesIntPointerMany', isLeaf: true)
+external int takeUnion2BytesIntPointerMany(
+  Pointer<Union2BytesInt> pointer0,
+  Pointer<Union2BytesInt> pointer1,
+  Pointer<Union2BytesInt> pointer2,
+  Pointer<Union2BytesInt> pointer3,
+  Pointer<Union2BytesInt> pointer4,
+  Pointer<Union2BytesInt> pointer5,
+  Pointer<Union2BytesInt> pointer6,
+  Pointer<Union2BytesInt> pointer7,
+  Pointer<Union2BytesInt> pointer8,
+  Pointer<Union2BytesInt> pointer9,
+  Pointer<Union2BytesInt> pointer10,
+  Pointer<Union2BytesInt> pointer11,
+  Pointer<Union2BytesInt> pointer12,
+  Pointer<Union2BytesInt> pointer13,
+  Pointer<Union2BytesInt> pointer14,
+  Pointer<Union2BytesInt> pointer15,
+  Pointer<Union2BytesInt> pointer16,
+  Pointer<Union2BytesInt> pointer17,
+  Pointer<Union2BytesInt> pointer18,
+  Pointer<Union2BytesInt> pointer19,
+);
+
+void testAddressOfUnionPointerMany() {
+  const length = 20;
+  final typedData = makeInt16List(length);
+  final struct0 = Union.create<Union2BytesInt>(typedData, 0);
+  final struct1 = Union.create<Union2BytesInt>(typedData, 1);
+  final struct2 = Union.create<Union2BytesInt>(typedData, 2);
+  final struct3 = Union.create<Union2BytesInt>(typedData, 3);
+  final struct4 = Union.create<Union2BytesInt>(typedData, 4);
+  final struct5 = Union.create<Union2BytesInt>(typedData, 5);
+  final struct6 = Union.create<Union2BytesInt>(typedData, 6);
+  final struct7 = Union.create<Union2BytesInt>(typedData, 7);
+  final struct8 = Union.create<Union2BytesInt>(typedData, 8);
+  final struct9 = Union.create<Union2BytesInt>(typedData, 9);
+  final struct10 = Union.create<Union2BytesInt>(typedData, 10);
+  final struct11 = Union.create<Union2BytesInt>(typedData, 11);
+  final struct12 = Union.create<Union2BytesInt>(typedData, 12);
+  final struct13 = Union.create<Union2BytesInt>(typedData, 13);
+  final struct14 = Union.create<Union2BytesInt>(typedData, 14);
+  final struct15 = Union.create<Union2BytesInt>(typedData, 15);
+  final struct16 = Union.create<Union2BytesInt>(typedData, 16);
+  final struct17 = Union.create<Union2BytesInt>(typedData, 17);
+  final struct18 = Union.create<Union2BytesInt>(typedData, 18);
+  final struct19 = Union.create<Union2BytesInt>(typedData, 19);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeUnion2BytesIntPointerMany(
+    struct0.address,
+    struct1.address,
+    struct2.address,
+    struct3.address,
+    struct4.address,
+    struct5.address,
+    struct6.address,
+    struct7.address,
+    struct8.address,
+    struct9.address,
+    struct10.address,
+    struct11.address,
+    struct12.address,
+    struct13.address,
+    struct14.address,
+    struct15.address,
+    struct16.address,
+    struct17.address,
+    struct18.address,
+    struct19.address,
+  );
+  Expect.equals(expectedResult, result);
+}
diff --git a/tests/ffi/address_of_test.dart b/tests/ffi/address_of_test.dart
new file mode 100644
index 0000000..60cda68
--- /dev/null
+++ b/tests/ffi/address_of_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+// ignore_for_file: unused_import
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import 'address_of_generated_shared.dart';
+import 'address_of_shared.dart';
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
+
+void main() {
+  // Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
+  dlopenGlobalPlatformSpecific('ffi_test_functions');
+
+  for (int i = 0; i < 100; ++i) {
+    testAddressOfInt8Array();
+  }
+}
+
+final class Int8ArrayStruct extends Struct {
+  @Array(20)
+  external Array<Int8> array;
+}
+
+Array<Int8> makeInt8Array(int length) {
+  assert(length == 20);
+  final typedData = makeInt8List(length);
+  final struct = Struct.create<Int8ArrayStruct>(typedData);
+  return struct.array;
+}
+
+void testAddressOfInt8Array() {
+  const length = 20;
+  final array = makeInt8Array(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = Foo.takeInt8Pointer(array.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+class Foo {
+  @Native<Int8 Function(Pointer<Int8>, Size)>(
+    symbol: 'TakeInt8Pointer',
+    isLeaf: true,
+  )
+  external static int takeInt8Pointer(Pointer<Int8> pointer, int length);
+}
diff --git a/tests/ffi/address_of_typeddata_generated_test.dart b/tests/ffi/address_of_typeddata_generated_test.dart
new file mode 100644
index 0000000..97a5ed5
--- /dev/null
+++ b/tests/ffi/address_of_typeddata_generated_test.dart
@@ -0,0 +1,853 @@
+// Copyright (c) 2024, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// Generated by tests/ffi/generator/address_of_test_generator.dart.
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+// ignore_for_file: unused_import
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import 'address_of_generated_shared.dart';
+import 'address_of_shared.dart';
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
+
+void main() {
+  // Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
+  dlopenGlobalPlatformSpecific('ffi_test_functions');
+
+  for (int i = 0; i < 100; ++i) {
+    testAddressOfInt8List();
+    testAddressOfInt8ListElementAt();
+    testAddressOfInt8ListView();
+    testAddressOfInt8ListViewMany();
+    testAddressOfInt16List();
+    testAddressOfInt16ListElementAt();
+    testAddressOfInt16ListView();
+    testAddressOfInt16ListViewMany();
+    testAddressOfInt32List();
+    testAddressOfInt32ListElementAt();
+    testAddressOfInt32ListView();
+    testAddressOfInt32ListViewMany();
+    testAddressOfInt64List();
+    testAddressOfInt64ListElementAt();
+    testAddressOfInt64ListView();
+    testAddressOfInt64ListViewMany();
+    testAddressOfUint8List();
+    testAddressOfUint8ListElementAt();
+    testAddressOfUint8ListView();
+    testAddressOfUint8ListViewMany();
+    testAddressOfUint16List();
+    testAddressOfUint16ListElementAt();
+    testAddressOfUint16ListView();
+    testAddressOfUint16ListViewMany();
+    testAddressOfUint32List();
+    testAddressOfUint32ListElementAt();
+    testAddressOfUint32ListView();
+    testAddressOfUint32ListViewMany();
+    testAddressOfUint64List();
+    testAddressOfUint64ListElementAt();
+    testAddressOfUint64ListView();
+    testAddressOfUint64ListViewMany();
+    testAddressOfFloat32List();
+    testAddressOfFloat32ListElementAt();
+    testAddressOfFloat32ListView();
+    testAddressOfFloat32ListViewMany();
+    testAddressOfFloat64List();
+    testAddressOfFloat64ListElementAt();
+    testAddressOfFloat64ListView();
+    testAddressOfFloat64ListViewMany();
+  }
+}
+
+void testAddressOfInt8List() {
+  const length = 20;
+  final typedData = makeInt8List(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = takeInt8Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt8ListElementAt() {
+  const length = 20;
+  final typedData = makeInt8List(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = takeInt8PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt8ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeInt8List(sourceLength);
+  final view = Int8List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultInt8(viewStart, viewEnd);
+  final result = takeInt8Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt8ListViewMany() {
+  const length = 20;
+  final typedData = makeInt8List(length);
+  final expectedResult = makeExpectedResultInt8(0, length);
+  final result = takeInt8PointerMany(
+    Int8List.sublistView(typedData, 0, 0 + 1).address,
+    Int8List.sublistView(typedData, 1, 1 + 1).address,
+    Int8List.sublistView(typedData, 2, 2 + 1).address,
+    Int8List.sublistView(typedData, 3, 3 + 1).address,
+    Int8List.sublistView(typedData, 4, 4 + 1).address,
+    Int8List.sublistView(typedData, 5, 5 + 1).address,
+    Int8List.sublistView(typedData, 6, 6 + 1).address,
+    Int8List.sublistView(typedData, 7, 7 + 1).address,
+    Int8List.sublistView(typedData, 8, 8 + 1).address,
+    Int8List.sublistView(typedData, 9, 9 + 1).address,
+    Int8List.sublistView(typedData, 10, 10 + 1).address,
+    Int8List.sublistView(typedData, 11, 11 + 1).address,
+    Int8List.sublistView(typedData, 12, 12 + 1).address,
+    Int8List.sublistView(typedData, 13, 13 + 1).address,
+    Int8List.sublistView(typedData, 14, 14 + 1).address,
+    Int8List.sublistView(typedData, 15, 15 + 1).address,
+    Int8List.sublistView(typedData, 16, 16 + 1).address,
+    Int8List.sublistView(typedData, 17, 17 + 1).address,
+    Int8List.sublistView(typedData, 18, 18 + 1).address,
+    Int8List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt16List() {
+  const length = 20;
+  final typedData = makeInt16List(length);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeInt16Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt16ListElementAt() {
+  const length = 20;
+  final typedData = makeInt16List(length);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeInt16PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt16ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeInt16List(sourceLength);
+  final view = Int16List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultInt16(viewStart, viewEnd);
+  final result = takeInt16Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt16ListViewMany() {
+  const length = 20;
+  final typedData = makeInt16List(length);
+  final expectedResult = makeExpectedResultInt16(0, length);
+  final result = takeInt16PointerMany(
+    Int16List.sublistView(typedData, 0, 0 + 1).address,
+    Int16List.sublistView(typedData, 1, 1 + 1).address,
+    Int16List.sublistView(typedData, 2, 2 + 1).address,
+    Int16List.sublistView(typedData, 3, 3 + 1).address,
+    Int16List.sublistView(typedData, 4, 4 + 1).address,
+    Int16List.sublistView(typedData, 5, 5 + 1).address,
+    Int16List.sublistView(typedData, 6, 6 + 1).address,
+    Int16List.sublistView(typedData, 7, 7 + 1).address,
+    Int16List.sublistView(typedData, 8, 8 + 1).address,
+    Int16List.sublistView(typedData, 9, 9 + 1).address,
+    Int16List.sublistView(typedData, 10, 10 + 1).address,
+    Int16List.sublistView(typedData, 11, 11 + 1).address,
+    Int16List.sublistView(typedData, 12, 12 + 1).address,
+    Int16List.sublistView(typedData, 13, 13 + 1).address,
+    Int16List.sublistView(typedData, 14, 14 + 1).address,
+    Int16List.sublistView(typedData, 15, 15 + 1).address,
+    Int16List.sublistView(typedData, 16, 16 + 1).address,
+    Int16List.sublistView(typedData, 17, 17 + 1).address,
+    Int16List.sublistView(typedData, 18, 18 + 1).address,
+    Int16List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt32List() {
+  const length = 20;
+  final typedData = makeInt32List(length);
+  final expectedResult = makeExpectedResultInt32(0, length);
+  final result = takeInt32Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt32ListElementAt() {
+  const length = 20;
+  final typedData = makeInt32List(length);
+  final expectedResult = makeExpectedResultInt32(0, length);
+  final result = takeInt32PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt32ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeInt32List(sourceLength);
+  final view = Int32List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultInt32(viewStart, viewEnd);
+  final result = takeInt32Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt32ListViewMany() {
+  const length = 20;
+  final typedData = makeInt32List(length);
+  final expectedResult = makeExpectedResultInt32(0, length);
+  final result = takeInt32PointerMany(
+    Int32List.sublistView(typedData, 0, 0 + 1).address,
+    Int32List.sublistView(typedData, 1, 1 + 1).address,
+    Int32List.sublistView(typedData, 2, 2 + 1).address,
+    Int32List.sublistView(typedData, 3, 3 + 1).address,
+    Int32List.sublistView(typedData, 4, 4 + 1).address,
+    Int32List.sublistView(typedData, 5, 5 + 1).address,
+    Int32List.sublistView(typedData, 6, 6 + 1).address,
+    Int32List.sublistView(typedData, 7, 7 + 1).address,
+    Int32List.sublistView(typedData, 8, 8 + 1).address,
+    Int32List.sublistView(typedData, 9, 9 + 1).address,
+    Int32List.sublistView(typedData, 10, 10 + 1).address,
+    Int32List.sublistView(typedData, 11, 11 + 1).address,
+    Int32List.sublistView(typedData, 12, 12 + 1).address,
+    Int32List.sublistView(typedData, 13, 13 + 1).address,
+    Int32List.sublistView(typedData, 14, 14 + 1).address,
+    Int32List.sublistView(typedData, 15, 15 + 1).address,
+    Int32List.sublistView(typedData, 16, 16 + 1).address,
+    Int32List.sublistView(typedData, 17, 17 + 1).address,
+    Int32List.sublistView(typedData, 18, 18 + 1).address,
+    Int32List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt64List() {
+  const length = 20;
+  final typedData = makeInt64List(length);
+  final expectedResult = makeExpectedResultInt64(0, length);
+  final result = takeInt64Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt64ListElementAt() {
+  const length = 20;
+  final typedData = makeInt64List(length);
+  final expectedResult = makeExpectedResultInt64(0, length);
+  final result = takeInt64PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt64ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeInt64List(sourceLength);
+  final view = Int64List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultInt64(viewStart, viewEnd);
+  final result = takeInt64Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfInt64ListViewMany() {
+  const length = 20;
+  final typedData = makeInt64List(length);
+  final expectedResult = makeExpectedResultInt64(0, length);
+  final result = takeInt64PointerMany(
+    Int64List.sublistView(typedData, 0, 0 + 1).address,
+    Int64List.sublistView(typedData, 1, 1 + 1).address,
+    Int64List.sublistView(typedData, 2, 2 + 1).address,
+    Int64List.sublistView(typedData, 3, 3 + 1).address,
+    Int64List.sublistView(typedData, 4, 4 + 1).address,
+    Int64List.sublistView(typedData, 5, 5 + 1).address,
+    Int64List.sublistView(typedData, 6, 6 + 1).address,
+    Int64List.sublistView(typedData, 7, 7 + 1).address,
+    Int64List.sublistView(typedData, 8, 8 + 1).address,
+    Int64List.sublistView(typedData, 9, 9 + 1).address,
+    Int64List.sublistView(typedData, 10, 10 + 1).address,
+    Int64List.sublistView(typedData, 11, 11 + 1).address,
+    Int64List.sublistView(typedData, 12, 12 + 1).address,
+    Int64List.sublistView(typedData, 13, 13 + 1).address,
+    Int64List.sublistView(typedData, 14, 14 + 1).address,
+    Int64List.sublistView(typedData, 15, 15 + 1).address,
+    Int64List.sublistView(typedData, 16, 16 + 1).address,
+    Int64List.sublistView(typedData, 17, 17 + 1).address,
+    Int64List.sublistView(typedData, 18, 18 + 1).address,
+    Int64List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint8List() {
+  const length = 20;
+  final typedData = makeUint8List(length);
+  final expectedResult = makeExpectedResultUint8(0, length);
+  final result = takeUint8Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint8ListElementAt() {
+  const length = 20;
+  final typedData = makeUint8List(length);
+  final expectedResult = makeExpectedResultUint8(0, length);
+  final result = takeUint8PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint8ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeUint8List(sourceLength);
+  final view = Uint8List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultUint8(viewStart, viewEnd);
+  final result = takeUint8Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint8ListViewMany() {
+  const length = 20;
+  final typedData = makeUint8List(length);
+  final expectedResult = makeExpectedResultUint8(0, length);
+  final result = takeUint8PointerMany(
+    Uint8List.sublistView(typedData, 0, 0 + 1).address,
+    Uint8List.sublistView(typedData, 1, 1 + 1).address,
+    Uint8List.sublistView(typedData, 2, 2 + 1).address,
+    Uint8List.sublistView(typedData, 3, 3 + 1).address,
+    Uint8List.sublistView(typedData, 4, 4 + 1).address,
+    Uint8List.sublistView(typedData, 5, 5 + 1).address,
+    Uint8List.sublistView(typedData, 6, 6 + 1).address,
+    Uint8List.sublistView(typedData, 7, 7 + 1).address,
+    Uint8List.sublistView(typedData, 8, 8 + 1).address,
+    Uint8List.sublistView(typedData, 9, 9 + 1).address,
+    Uint8List.sublistView(typedData, 10, 10 + 1).address,
+    Uint8List.sublistView(typedData, 11, 11 + 1).address,
+    Uint8List.sublistView(typedData, 12, 12 + 1).address,
+    Uint8List.sublistView(typedData, 13, 13 + 1).address,
+    Uint8List.sublistView(typedData, 14, 14 + 1).address,
+    Uint8List.sublistView(typedData, 15, 15 + 1).address,
+    Uint8List.sublistView(typedData, 16, 16 + 1).address,
+    Uint8List.sublistView(typedData, 17, 17 + 1).address,
+    Uint8List.sublistView(typedData, 18, 18 + 1).address,
+    Uint8List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint16List() {
+  const length = 20;
+  final typedData = makeUint16List(length);
+  final expectedResult = makeExpectedResultUint16(0, length);
+  final result = takeUint16Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint16ListElementAt() {
+  const length = 20;
+  final typedData = makeUint16List(length);
+  final expectedResult = makeExpectedResultUint16(0, length);
+  final result = takeUint16PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint16ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeUint16List(sourceLength);
+  final view = Uint16List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultUint16(viewStart, viewEnd);
+  final result = takeUint16Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint16ListViewMany() {
+  const length = 20;
+  final typedData = makeUint16List(length);
+  final expectedResult = makeExpectedResultUint16(0, length);
+  final result = takeUint16PointerMany(
+    Uint16List.sublistView(typedData, 0, 0 + 1).address,
+    Uint16List.sublistView(typedData, 1, 1 + 1).address,
+    Uint16List.sublistView(typedData, 2, 2 + 1).address,
+    Uint16List.sublistView(typedData, 3, 3 + 1).address,
+    Uint16List.sublistView(typedData, 4, 4 + 1).address,
+    Uint16List.sublistView(typedData, 5, 5 + 1).address,
+    Uint16List.sublistView(typedData, 6, 6 + 1).address,
+    Uint16List.sublistView(typedData, 7, 7 + 1).address,
+    Uint16List.sublistView(typedData, 8, 8 + 1).address,
+    Uint16List.sublistView(typedData, 9, 9 + 1).address,
+    Uint16List.sublistView(typedData, 10, 10 + 1).address,
+    Uint16List.sublistView(typedData, 11, 11 + 1).address,
+    Uint16List.sublistView(typedData, 12, 12 + 1).address,
+    Uint16List.sublistView(typedData, 13, 13 + 1).address,
+    Uint16List.sublistView(typedData, 14, 14 + 1).address,
+    Uint16List.sublistView(typedData, 15, 15 + 1).address,
+    Uint16List.sublistView(typedData, 16, 16 + 1).address,
+    Uint16List.sublistView(typedData, 17, 17 + 1).address,
+    Uint16List.sublistView(typedData, 18, 18 + 1).address,
+    Uint16List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint32List() {
+  const length = 20;
+  final typedData = makeUint32List(length);
+  final expectedResult = makeExpectedResultUint32(0, length);
+  final result = takeUint32Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint32ListElementAt() {
+  const length = 20;
+  final typedData = makeUint32List(length);
+  final expectedResult = makeExpectedResultUint32(0, length);
+  final result = takeUint32PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint32ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeUint32List(sourceLength);
+  final view = Uint32List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultUint32(viewStart, viewEnd);
+  final result = takeUint32Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint32ListViewMany() {
+  const length = 20;
+  final typedData = makeUint32List(length);
+  final expectedResult = makeExpectedResultUint32(0, length);
+  final result = takeUint32PointerMany(
+    Uint32List.sublistView(typedData, 0, 0 + 1).address,
+    Uint32List.sublistView(typedData, 1, 1 + 1).address,
+    Uint32List.sublistView(typedData, 2, 2 + 1).address,
+    Uint32List.sublistView(typedData, 3, 3 + 1).address,
+    Uint32List.sublistView(typedData, 4, 4 + 1).address,
+    Uint32List.sublistView(typedData, 5, 5 + 1).address,
+    Uint32List.sublistView(typedData, 6, 6 + 1).address,
+    Uint32List.sublistView(typedData, 7, 7 + 1).address,
+    Uint32List.sublistView(typedData, 8, 8 + 1).address,
+    Uint32List.sublistView(typedData, 9, 9 + 1).address,
+    Uint32List.sublistView(typedData, 10, 10 + 1).address,
+    Uint32List.sublistView(typedData, 11, 11 + 1).address,
+    Uint32List.sublistView(typedData, 12, 12 + 1).address,
+    Uint32List.sublistView(typedData, 13, 13 + 1).address,
+    Uint32List.sublistView(typedData, 14, 14 + 1).address,
+    Uint32List.sublistView(typedData, 15, 15 + 1).address,
+    Uint32List.sublistView(typedData, 16, 16 + 1).address,
+    Uint32List.sublistView(typedData, 17, 17 + 1).address,
+    Uint32List.sublistView(typedData, 18, 18 + 1).address,
+    Uint32List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint64List() {
+  const length = 20;
+  final typedData = makeUint64List(length);
+  final expectedResult = makeExpectedResultUint64(0, length);
+  final result = takeUint64Pointer(typedData.address, length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint64ListElementAt() {
+  const length = 20;
+  final typedData = makeUint64List(length);
+  final expectedResult = makeExpectedResultUint64(0, length);
+  final result = takeUint64PointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint64ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeUint64List(sourceLength);
+  final view = Uint64List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultUint64(viewStart, viewEnd);
+  final result = takeUint64Pointer(view.address, view.length);
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfUint64ListViewMany() {
+  const length = 20;
+  final typedData = makeUint64List(length);
+  final expectedResult = makeExpectedResultUint64(0, length);
+  final result = takeUint64PointerMany(
+    Uint64List.sublistView(typedData, 0, 0 + 1).address,
+    Uint64List.sublistView(typedData, 1, 1 + 1).address,
+    Uint64List.sublistView(typedData, 2, 2 + 1).address,
+    Uint64List.sublistView(typedData, 3, 3 + 1).address,
+    Uint64List.sublistView(typedData, 4, 4 + 1).address,
+    Uint64List.sublistView(typedData, 5, 5 + 1).address,
+    Uint64List.sublistView(typedData, 6, 6 + 1).address,
+    Uint64List.sublistView(typedData, 7, 7 + 1).address,
+    Uint64List.sublistView(typedData, 8, 8 + 1).address,
+    Uint64List.sublistView(typedData, 9, 9 + 1).address,
+    Uint64List.sublistView(typedData, 10, 10 + 1).address,
+    Uint64List.sublistView(typedData, 11, 11 + 1).address,
+    Uint64List.sublistView(typedData, 12, 12 + 1).address,
+    Uint64List.sublistView(typedData, 13, 13 + 1).address,
+    Uint64List.sublistView(typedData, 14, 14 + 1).address,
+    Uint64List.sublistView(typedData, 15, 15 + 1).address,
+    Uint64List.sublistView(typedData, 16, 16 + 1).address,
+    Uint64List.sublistView(typedData, 17, 17 + 1).address,
+    Uint64List.sublistView(typedData, 18, 18 + 1).address,
+    Uint64List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.equals(expectedResult, result);
+}
+
+void testAddressOfFloat32List() {
+  const length = 20;
+  final typedData = makeFloat32List(length);
+  final expectedResult = makeExpectedResultFloat(0, length);
+  final result = takeFloatPointer(typedData.address, length);
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat32ListElementAt() {
+  const length = 20;
+  final typedData = makeFloat32List(length);
+  final expectedResult = makeExpectedResultFloat(0, length);
+  final result = takeFloatPointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat32ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeFloat32List(sourceLength);
+  final view = Float32List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultFloat(viewStart, viewEnd);
+  final result = takeFloatPointer(view.address, view.length);
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat32ListViewMany() {
+  const length = 20;
+  final typedData = makeFloat32List(length);
+  final expectedResult = makeExpectedResultFloat(0, length);
+  final result = takeFloatPointerMany(
+    Float32List.sublistView(typedData, 0, 0 + 1).address,
+    Float32List.sublistView(typedData, 1, 1 + 1).address,
+    Float32List.sublistView(typedData, 2, 2 + 1).address,
+    Float32List.sublistView(typedData, 3, 3 + 1).address,
+    Float32List.sublistView(typedData, 4, 4 + 1).address,
+    Float32List.sublistView(typedData, 5, 5 + 1).address,
+    Float32List.sublistView(typedData, 6, 6 + 1).address,
+    Float32List.sublistView(typedData, 7, 7 + 1).address,
+    Float32List.sublistView(typedData, 8, 8 + 1).address,
+    Float32List.sublistView(typedData, 9, 9 + 1).address,
+    Float32List.sublistView(typedData, 10, 10 + 1).address,
+    Float32List.sublistView(typedData, 11, 11 + 1).address,
+    Float32List.sublistView(typedData, 12, 12 + 1).address,
+    Float32List.sublistView(typedData, 13, 13 + 1).address,
+    Float32List.sublistView(typedData, 14, 14 + 1).address,
+    Float32List.sublistView(typedData, 15, 15 + 1).address,
+    Float32List.sublistView(typedData, 16, 16 + 1).address,
+    Float32List.sublistView(typedData, 17, 17 + 1).address,
+    Float32List.sublistView(typedData, 18, 18 + 1).address,
+    Float32List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat64List() {
+  const length = 20;
+  final typedData = makeFloat64List(length);
+  final expectedResult = makeExpectedResultDouble(0, length);
+  final result = takeDoublePointer(typedData.address, length);
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat64ListElementAt() {
+  const length = 20;
+  final typedData = makeFloat64List(length);
+  final expectedResult = makeExpectedResultDouble(0, length);
+  final result = takeDoublePointerMany(
+    typedData[0].address,
+    typedData[1].address,
+    typedData[2].address,
+    typedData[3].address,
+    typedData[4].address,
+    typedData[5].address,
+    typedData[6].address,
+    typedData[7].address,
+    typedData[8].address,
+    typedData[9].address,
+    typedData[10].address,
+    typedData[11].address,
+    typedData[12].address,
+    typedData[13].address,
+    typedData[14].address,
+    typedData[15].address,
+    typedData[16].address,
+    typedData[17].address,
+    typedData[18].address,
+    typedData[19].address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat64ListView() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = makeFloat64List(sourceLength);
+  final view = Float64List.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResultDouble(viewStart, viewEnd);
+  final result = takeDoublePointer(view.address, view.length);
+  Expect.approxEquals(expectedResult, result);
+}
+
+void testAddressOfFloat64ListViewMany() {
+  const length = 20;
+  final typedData = makeFloat64List(length);
+  final expectedResult = makeExpectedResultDouble(0, length);
+  final result = takeDoublePointerMany(
+    Float64List.sublistView(typedData, 0, 0 + 1).address,
+    Float64List.sublistView(typedData, 1, 1 + 1).address,
+    Float64List.sublistView(typedData, 2, 2 + 1).address,
+    Float64List.sublistView(typedData, 3, 3 + 1).address,
+    Float64List.sublistView(typedData, 4, 4 + 1).address,
+    Float64List.sublistView(typedData, 5, 5 + 1).address,
+    Float64List.sublistView(typedData, 6, 6 + 1).address,
+    Float64List.sublistView(typedData, 7, 7 + 1).address,
+    Float64List.sublistView(typedData, 8, 8 + 1).address,
+    Float64List.sublistView(typedData, 9, 9 + 1).address,
+    Float64List.sublistView(typedData, 10, 10 + 1).address,
+    Float64List.sublistView(typedData, 11, 11 + 1).address,
+    Float64List.sublistView(typedData, 12, 12 + 1).address,
+    Float64List.sublistView(typedData, 13, 13 + 1).address,
+    Float64List.sublistView(typedData, 14, 14 + 1).address,
+    Float64List.sublistView(typedData, 15, 15 + 1).address,
+    Float64List.sublistView(typedData, 16, 16 + 1).address,
+    Float64List.sublistView(typedData, 17, 17 + 1).address,
+    Float64List.sublistView(typedData, 18, 18 + 1).address,
+    Float64List.sublistView(typedData, 19, 19 + 1).address,
+  );
+  Expect.approxEquals(expectedResult, result);
+}
diff --git a/tests/ffi/generator/address_of_test_generator.dart b/tests/ffi/generator/address_of_test_generator.dart
new file mode 100644
index 0000000..2818b28
--- /dev/null
+++ b/tests/ffi/generator/address_of_test_generator.dart
@@ -0,0 +1,517 @@
+// Copyright (c) 2024, 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.
+
+import 'dart:io';
+
+import 'c_types.dart';
+import 'utils.dart';
+import 'structs_by_value_tests_generator.dart';
+
+void main() async {
+  await Future.wait([
+    writeC(),
+    for (final container in Container.values) writeDart(container),
+    writeDartShared()
+  ]);
+}
+
+class Container {
+  /// The name of the container in Dart code.
+  final String name;
+
+  /// The copyright year for the generated test file for this container.
+  final int copyrightYear;
+
+  /// How this containers' name shows up in tests.
+  final String Function(PointerType) testName;
+
+  final Iterable<Test> tests;
+
+  final Iterable<FundamentalType> elementTypes;
+
+  const Container(
+    this.name,
+    this.copyrightYear,
+    this.testName,
+    this.tests,
+    this.elementTypes,
+  );
+
+  static final array = Container(
+    'Array',
+    2024,
+    (pointerType) => '${pointerType.pointerTo}Array',
+    [
+      Test.self,
+      Test.elementAt,
+    ],
+    [
+      int8,
+      int16,
+      int32,
+      int64,
+      uint8,
+      uint16,
+      uint32,
+      uint64,
+      float,
+      double_,
+      bool_,
+    ],
+  );
+
+  static final struct = Container(
+    'Struct',
+    2024,
+    (pointerType) => '${pointerType.pointerTo}Struct',
+    [
+      Test.field,
+    ],
+    [
+      int8,
+      int16,
+      int32,
+      int64,
+      uint8,
+      uint16,
+      uint32,
+      uint64,
+      float,
+      double_,
+      bool_,
+    ],
+  );
+
+  static final typedData = Container(
+    'TypedData',
+    2024,
+    (pointerType) => pointerType.dartTypedData,
+    [
+      Test.self,
+      Test.elementAt,
+      Test.view,
+      Test.viewMany,
+    ],
+    [
+      int8,
+      int16,
+      int32,
+      int64,
+      uint8,
+      uint16,
+      uint32,
+      uint64,
+      float,
+      double_,
+    ],
+  );
+
+  static final values = [
+    array,
+    struct,
+    typedData,
+  ];
+}
+
+class Test {
+  final String name;
+
+  const Test(this.name);
+
+  String testName(Container container, CType elementType) {
+    final containerName = container.testName(PointerType(elementType));
+    return 'testAddressOf$containerName$name';
+  }
+
+  static const elementAt = Test('ElementAt');
+  static const field = Test('Field');
+  static const self = Test('');
+  static const view = Test('View');
+  static const viewMany = Test('ViewMany');
+}
+
+const generatorPath = 'tests/ffi/generator/address_of_test_generator.dart';
+
+Future<void> writeDart(Container container) async {
+  final StringBuffer buffer = StringBuffer();
+  buffer.write(headerDart(
+    copyrightYear: container.copyrightYear,
+  ));
+
+  buffer.write("""
+void main() {${'''
+  // Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
+  dlopenGlobalPlatformSpecific('ffi_test_functions');
+
+'''}
+  for (int i = 0; i < 100; ++i) {
+""");
+
+  for (final elementType in container.elementTypes) {
+    for (final test in container.tests) {
+      buffer.write("""
+    ${test.testName(container, elementType)}();
+""");
+    }
+  }
+  if (container == Container.struct) {
+    buffer.write('''
+    testAddressOfStructPointerMany();
+''');
+  }
+  buffer.write("""
+  }
+}
+
+""");
+
+  for (final elementType in container.elementTypes) {
+    final pointerType = PointerType(elementType);
+    final testName = container.testName(pointerType);
+    final varName = container.name.lowerCaseFirst();
+    String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
+    if (elementType.isFloatingPoint) {
+      value = '($value).toDouble()';
+    }
+    final equals = elementType.isFloatingPoint ? 'approxEquals' : 'equals';
+
+    if (container == Container.array) {
+      buffer.write("""
+final class ${elementType.dartCType}ArrayStruct extends Struct {
+  @Array(20)
+  external Array<${elementType.dartCType}> array;
+}
+
+Array<${elementType.dartCType}> make${elementType.dartCType}Array(int length){
+  assert(length == 20);
+  final typedData = make${pointerType.dartTypedData}(length);
+  final struct = Struct.create<${elementType.dartCType}ArrayStruct>(typedData);
+  return struct.array;
+}
+
+""");
+    }
+    if (container == Container.struct) {
+      buffer.write("""
+final class ${elementType.dartCType}Struct extends Struct {
+${[
+        for (int i = 0; i < manyCount; i++)
+          '''
+@${elementType.dartCType}()
+external ${elementType.dartType} a$i;
+'''
+      ].join()}
+}
+
+${elementType.dartCType}Struct make${elementType.dartCType}Struct(int length){
+  assert(length == 20);
+  final typedData = make${pointerType.dartTypedData}(length);
+  final struct = Struct.create<${elementType.dartCType}Struct>(typedData);
+  return struct;
+}
+
+""");
+    }
+
+    for (final test in container.tests) {
+      final methodName = test.testName(container, elementType);
+      switch (test) {
+        case Test.self:
+          buffer.write("""
+void $methodName() {
+  const length = 20;
+  final $varName = make${testName}(length);
+  final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
+  final result = take${elementType.dartCType}Pointer($varName.address, length);
+  Expect.$equals(expectedResult, result);
+}
+
+""");
+        case Test.elementAt:
+          buffer.write("""
+void $methodName() {
+  const length = $manyCount;
+  final $varName = make${testName}(length);
+  final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
+  final result = take${elementType.dartCType}PointerMany(${[
+            for (int i = 0; i < manyCount; i++) '$varName[$i].address,'
+          ].join()});
+  Expect.$equals(expectedResult, result);
+}
+""");
+        case Test.view:
+          buffer.write("""
+void $methodName() {
+  const sourceLength = 30;
+  const viewStart = 10;
+  const viewLength = 10;
+  final viewEnd = viewStart + viewLength;
+  final source = make${pointerType.dartTypedData}(sourceLength);
+  final view = ${pointerType.dartTypedData}.sublistView(source, viewStart, viewEnd);
+  final expectedResult = makeExpectedResult${elementType.dartCType}(viewStart, viewEnd);
+  final result = take${elementType.dartCType}Pointer(view.address, view.length);
+  Expect.$equals(expectedResult, result);
+}
+
+""");
+        case Test.viewMany:
+          buffer.write("""
+void $methodName() {
+  const length = $manyCount;
+  final typedData = make${pointerType.dartTypedData}(length);
+  final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
+  final result = take${elementType.dartCType}PointerMany(${[
+            for (int i = 0; i < manyCount; i++)
+              '${pointerType.dartTypedData}.sublistView(typedData, $i, $i + 1).address,'
+          ].join()});
+  Expect.$equals(expectedResult, result);
+}
+
+""");
+        case Test.field:
+          buffer.write("""
+void $methodName() {
+  const length = $manyCount;
+  final $varName = make${testName}(length);
+  final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
+  final result = take${elementType.dartCType}PointerMany(${[
+            for (int i = 0; i < manyCount; i++) '$varName.a$i.address,'
+          ].join()});
+  Expect.$equals(expectedResult, result);
+}
+""");
+      }
+    }
+  }
+
+  if (container == Container.struct) {
+    final elementType = int16;
+    for (final compoundType in [
+      StructType([elementType]),
+      UnionType([elementType])
+    ]) {
+      final compoundKind = compoundType is StructType ? 'Struct' : 'Union';
+      final pointerType = PointerType(compoundType);
+
+      buffer.write(compoundType.dartClass());
+
+      buffer.write('''
+@Native<${elementType.dartCType} Function(
+  ${[for (int i = 0; i < manyCount; i++) '${pointerType.dartCType},'].join()}
+)>(symbol: 'Take${compoundKind}2BytesIntPointerMany', isLeaf: true)
+external ${elementType.dartType} take${compoundKind}2BytesIntPointerMany(
+  ${[
+        for (int i = 0; i < manyCount; i++)
+          '${pointerType.dartCType} pointer$i,',
+      ].join()}
+);
+
+void testAddressOf${compoundKind}PointerMany() {
+  const length = $manyCount;
+  final typedData = makeInt16List(length);
+${[
+        for (int i = 0; i < manyCount; i++)
+          'final struct$i = ${compoundKind}.create<${compoundKind}2BytesInt>(typedData, $i);'
+      ].join()}
+  final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
+  final result = take${compoundKind}2BytesIntPointerMany(${[
+        for (int i = 0; i < manyCount; i++) 'struct$i.address,'
+      ].join()});
+  Expect.equals(expectedResult, result);
+}
+''');
+    }
+  }
+
+  final path = testPath(container);
+  await File(path).writeAsString(buffer.toString());
+  await runProcess(Platform.resolvedExecutable, ["format", path]);
+}
+
+Future<void> writeDartShared() async {
+  final StringBuffer buffer = StringBuffer();
+  buffer.write(headerDart(
+    copyrightYear: 2024,
+  ));
+
+  for (final elementType in Container.struct.elementTypes) {
+    final pointerType = PointerType(elementType);
+    String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
+    if (elementType.isFloatingPoint) {
+      value = '($value).toDouble()';
+    }
+
+    buffer.write("""
+@Native<${elementType.dartCType} Function(${pointerType.dartCType}, Size)>(symbol: 'Take${elementType.dartCType}Pointer', isLeaf: true)
+external ${elementType.dartType} take${elementType.dartCType}Pointer(${pointerType.dartCType} pointer, int length);
+
+@Native<${elementType.dartCType} Function(
+  ${[for (int i = 0; i < manyCount; i++) '${pointerType.dartCType},'].join()}
+)>(symbol: 'Take${elementType.dartCType}PointerMany', isLeaf: true)
+external ${elementType.dartType} take${elementType.dartCType}PointerMany(
+  ${[
+      for (int i = 0; i < manyCount; i++) '${pointerType.dartCType} pointer$i,',
+    ].join()}
+);
+
+""");
+    if (elementType != bool_) {
+      buffer.write("""
+${pointerType.dartTypedData} make${pointerType.dartTypedData}(int length) {
+  final typedData = ${pointerType.dartTypedData}(length);
+  for (int i = 0; i < length; i++) {
+    final value = $value;
+    typedData[i] = value;
+  }
+  return typedData;
+}
+
+${elementType.dartType} makeExpectedResult${elementType.dartCType}(int start, int end) {
+  ${elementType.dartType} expectedResult = 0;
+  for (int i = start; i < end; i++) {
+    final value = $value;
+    expectedResult += value;
+  }
+  return expectedResult;
+}
+
+""");
+    }
+  }
+
+  final path = Platform.script
+      .resolve("../../ffi/address_of_generated_shared.dart")
+      .toFilePath();
+  ;
+  await File(path).writeAsString(buffer.toString());
+  await runProcess(Platform.resolvedExecutable, ["format", path]);
+}
+
+String testPath(Container container) {
+  final lowerCase = container.name.toLowerCase();
+  return Platform.script
+      .resolve("../../ffi/address_of_${lowerCase}_generated_test.dart")
+      .toFilePath();
+}
+
+String headerDart({
+  required int copyrightYear,
+}) {
+  return """
+${headerCommon(copyrightYear: copyrightYear, generatorPath: generatorPath)}
+//
+// SharedObjects=ffi_test_functions
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=90
+// VMOptions=--use-slow-path
+// VMOptions=--use-slow-path --stacktrace-every=100
+
+// ignore_for_file: unused_import
+
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import 'address_of_generated_shared.dart';
+import 'address_of_shared.dart';
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
+
+""";
+}
+
+const manyCount = 20;
+
+Future<void> writeC() async {
+  final StringBuffer buffer = StringBuffer();
+  buffer.write(headerC(copyrightYear: 2024, generatorPath: generatorPath));
+
+  for (final elementType in Container.struct.elementTypes) {
+    final pointerType = PointerType(elementType);
+
+    String coutCast(String input) {
+      if (elementType == uint8 || elementType == int8 || elementType == bool_) {
+        return "static_cast<int>($input)";
+      }
+      return input;
+    }
+
+    String opCast(String input) {
+      if (elementType == bool_) {
+        return "static_cast<int>($input)";
+      }
+      return input;
+    }
+
+    buffer.write('''
+DART_EXPORT ${elementType.cType} Take${elementType.dartCType}Pointer(${pointerType.cType} data, size_t length) {
+  ${elementType.cType} result = ${elementType.zero};
+  if (length > 100) {
+    std::cout << "Mangled arguments\\n";
+    return result;
+  }
+  for (size_t i = 0; i < length; i++) {
+    std::cout << "data[" << i << "] = " << ${coutCast('data[i]')} << "\\n";
+    result ${elementType.addAssignOp} ${opCast('data[i]')};
+  }
+  return result;
+}
+
+DART_EXPORT ${elementType.cType} Take${elementType.dartCType}PointerMany(
+  ${[
+      for (int i = 0; i < manyCount; i++) '${pointerType.cType} data$i'
+    ].join(',')}
+) {
+  ${elementType.cType} result = ${elementType.zero};
+  ${[
+      for (int i = 0; i < manyCount; i++)
+        '''
+std::cout << "data$i[0] = " << ${coutCast('data$i[0]')} << "\\n";
+result ${elementType.addAssignOp} ${opCast('data$i[0]')};''',
+    ].join('\n')}
+  return result;
+}
+
+''');
+  }
+
+  final elementType = int16;
+  for (final compoundType in [
+    StructType([elementType]),
+    UnionType([elementType])
+  ]) {
+    final pointerType = PointerType(compoundType);
+
+    buffer.write(compoundType.cDefinition);
+
+    buffer.write('''
+DART_EXPORT ${elementType.cType} Take${compoundType.dartCType}PointerMany(
+  ${[
+      for (int i = 0; i < manyCount; i++) '${pointerType.cType} data$i'
+    ].join(',')}
+) {
+  ${elementType.cType} result = ${elementType.zero};
+  ${[
+      for (int i = 0; i < manyCount; i++)
+        '''
+std::cout << "data$i->a0 = " << ${'data$i->a0'} << "\\n";
+result += data$i->a0;''',
+    ].join('\n')}
+  return result;
+}
+  
+''');
+  }
+
+  buffer.write(footerC);
+
+  await File(ccPath).writeAsString(buffer.toString());
+  await runProcess("clang-format", ["-i", ccPath]);
+}
+
+final ccPath = Platform.script
+    .resolve("../../../runtime/bin/ffi_test/ffi_test_functions_generated_2.cc")
+    .toFilePath();
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index 35d7034..4afd2a2 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -194,6 +194,19 @@
     }
     return primitiveSizesInBytes[primitive]!;
   }
+
+  String get zero {
+    if (isBool) return 'false';
+    if (isInteger) return '0';
+    if (isFloatingPoint) return '0.0';
+    throw 'Unknown type $primitive';
+  }
+
+  String get addAssignOp {
+    if (isBool) return '^=';
+    if (isInteger || isFloatingPoint) return '+=';
+    throw 'Unknown type $primitive';
+  }
 }
 
 class PointerType extends CType {
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index 24c2455..1b72f51 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -502,7 +502,7 @@
   }
 }
 
-extension on CompositeType {
+extension CompositeTypeGenerator on CompositeType {
   String dartClass() {
     final self = this;
     final packingAnnotation = (self is StructType) && self.hasPacking
diff --git a/tests/ffi/generator/unwrap_typeddata_test_generator.dart b/tests/ffi/generator/unwrap_typeddata_test_generator.dart
deleted file mode 100644
index d52db6d..0000000
--- a/tests/ffi/generator/unwrap_typeddata_test_generator.dart
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright (c) 2023, 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.
-
-import 'dart:io';
-
-import 'c_types.dart';
-import 'utils.dart';
-
-void main() async {
-  await Future.wait([
-    writeC(),
-    for (bool isNative in [true, false]) writeDart(isNative: isNative),
-  ]);
-}
-
-final elementTypes = [
-  int8,
-  int16,
-  int32,
-  int64,
-  uint8,
-  uint16,
-  uint32,
-  uint64,
-  float,
-  double_,
-];
-
-const generatorPath =
-    'tests/ffi/generator/unwrap_typeddata_test_generator.dart';
-
-Future<void> writeDart({
-  required bool isNative,
-}) async {
-  final StringBuffer buffer = StringBuffer();
-  buffer.write(headerDart(
-    copyrightYear: 2023,
-  ));
-
-  final forceDlOpen = !isNative
-      ? ''
-      : '''
-  // Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
-  dlopenGlobalPlatformSpecific('ffi_test_functions');
-
-''';
-
-  buffer.write("""
-void main() {$forceDlOpen
-  for (int i = 0; i < 100; ++i) {
-""");
-
-  for (final elementType in elementTypes) {
-    final pointerType = PointerType(elementType);
-    buffer.write("""
-    testUnwrap${pointerType.dartTypedData}();
-    testUnwrap${pointerType.dartTypedData}View();
-    testUnwrap${pointerType.dartTypedData}Many();
-""");
-  }
-
-  buffer.write("""
-  }
-}
-
-""");
-
-  for (final elementType in elementTypes) {
-    final pointerType = PointerType(elementType);
-    String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
-    if (elementType.isFloatingPoint) {
-      value = '($value).toDouble()';
-    }
-    final equals = elementType.isFloatingPoint ? 'approxEquals' : 'equals';
-
-    final manyDartCTypes = Iterable.generate(
-      many,
-      (i) => '${pointerType.dartCType}',
-    ).join(',');
-    final manyArgs = Iterable.generate(
-      many,
-      (i) => '${pointerType.dartTypedData} typedData$i',
-    ).join(',');
-    final manyBodies = Iterable.generate(
-      many,
-      (i) =>
-          '${pointerType.dartTypedData}.view(source.buffer, elementSize * $i, 1)',
-    ).join(',');
-
-    if (isNative) {
-      buffer.write("""
-@Native<${elementType.dartCType} Function(${pointerType.dartCType}, Size)>(symbol: 'Unwrap${pointerType.dartTypedData}', isLeaf: true)
-external ${elementType.dartType} unwrap${pointerType.dartTypedData}(${pointerType.dartTypedData} typedData, int length);
-
-@Native<${elementType.dartCType} Function($manyDartCTypes,)>(symbol: 'Unwrap${pointerType.dartTypedData}Many', isLeaf: true)
-external ${elementType.dartType} unwrap${pointerType.dartTypedData}Many($manyArgs,);
-
-""");
-    } else {
-      buffer.write("""
-final unwrap${pointerType.dartTypedData} = ffiTestFunctions.lookupFunction<
-    ${elementType.dartCType} Function(${pointerType.dartCType}, Size),
-    ${elementType.dartType} Function(${pointerType.dartTypedData}, int)>('Unwrap${pointerType.dartTypedData}', isLeaf: true);
-
-final unwrap${pointerType.dartTypedData}Many = ffiTestFunctions.lookupFunction<
-    ${elementType.dartCType} Function($manyDartCTypes,),
-    ${elementType.dartType} Function($manyArgs,)>('Unwrap${pointerType.dartTypedData}Many', isLeaf: true);
-
-""");
-    }
-
-    buffer.write("""
-void testUnwrap${pointerType.dartTypedData}() {
-  const length = 10;
-  final typedData = ${pointerType.dartTypedData}(length);
-  ${elementType.dartType} expectedResult = 0;
-  for (int i = 0; i < length; i++) {
-    final value = $value;
-    typedData[i] = value;
-    expectedResult += value;
-  }
-  final result = unwrap${pointerType.dartTypedData}(typedData, typedData.length);
-  Expect.$equals(expectedResult, result);
-}
-
-void testUnwrap${pointerType.dartTypedData}View() {
-  const sourceLength = 30;
-  const elementSize = ${elementType.size};
-  const viewStart = 10;
-  const viewOffsetInBytes = viewStart * elementSize;
-  const viewLength = 10;
-  final viewEnd = viewStart + viewLength;
-  final source = ${pointerType.dartTypedData}(sourceLength);
-  final view = ${pointerType.dartTypedData}.view(source.buffer, viewOffsetInBytes, viewLength);
-  ${elementType.dartType} expectedResult = 0;
-  for (int i = 0; i < sourceLength; i++) {
-    final value = $value;
-    source[i] = value;
-    if (viewStart <= i && i < viewEnd) {
-      expectedResult += value;
-    }
-  }
-  final result = unwrap${pointerType.dartTypedData}(view, view.length);
-  Expect.$equals(expectedResult, result);
-}
-
-void testUnwrap${pointerType.dartTypedData}Many() {
-  const length = 20;
-  const elementSize = ${elementType.size};
-  final source = ${pointerType.dartTypedData}(length);
-  ${elementType.dartType} expectedResult = 0;
-  for (int i = 0; i < length; i++) {
-    final value = $value;
-    source[i] = value;
-    expectedResult += value;
-  }
-  final result = unwrap${pointerType.dartTypedData}Many(
-    $manyBodies,
-  );
-  Expect.$equals(expectedResult, result);
-}
-
-""");
-  }
-
-  final path = testPath(isNative: isNative);
-  await File(path).writeAsString(buffer.toString());
-  await runProcess(Platform.resolvedExecutable, ["format", path]);
-}
-
-String testPath({
-  required bool isNative,
-}) {
-  final suffix = '${isNative ? '_native' : ''}';
-  return Platform.script
-      .resolve("../../ffi/unwrap_typeddata_generated${suffix}_test.dart")
-      .toFilePath();
-}
-
-String headerDart({
-  required int copyrightYear,
-}) {
-  return """
-${headerCommon(copyrightYear: copyrightYear, generatorPath: generatorPath)}
-//
-// SharedObjects=ffi_test_functions
-// VMOptions=
-// VMOptions=--deterministic --optimization-counter-threshold=90
-// VMOptions=--use-slow-path
-// VMOptions=--use-slow-path --stacktrace-every=100
-
-import 'dart:ffi';
-import 'dart:typed_data';
-
-import 'package:expect/expect.dart';
-
-import 'dylib_utils.dart';
-
-final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
-
-""";
-}
-
-const many = 20;
-
-Future<void> writeC() async {
-  final StringBuffer buffer = StringBuffer();
-  buffer.write(headerC(copyrightYear: 2023, generatorPath: generatorPath));
-
-  for (final elementType in elementTypes) {
-    final pointerType = PointerType(elementType);
-
-    String coutCast(String input) {
-      if (elementType == uint8 || elementType == int8) {
-        return "static_cast<int>($input)";
-      }
-      return input;
-    }
-
-    final manyArgs = Iterable.generate(
-      many,
-      (i) => '${pointerType.cType} data$i',
-    ).join(',');
-    final manyBodies = Iterable.generate(
-      many,
-      (i) => '''
-std::cout << "data$i[0] = " << ${coutCast('data$i[0]')} << "\\n";
-result += data$i[0];''',
-    ).join('\n');
-
-    buffer.write('''
-DART_EXPORT ${elementType.cType} Unwrap${pointerType.dartTypedData}(${pointerType.cType} data, size_t length) {
-  ${elementType.cType} result = 0;
-  for (size_t i = 0; i < length; i++) {
-    std::cout << "data[" << i << "] = " << ${coutCast('data[i]')} << "\\n";
-    result += data[i];
-  }
-  return result;
-}
-
-DART_EXPORT ${elementType.cType} Unwrap${pointerType.dartTypedData}Many($manyArgs) {
-  ${elementType.cType} result = 0;
-  $manyBodies
-  return result;
-}
-
-''');
-  }
-
-  buffer.write(footerC);
-
-  await File(ccPath).writeAsString(buffer.toString());
-  await runProcess("clang-format", ["-i", ccPath]);
-}
-
-final ccPath = Platform.script
-    .resolve("../../../runtime/bin/ffi_test/ffi_test_functions_generated_2.cc")
-    .toFilePath();
diff --git a/tests/ffi/static_checks/vmspecific_static_checks_address_of_test.dart b/tests/ffi/static_checks/vmspecific_static_checks_address_of_test.dart
new file mode 100644
index 0000000..86d2fe4
--- /dev/null
+++ b/tests/ffi/static_checks/vmspecific_static_checks_address_of_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2024, 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.
+//
+// Dart test program for testing dart:ffi extra checks
+//
+// SharedObjects=ffi_test_dynamic_library ffi_test_functions
+
+import 'dart:ffi';
+
+void main() {
+  testUnsupportedAddress();
+}
+
+void testUnsupportedAddress() {
+  myNative(4.address);
+  //         ^^^^^^^
+  // [analyzer] COMPILE_TIME_ERROR.ADDRESS_RECEIVER
+  // [cfe] The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field.
+
+  final myStruct = Struct.create<MyStruct>();
+  myStruct.address;
+  //       ^^^^^^^
+  // [analyzer] COMPILE_TIME_ERROR.ADDRESS_POSITION
+  // [cfe] The '.address' expression can only be used as argument to a leaf native external call.
+
+  myNativeNoLeaf(myStruct.a.address);
+  //                        ^^^^^^^
+  // [analyzer] COMPILE_TIME_ERROR.ADDRESS_POSITION
+  // [cfe] The '.address' expression can only be used as argument to a leaf native external call.
+}
+
+@Native<Int8 Function(Pointer<Int8>)>(isLeaf: true)
+external int myNative(Pointer<Int8> pointer);
+
+@Native<Int8 Function(Pointer<Int8>)>()
+external int myNativeNoLeaf(Pointer<Int8> pointer);
+
+final class MyStruct extends Struct {
+  @Int8()
+  external int a;
+}