[cfe/ffi] Move `NativeTypeCfe` to its own file

We're going to extend this logic in subsequent CLs, so lets give this
abstraction its own place to live.

TEST=just moving file contents

Change-Id: Ie6b267e77dd3a772bb3bbf0b79b33946ee767b0e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221630
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/pkg/vm/lib/transformations/ffi/definitions.dart b/pkg/vm/lib/transformations/ffi/definitions.dart
index 29ae1d9..d80ef22 100644
--- a/pkg/vm/lib/transformations/ffi/definitions.dart
+++ b/pkg/vm/lib/transformations/ffi/definitions.dart
@@ -2,8 +2,6 @@
 // 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:math' as math;
-
 import 'package:front_end/src/api_unstable/vm.dart'
     show
         messageFfiPackedAnnotationAlignment,
@@ -33,6 +31,7 @@
 
 import 'abi.dart';
 import 'common.dart';
+import 'native_type_cfe.dart';
 
 /// Checks and elaborates the dart:ffi compounds and their fields.
 ///
@@ -956,546 +955,3 @@
 
   CompoundField(this.type, this.field, this.getter, this.setter);
 }
-
-/// The layout of a `Struct` or `Union` in one [Abi].
-class CompoundLayout {
-  /// Size of the entire struct or union.
-  final int size;
-
-  /// Alignment of struct or union when nested in a struct.
-  final int alignment;
-
-  /// Offset in bytes for each field, indexed by field number.
-  ///
-  /// Always 0 for unions.
-  final List<int> offsets;
-
-  CompoundLayout(this.size, this.alignment, this.offsets);
-}
-
-/// AST node wrapper for native types.
-///
-/// This algebraic data structure does not stand on its own but refers
-/// intimately to AST nodes such as [Class].
-abstract class NativeTypeCfe {
-  factory NativeTypeCfe(FfiTransformer transformer, DartType dartType,
-      {List<int>? arrayDimensions,
-      Map<Class, NativeTypeCfe> compoundCache = const {}}) {
-    if (transformer.isPrimitiveType(dartType)) {
-      final clazz = (dartType as InterfaceType).classNode;
-      final nativeType = transformer.getType(clazz)!;
-      return PrimitiveNativeTypeCfe(nativeType, clazz);
-    }
-    if (transformer.isPointerType(dartType)) {
-      return PointerNativeTypeCfe();
-    }
-    if (transformer.isCompoundSubtype(dartType)) {
-      final clazz = (dartType as InterfaceType).classNode;
-      if (compoundCache.containsKey(clazz)) {
-        return compoundCache[clazz]!;
-      } else {
-        throw "Class '$clazz' not found in compoundCache.";
-      }
-    }
-    if (transformer.isArrayType(dartType)) {
-      if (arrayDimensions == null) {
-        throw "Must have array dimensions for ArrayType.";
-      }
-      if (arrayDimensions.length == 0) {
-        throw "Must have a size for this array dimension.";
-      }
-      final elementType = transformer.arraySingleElementType(dartType);
-      final elementCfeType =
-          NativeTypeCfe(transformer, elementType, compoundCache: compoundCache);
-      if (elementCfeType is InvalidNativeTypeCfe) {
-        return elementCfeType;
-      }
-      return ArrayNativeTypeCfe.multi(elementCfeType, arrayDimensions);
-    }
-    throw "Invalid type $dartType";
-  }
-
-  /// The size in bytes per [Abi].
-  Map<Abi, int> get size;
-
-  /// The alignment inside structs in bytes per [Abi].
-  ///
-  /// This is not the alignment on stack, this is only calculated in the VM.
-  Map<Abi, int> get alignment;
-
-  /// Generates a Constant representing the type which is consumed by the VM.
-  ///
-  /// Takes [transformer] to be able to lookup classes and methods.
-  ///
-  /// See runtime/vm/compiler/ffi/native_type.cc:NativeType::FromAbstractType.
-  Constant generateConstant(FfiTransformer transformer);
-
-  /// Generates the return statement for a compound field getter with this type.
-  ///
-  /// Takes [transformer] to be able to lookup classes and methods.
-  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer);
-
-  /// Generates the return statement for a compound field setter with this type.
-  ///
-  /// Takes [transformer] to be able to lookup classes and methods.
-  ReturnStatement generateSetterStatement(
-      DartType dartType,
-      int fileOffset,
-      Map<Abi, int> offsets,
-      bool unalignedAccess,
-      VariableDeclaration argument,
-      FfiTransformer transformer);
-}
-
-class InvalidNativeTypeCfe implements NativeTypeCfe {
-  final String reason;
-
-  InvalidNativeTypeCfe(this.reason);
-
-  @override
-  Map<Abi, int> get alignment => throw reason;
-
-  @override
-  Constant generateConstant(FfiTransformer transformer) => throw reason;
-
-  @override
-  ReturnStatement generateGetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          FfiTransformer transformer) =>
-      throw reason;
-
-  @override
-  ReturnStatement generateSetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          VariableDeclaration argument,
-          FfiTransformer transformer) =>
-      throw reason;
-
-  @override
-  Map<Abi, int> get size => throw reason;
-}
-
-class PrimitiveNativeTypeCfe implements NativeTypeCfe {
-  final NativeType nativeType;
-
-  final Class clazz;
-
-  PrimitiveNativeTypeCfe(this.nativeType, this.clazz);
-
-  @override
-  Map<Abi, int> get size {
-    final int size = nativeTypeSizes[nativeType]!;
-    if (size == WORD_SIZE) {
-      return wordSize;
-    }
-    return {for (var abi in Abi.values) abi: size};
-  }
-
-  @override
-  Map<Abi, int> get alignment => {
-        for (var abi in Abi.values)
-          abi: nonSizeAlignment[abi]![nativeType] ?? size[abi]!
-      };
-
-  @override
-  Constant generateConstant(FfiTransformer transformer) =>
-      TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
-
-  bool get isFloat =>
-      nativeType == NativeType.kFloat || nativeType == NativeType.kDouble;
-
-  bool isUnaligned(Map<Abi, int> offsets) {
-    final alignments = alignment;
-    for (final abi in offsets.keys) {
-      final offset = offsets[abi]!;
-      final alignment = alignments[abi]!;
-      if (offset % alignment != 0) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /// Sample output for `int get x =>`:
-  ///
-  /// ```
-  /// _loadInt8(_typedDataBase, offset);
-  /// ```
-  @override
-  ReturnStatement generateGetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          FfiTransformer transformer) =>
-      ReturnStatement(StaticInvocation(
-          (unalignedAccess && isFloat
-              ? transformer.loadUnalignedMethods
-              : transformer.loadMethods)[nativeType]!,
-          Arguments([
-            transformer.getCompoundTypedDataBaseField(
-                ThisExpression(), fileOffset),
-            transformer.runtimeBranchOnLayout(offsets)
-          ]))
-        ..fileOffset = fileOffset);
-
-  /// Sample output for `set x(int #v) =>`:
-  ///
-  /// ```
-  /// _storeInt8(_typedDataBase, offset, #v);
-  /// ```
-  @override
-  ReturnStatement generateSetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          VariableDeclaration argument,
-          FfiTransformer transformer) =>
-      ReturnStatement(StaticInvocation(
-          (unalignedAccess && isFloat
-              ? transformer.storeUnalignedMethods
-              : transformer.storeMethods)[nativeType]!,
-          Arguments([
-            transformer.getCompoundTypedDataBaseField(
-                ThisExpression(), fileOffset),
-            transformer.runtimeBranchOnLayout(offsets),
-            VariableGet(argument)
-          ]))
-        ..fileOffset = fileOffset);
-}
-
-class PointerNativeTypeCfe implements NativeTypeCfe {
-  @override
-  Map<Abi, int> get size => wordSize;
-
-  @override
-  Map<Abi, int> get alignment => wordSize;
-
-  @override
-  Constant generateConstant(FfiTransformer transformer) => TypeLiteralConstant(
-          InterfaceType(transformer.pointerClass, Nullability.nonNullable, [
-        InterfaceType(
-            transformer.pointerClass.superclass!, Nullability.nonNullable)
-      ]));
-
-  /// Sample output for `Pointer<Int8> get x =>`:
-  ///
-  /// ```
-  /// _fromAddress<Int8>(_loadIntPtr(_typedDataBase, offset));
-  /// ```
-  @override
-  ReturnStatement generateGetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          FfiTransformer transformer) =>
-      ReturnStatement(StaticInvocation(
-          transformer.fromAddressInternal,
-          Arguments([
-            StaticInvocation(
-                transformer.loadMethods[NativeType.kIntptr]!,
-                Arguments([
-                  transformer.getCompoundTypedDataBaseField(
-                      ThisExpression(), fileOffset),
-                  transformer.runtimeBranchOnLayout(offsets)
-                ]))
-              ..fileOffset = fileOffset
-          ], types: [
-            (dartType as InterfaceType).typeArguments.single
-          ]))
-        ..fileOffset = fileOffset);
-
-  /// Sample output for `set x(Pointer<Int8> #v) =>`:
-  ///
-  /// ```
-  /// _storeIntPtr(_typedDataBase, offset, (#v as Pointer<Int8>).address);
-  /// ```
-  @override
-  ReturnStatement generateSetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          VariableDeclaration argument,
-          FfiTransformer transformer) =>
-      ReturnStatement(StaticInvocation(
-          transformer.storeMethods[NativeType.kIntptr]!,
-          Arguments([
-            transformer.getCompoundTypedDataBaseField(
-                ThisExpression(), fileOffset),
-            transformer.runtimeBranchOnLayout(offsets),
-            InstanceGet(InstanceAccessKind.Instance, VariableGet(argument),
-                transformer.addressGetter.name,
-                interfaceTarget: transformer.addressGetter,
-                resultType: transformer.addressGetter.getterType)
-              ..fileOffset = fileOffset
-          ]))
-        ..fileOffset = fileOffset);
-}
-
-abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
-  final Class clazz;
-
-  final List<NativeTypeCfe> members;
-
-  final Map<Abi, CompoundLayout> layout;
-
-  CompoundNativeTypeCfe._(this.clazz, this.members, this.layout);
-
-  @override
-  Map<Abi, int> get size =>
-      layout.map((abi, layout) => MapEntry(abi, layout.size));
-
-  @override
-  Map<Abi, int> get alignment =>
-      layout.map((abi, layout) => MapEntry(abi, layout.alignment));
-
-  @override
-  Constant generateConstant(FfiTransformer transformer) =>
-      TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
-
-  /// Sample output for `MyStruct get x =>`:
-  ///
-  /// ```
-  /// MyStruct.#fromTypedDataBase(
-  ///   typedDataBaseOffset(_typedDataBase, offset, size, dartType)
-  /// );
-  /// ```
-  @override
-  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
-    final constructor = clazz.constructors
-        .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
-
-    return ReturnStatement(ConstructorInvocation(
-        constructor,
-        Arguments([
-          transformer.typedDataBaseOffset(
-              transformer.getCompoundTypedDataBaseField(
-                  ThisExpression(), fileOffset),
-              transformer.runtimeBranchOnLayout(offsets),
-              transformer.runtimeBranchOnLayout(size),
-              dartType,
-              fileOffset)
-        ]))
-      ..fileOffset = fileOffset);
-  }
-
-  /// Sample output for `set x(MyStruct #v) =>`:
-  ///
-  /// ```
-  /// _memCopy(_typedDataBase, offset, #v._typedDataBase, 0, size);
-  /// ```
-  @override
-  ReturnStatement generateSetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          VariableDeclaration argument,
-          FfiTransformer transformer) =>
-      ReturnStatement(StaticInvocation(
-          transformer.memCopy,
-          Arguments([
-            transformer.getCompoundTypedDataBaseField(
-                ThisExpression(), fileOffset),
-            transformer.runtimeBranchOnLayout(offsets),
-            transformer.getCompoundTypedDataBaseField(
-                VariableGet(argument), fileOffset),
-            ConstantExpression(IntConstant(0)),
-            transformer.runtimeBranchOnLayout(size),
-          ]))
-        ..fileOffset = fileOffset);
-}
-
-class StructNativeTypeCfe extends CompoundNativeTypeCfe {
-  // Nullable int.
-  final int? packing;
-
-  factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members,
-      {int? packing}) {
-    final layout = {
-      for (var abi in Abi.values) abi: _calculateLayout(members, packing, abi)
-    };
-    return StructNativeTypeCfe._(clazz, members, packing, layout);
-  }
-
-  StructNativeTypeCfe._(Class clazz, List<NativeTypeCfe> members, this.packing,
-      Map<Abi, CompoundLayout> layout)
-      : super._(clazz, members, layout);
-
-  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
-  // NativeStructType::FromNativeTypes.
-  static CompoundLayout _calculateLayout(
-      List<NativeTypeCfe> types, int? packing, Abi abi) {
-    int offset = 0;
-    final offsets = <int>[];
-    int structAlignment = 1;
-    for (int i = 0; i < types.length; i++) {
-      final int size = types[i].size[abi]!;
-      int alignment = types[i].alignment[abi]!;
-      if (packing != null && packing < alignment) {
-        alignment = packing;
-      }
-      if (alignment > 0) {
-        offset = _alignOffset(offset, alignment);
-      }
-      offsets.add(offset);
-      offset += size;
-      structAlignment = math.max(structAlignment, alignment);
-    }
-    final int size = _alignOffset(offset, structAlignment);
-    return CompoundLayout(size, structAlignment, offsets);
-  }
-}
-
-class UnionNativeTypeCfe extends CompoundNativeTypeCfe {
-  factory UnionNativeTypeCfe(Class clazz, List<NativeTypeCfe> members) {
-    final layout = {
-      for (var abi in Abi.values) abi: _calculateLayout(members, abi)
-    };
-    return UnionNativeTypeCfe._(clazz, members, layout);
-  }
-
-  UnionNativeTypeCfe._(
-      Class clazz, List<NativeTypeCfe> members, Map<Abi, CompoundLayout> layout)
-      : super._(clazz, members, layout);
-
-  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
-  // NativeUnionType::FromNativeTypes.
-  static CompoundLayout _calculateLayout(List<NativeTypeCfe> types, Abi abi) {
-    int unionSize = 1;
-    int unionAlignment = 1;
-    for (int i = 0; i < types.length; i++) {
-      final int size = types[i].size[abi]!;
-      int alignment = types[i].alignment[abi]!;
-      unionSize = math.max(unionSize, size);
-      unionAlignment = math.max(unionAlignment, alignment);
-    }
-    final int size = _alignOffset(unionSize, unionAlignment);
-    return CompoundLayout(size, unionAlignment, List.filled(types.length, 0));
-  }
-}
-
-class ArrayNativeTypeCfe implements NativeTypeCfe {
-  final NativeTypeCfe elementType;
-  final int length;
-
-  ArrayNativeTypeCfe(this.elementType, this.length);
-
-  factory ArrayNativeTypeCfe.multi(
-      NativeTypeCfe elementType, List<int> dimensions) {
-    if (dimensions.length == 1) {
-      return ArrayNativeTypeCfe(elementType, dimensions.single);
-    }
-    return ArrayNativeTypeCfe(
-        ArrayNativeTypeCfe.multi(elementType, dimensions.sublist(1)),
-        dimensions.first);
-  }
-
-  List<int> get dimensions {
-    final elementType = this.elementType;
-    if (elementType is ArrayNativeTypeCfe) {
-      return [length, ...elementType.dimensions];
-    }
-    return [length];
-  }
-
-  List<int> get nestedDimensions => dimensions.sublist(1);
-
-  int get dimensionsFlattened =>
-      dimensions.fold(1, (accumulator, element) => accumulator * element);
-
-  NativeTypeCfe get singleElementType {
-    final elementType = this.elementType;
-    if (elementType is ArrayNativeTypeCfe) {
-      return elementType.singleElementType;
-    }
-    return elementType;
-  }
-
-  @override
-  Map<Abi, int> get size =>
-      elementType.size.map((abi, size) => MapEntry(abi, size * length));
-
-  @override
-  Map<Abi, int> get alignment => elementType.alignment;
-
-  // Note that we flatten multi dimensional arrays.
-  @override
-  Constant generateConstant(FfiTransformer transformer) =>
-      InstanceConstant(transformer.ffiInlineArrayClass.reference, [], {
-        transformer.ffiInlineArrayElementTypeField.fieldReference:
-            singleElementType.generateConstant(transformer),
-        transformer.ffiInlineArrayLengthField.fieldReference:
-            IntConstant(dimensionsFlattened)
-      });
-
-  /// Sample output for `Array<Int8> get x =>`:
-  ///
-  /// ```
-  /// Array<Int8>._(
-  ///   typedDataBaseOffset(_typedDataBase, offset, size, typeArgument)
-  /// );
-  /// ```
-  @override
-  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
-    InterfaceType typeArgument =
-        (dartType as InterfaceType).typeArguments.single as InterfaceType;
-    return ReturnStatement(ConstructorInvocation(
-        transformer.arrayConstructor,
-        Arguments([
-          transformer.typedDataBaseOffset(
-              transformer.getCompoundTypedDataBaseField(
-                  ThisExpression(), fileOffset),
-              transformer.runtimeBranchOnLayout(offsets),
-              transformer.runtimeBranchOnLayout(size),
-              typeArgument,
-              fileOffset),
-          ConstantExpression(IntConstant(length)),
-          transformer.intListConstantExpression(nestedDimensions)
-        ], types: [
-          typeArgument
-        ]))
-      ..fileOffset = fileOffset);
-  }
-
-  /// Sample output for `set x(Array #v) =>`:
-  ///
-  /// ```
-  /// _memCopy(_typedDataBase, offset, #v._typedDataBase, 0, size);
-  /// ```
-  @override
-  ReturnStatement generateSetterStatement(
-          DartType dartType,
-          int fileOffset,
-          Map<Abi, int> offsets,
-          bool unalignedAccess,
-          VariableDeclaration argument,
-          FfiTransformer transformer) =>
-      ReturnStatement(StaticInvocation(
-          transformer.memCopy,
-          Arguments([
-            transformer.getCompoundTypedDataBaseField(
-                ThisExpression(), fileOffset),
-            transformer.runtimeBranchOnLayout(offsets),
-            transformer.getArrayTypedDataBaseField(
-                VariableGet(argument), fileOffset),
-            ConstantExpression(IntConstant(0)),
-            transformer.runtimeBranchOnLayout(size),
-          ]))
-        ..fileOffset = fileOffset);
-}
-
-int _alignOffset(int offset, int alignment) =>
-    ((offset + alignment - 1) ~/ alignment) * alignment;
diff --git a/pkg/vm/lib/transformations/ffi/native_type_cfe.dart b/pkg/vm/lib/transformations/ffi/native_type_cfe.dart
new file mode 100644
index 0000000..9784ac9
--- /dev/null
+++ b/pkg/vm/lib/transformations/ffi/native_type_cfe.dart
@@ -0,0 +1,553 @@
+// Copyright (c) 2021, 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:math' as math;
+
+import 'package:kernel/ast.dart';
+
+import 'abi.dart';
+import 'common.dart';
+
+/// AST node wrapper for native types.
+///
+/// This algebraic data structure does not stand on its own but refers
+/// intimately to AST nodes such as [Class].
+abstract class NativeTypeCfe {
+  factory NativeTypeCfe(FfiTransformer transformer, DartType dartType,
+      {List<int>? arrayDimensions,
+      Map<Class, NativeTypeCfe> compoundCache = const {}}) {
+    if (transformer.isPrimitiveType(dartType)) {
+      final clazz = (dartType as InterfaceType).classNode;
+      final nativeType = transformer.getType(clazz)!;
+      return PrimitiveNativeTypeCfe(nativeType, clazz);
+    }
+    if (transformer.isPointerType(dartType)) {
+      return PointerNativeTypeCfe();
+    }
+    if (transformer.isCompoundSubtype(dartType)) {
+      final clazz = (dartType as InterfaceType).classNode;
+      if (compoundCache.containsKey(clazz)) {
+        return compoundCache[clazz]!;
+      } else {
+        throw "Class '$clazz' not found in compoundCache.";
+      }
+    }
+    if (transformer.isArrayType(dartType)) {
+      if (arrayDimensions == null) {
+        throw "Must have array dimensions for ArrayType.";
+      }
+      if (arrayDimensions.length == 0) {
+        throw "Must have a size for this array dimension.";
+      }
+      final elementType = transformer.arraySingleElementType(dartType);
+      final elementCfeType =
+          NativeTypeCfe(transformer, elementType, compoundCache: compoundCache);
+      if (elementCfeType is InvalidNativeTypeCfe) {
+        return elementCfeType;
+      }
+      return ArrayNativeTypeCfe.multi(elementCfeType, arrayDimensions);
+    }
+    throw "Invalid type $dartType";
+  }
+
+  /// The size in bytes per [Abi].
+  Map<Abi, int> get size;
+
+  /// The alignment inside structs in bytes per [Abi].
+  ///
+  /// This is not the alignment on stack, this is only calculated in the VM.
+  Map<Abi, int> get alignment;
+
+  /// Generates a Constant representing the type which is consumed by the VM.
+  ///
+  /// Takes [transformer] to be able to lookup classes and methods.
+  ///
+  /// See runtime/vm/compiler/ffi/native_type.cc:NativeType::FromAbstractType.
+  Constant generateConstant(FfiTransformer transformer);
+
+  /// Generates the return statement for a compound field getter with this type.
+  ///
+  /// Takes [transformer] to be able to lookup classes and methods.
+  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
+      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer);
+
+  /// Generates the return statement for a compound field setter with this type.
+  ///
+  /// Takes [transformer] to be able to lookup classes and methods.
+  ReturnStatement generateSetterStatement(
+      DartType dartType,
+      int fileOffset,
+      Map<Abi, int> offsets,
+      bool unalignedAccess,
+      VariableDeclaration argument,
+      FfiTransformer transformer);
+}
+
+class InvalidNativeTypeCfe implements NativeTypeCfe {
+  final String reason;
+
+  InvalidNativeTypeCfe(this.reason);
+
+  @override
+  Map<Abi, int> get alignment => throw reason;
+
+  @override
+  Constant generateConstant(FfiTransformer transformer) => throw reason;
+
+  @override
+  ReturnStatement generateGetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          FfiTransformer transformer) =>
+      throw reason;
+
+  @override
+  ReturnStatement generateSetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          VariableDeclaration argument,
+          FfiTransformer transformer) =>
+      throw reason;
+
+  @override
+  Map<Abi, int> get size => throw reason;
+}
+
+class PrimitiveNativeTypeCfe implements NativeTypeCfe {
+  final NativeType nativeType;
+
+  final Class clazz;
+
+  PrimitiveNativeTypeCfe(this.nativeType, this.clazz);
+
+  @override
+  Map<Abi, int> get size {
+    final int size = nativeTypeSizes[nativeType]!;
+    if (size == WORD_SIZE) {
+      return wordSize;
+    }
+    return {for (var abi in Abi.values) abi: size};
+  }
+
+  @override
+  Map<Abi, int> get alignment => {
+        for (var abi in Abi.values)
+          abi: nonSizeAlignment[abi]![nativeType] ?? size[abi]!
+      };
+
+  @override
+  Constant generateConstant(FfiTransformer transformer) =>
+      TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
+
+  bool get isFloat =>
+      nativeType == NativeType.kFloat || nativeType == NativeType.kDouble;
+
+  bool isUnaligned(Map<Abi, int> offsets) {
+    final alignments = alignment;
+    for (final abi in offsets.keys) {
+      final offset = offsets[abi]!;
+      final alignment = alignments[abi]!;
+      if (offset % alignment != 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /// Sample output for `int get x =>`:
+  ///
+  /// ```
+  /// _loadInt8(_typedDataBase, offset);
+  /// ```
+  @override
+  ReturnStatement generateGetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          FfiTransformer transformer) =>
+      ReturnStatement(StaticInvocation(
+          (unalignedAccess && isFloat
+              ? transformer.loadUnalignedMethods
+              : transformer.loadMethods)[nativeType]!,
+          Arguments([
+            transformer.getCompoundTypedDataBaseField(
+                ThisExpression(), fileOffset),
+            transformer.runtimeBranchOnLayout(offsets)
+          ]))
+        ..fileOffset = fileOffset);
+
+  /// Sample output for `set x(int #v) =>`:
+  ///
+  /// ```
+  /// _storeInt8(_typedDataBase, offset, #v);
+  /// ```
+  @override
+  ReturnStatement generateSetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          VariableDeclaration argument,
+          FfiTransformer transformer) =>
+      ReturnStatement(StaticInvocation(
+          (unalignedAccess && isFloat
+              ? transformer.storeUnalignedMethods
+              : transformer.storeMethods)[nativeType]!,
+          Arguments([
+            transformer.getCompoundTypedDataBaseField(
+                ThisExpression(), fileOffset),
+            transformer.runtimeBranchOnLayout(offsets),
+            VariableGet(argument)
+          ]))
+        ..fileOffset = fileOffset);
+}
+
+class PointerNativeTypeCfe implements NativeTypeCfe {
+  @override
+  Map<Abi, int> get size => wordSize;
+
+  @override
+  Map<Abi, int> get alignment => wordSize;
+
+  @override
+  Constant generateConstant(FfiTransformer transformer) => TypeLiteralConstant(
+          InterfaceType(transformer.pointerClass, Nullability.nonNullable, [
+        InterfaceType(
+            transformer.pointerClass.superclass!, Nullability.nonNullable)
+      ]));
+
+  /// Sample output for `Pointer<Int8> get x =>`:
+  ///
+  /// ```
+  /// _fromAddress<Int8>(_loadIntPtr(_typedDataBase, offset));
+  /// ```
+  @override
+  ReturnStatement generateGetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          FfiTransformer transformer) =>
+      ReturnStatement(StaticInvocation(
+          transformer.fromAddressInternal,
+          Arguments([
+            StaticInvocation(
+                transformer.loadMethods[NativeType.kIntptr]!,
+                Arguments([
+                  transformer.getCompoundTypedDataBaseField(
+                      ThisExpression(), fileOffset),
+                  transformer.runtimeBranchOnLayout(offsets)
+                ]))
+              ..fileOffset = fileOffset
+          ], types: [
+            (dartType as InterfaceType).typeArguments.single
+          ]))
+        ..fileOffset = fileOffset);
+
+  /// Sample output for `set x(Pointer<Int8> #v) =>`:
+  ///
+  /// ```
+  /// _storeIntPtr(_typedDataBase, offset, (#v as Pointer<Int8>).address);
+  /// ```
+  @override
+  ReturnStatement generateSetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          VariableDeclaration argument,
+          FfiTransformer transformer) =>
+      ReturnStatement(StaticInvocation(
+          transformer.storeMethods[NativeType.kIntptr]!,
+          Arguments([
+            transformer.getCompoundTypedDataBaseField(
+                ThisExpression(), fileOffset),
+            transformer.runtimeBranchOnLayout(offsets),
+            InstanceGet(InstanceAccessKind.Instance, VariableGet(argument),
+                transformer.addressGetter.name,
+                interfaceTarget: transformer.addressGetter,
+                resultType: transformer.addressGetter.getterType)
+              ..fileOffset = fileOffset
+          ]))
+        ..fileOffset = fileOffset);
+}
+
+/// The layout of a `Struct` or `Union` in one [Abi].
+class CompoundLayout {
+  /// Size of the entire struct or union.
+  final int size;
+
+  /// Alignment of struct or union when nested in a struct.
+  final int alignment;
+
+  /// Offset in bytes for each field, indexed by field number.
+  ///
+  /// Always 0 for unions.
+  final List<int> offsets;
+
+  CompoundLayout(this.size, this.alignment, this.offsets);
+}
+
+abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
+  final Class clazz;
+
+  final List<NativeTypeCfe> members;
+
+  final Map<Abi, CompoundLayout> layout;
+
+  CompoundNativeTypeCfe._(this.clazz, this.members, this.layout);
+
+  @override
+  Map<Abi, int> get size =>
+      layout.map((abi, layout) => MapEntry(abi, layout.size));
+
+  @override
+  Map<Abi, int> get alignment =>
+      layout.map((abi, layout) => MapEntry(abi, layout.alignment));
+
+  @override
+  Constant generateConstant(FfiTransformer transformer) =>
+      TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
+
+  /// Sample output for `MyStruct get x =>`:
+  ///
+  /// ```
+  /// MyStruct.#fromTypedDataBase(
+  ///   typedDataBaseOffset(_typedDataBase, offset, size, dartType)
+  /// );
+  /// ```
+  @override
+  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
+      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
+    final constructor = clazz.constructors
+        .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
+
+    return ReturnStatement(ConstructorInvocation(
+        constructor,
+        Arguments([
+          transformer.typedDataBaseOffset(
+              transformer.getCompoundTypedDataBaseField(
+                  ThisExpression(), fileOffset),
+              transformer.runtimeBranchOnLayout(offsets),
+              transformer.runtimeBranchOnLayout(size),
+              dartType,
+              fileOffset)
+        ]))
+      ..fileOffset = fileOffset);
+  }
+
+  /// Sample output for `set x(MyStruct #v) =>`:
+  ///
+  /// ```
+  /// _memCopy(_typedDataBase, offset, #v._typedDataBase, 0, size);
+  /// ```
+  @override
+  ReturnStatement generateSetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          VariableDeclaration argument,
+          FfiTransformer transformer) =>
+      ReturnStatement(StaticInvocation(
+          transformer.memCopy,
+          Arguments([
+            transformer.getCompoundTypedDataBaseField(
+                ThisExpression(), fileOffset),
+            transformer.runtimeBranchOnLayout(offsets),
+            transformer.getCompoundTypedDataBaseField(
+                VariableGet(argument), fileOffset),
+            ConstantExpression(IntConstant(0)),
+            transformer.runtimeBranchOnLayout(size),
+          ]))
+        ..fileOffset = fileOffset);
+}
+
+class StructNativeTypeCfe extends CompoundNativeTypeCfe {
+  // Nullable int.
+  final int? packing;
+
+  factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members,
+      {int? packing}) {
+    final layout = {
+      for (var abi in Abi.values) abi: _calculateLayout(members, packing, abi)
+    };
+    return StructNativeTypeCfe._(clazz, members, packing, layout);
+  }
+
+  StructNativeTypeCfe._(Class clazz, List<NativeTypeCfe> members, this.packing,
+      Map<Abi, CompoundLayout> layout)
+      : super._(clazz, members, layout);
+
+  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
+  // NativeStructType::FromNativeTypes.
+  static CompoundLayout _calculateLayout(
+      List<NativeTypeCfe> types, int? packing, Abi abi) {
+    int offset = 0;
+    final offsets = <int>[];
+    int structAlignment = 1;
+    for (int i = 0; i < types.length; i++) {
+      final int size = types[i].size[abi]!;
+      int alignment = types[i].alignment[abi]!;
+      if (packing != null && packing < alignment) {
+        alignment = packing;
+      }
+      if (alignment > 0) {
+        offset = _alignOffset(offset, alignment);
+      }
+      offsets.add(offset);
+      offset += size;
+      structAlignment = math.max(structAlignment, alignment);
+    }
+    final int size = _alignOffset(offset, structAlignment);
+    return CompoundLayout(size, structAlignment, offsets);
+  }
+}
+
+class UnionNativeTypeCfe extends CompoundNativeTypeCfe {
+  factory UnionNativeTypeCfe(Class clazz, List<NativeTypeCfe> members) {
+    final layout = {
+      for (var abi in Abi.values) abi: _calculateLayout(members, abi)
+    };
+    return UnionNativeTypeCfe._(clazz, members, layout);
+  }
+
+  UnionNativeTypeCfe._(
+      Class clazz, List<NativeTypeCfe> members, Map<Abi, CompoundLayout> layout)
+      : super._(clazz, members, layout);
+
+  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
+  // NativeUnionType::FromNativeTypes.
+  static CompoundLayout _calculateLayout(List<NativeTypeCfe> types, Abi abi) {
+    int unionSize = 1;
+    int unionAlignment = 1;
+    for (int i = 0; i < types.length; i++) {
+      final int size = types[i].size[abi]!;
+      int alignment = types[i].alignment[abi]!;
+      unionSize = math.max(unionSize, size);
+      unionAlignment = math.max(unionAlignment, alignment);
+    }
+    final int size = _alignOffset(unionSize, unionAlignment);
+    return CompoundLayout(size, unionAlignment, List.filled(types.length, 0));
+  }
+}
+
+class ArrayNativeTypeCfe implements NativeTypeCfe {
+  final NativeTypeCfe elementType;
+  final int length;
+
+  ArrayNativeTypeCfe(this.elementType, this.length);
+
+  factory ArrayNativeTypeCfe.multi(
+      NativeTypeCfe elementType, List<int> dimensions) {
+    if (dimensions.length == 1) {
+      return ArrayNativeTypeCfe(elementType, dimensions.single);
+    }
+    return ArrayNativeTypeCfe(
+        ArrayNativeTypeCfe.multi(elementType, dimensions.sublist(1)),
+        dimensions.first);
+  }
+
+  List<int> get dimensions {
+    final elementType = this.elementType;
+    if (elementType is ArrayNativeTypeCfe) {
+      return [length, ...elementType.dimensions];
+    }
+    return [length];
+  }
+
+  List<int> get nestedDimensions => dimensions.sublist(1);
+
+  int get dimensionsFlattened =>
+      dimensions.fold(1, (accumulator, element) => accumulator * element);
+
+  NativeTypeCfe get singleElementType {
+    final elementType = this.elementType;
+    if (elementType is ArrayNativeTypeCfe) {
+      return elementType.singleElementType;
+    }
+    return elementType;
+  }
+
+  @override
+  Map<Abi, int> get size =>
+      elementType.size.map((abi, size) => MapEntry(abi, size * length));
+
+  @override
+  Map<Abi, int> get alignment => elementType.alignment;
+
+  // Note that we flatten multi dimensional arrays.
+  @override
+  Constant generateConstant(FfiTransformer transformer) =>
+      InstanceConstant(transformer.ffiInlineArrayClass.reference, [], {
+        transformer.ffiInlineArrayElementTypeField.fieldReference:
+            singleElementType.generateConstant(transformer),
+        transformer.ffiInlineArrayLengthField.fieldReference:
+            IntConstant(dimensionsFlattened)
+      });
+
+  /// Sample output for `Array<Int8> get x =>`:
+  ///
+  /// ```
+  /// Array<Int8>._(
+  ///   typedDataBaseOffset(_typedDataBase, offset, size, typeArgument)
+  /// );
+  /// ```
+  @override
+  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
+      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
+    InterfaceType typeArgument =
+        (dartType as InterfaceType).typeArguments.single as InterfaceType;
+    return ReturnStatement(ConstructorInvocation(
+        transformer.arrayConstructor,
+        Arguments([
+          transformer.typedDataBaseOffset(
+              transformer.getCompoundTypedDataBaseField(
+                  ThisExpression(), fileOffset),
+              transformer.runtimeBranchOnLayout(offsets),
+              transformer.runtimeBranchOnLayout(size),
+              typeArgument,
+              fileOffset),
+          ConstantExpression(IntConstant(length)),
+          transformer.intListConstantExpression(nestedDimensions)
+        ], types: [
+          typeArgument
+        ]))
+      ..fileOffset = fileOffset);
+  }
+
+  /// Sample output for `set x(Array #v) =>`:
+  ///
+  /// ```
+  /// _memCopy(_typedDataBase, offset, #v._typedDataBase, 0, size);
+  /// ```
+  @override
+  ReturnStatement generateSetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          VariableDeclaration argument,
+          FfiTransformer transformer) =>
+      ReturnStatement(StaticInvocation(
+          transformer.memCopy,
+          Arguments([
+            transformer.getCompoundTypedDataBaseField(
+                ThisExpression(), fileOffset),
+            transformer.runtimeBranchOnLayout(offsets),
+            transformer.getArrayTypedDataBaseField(
+                VariableGet(argument), fileOffset),
+            ConstantExpression(IntConstant(0)),
+            transformer.runtimeBranchOnLayout(size),
+          ]))
+        ..fileOffset = fileOffset);
+}
+
+int _alignOffset(int offset, int alignment) =>
+    ((offset + alignment - 1) ~/ alignment) * alignment;