[vm/ffi] Don't tree-shake `Finalizable` members.
This CL prevents treeshaking of members that have type `Finalizable`, `Future<Finalizable>` or `FutureOr<Finalizable>`.
All `Finalizable` members are kept. Even the ones which are not
members of `Finalizable`s. This is in line with the logic to keep
all `Finalizable` arguments/variables alive.
Moreover, this CL adds AOT tests to the FFI transformer tests so that
we catch differences between AOT/JIT in the future.
Closes: https://github.com/dart-lang/sdk/issues/49643
TEST=pkg/vm/test/transformations/ffi_test.dart
TEST=pkg/vm/testcases/transformations/ffi/finalizable_member.dart
Change-Id: I14003314b9f23692fee30d1c3eef1bdcd27ed1ec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254904
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/transformations/ffi/finalizable.dart b/pkg/vm/lib/transformations/ffi/finalizable.dart
index 4fe96f3..b59786b 100644
--- a/pkg/vm/lib/transformations/ffi/finalizable.dart
+++ b/pkg/vm/lib/transformations/ffi/finalizable.dart
@@ -411,41 +411,17 @@
return newStatement;
}
- /// Cache for [isFinalizable].
+ /// Cache for [_isFinalizable].
///
/// Speeds up the type checks by about a factor of 2 on Flutter Gallery.
Map<DartType, bool> _isFinalizableCache = {};
/// Whether [type] is something that subtypes `FutureOr<Finalizable?>?`.
- bool _isFinalizable(DartType type) {
- final cached = _isFinalizableCache[type];
- if (cached != null) {
- return cached;
- }
-
- final finalizableType = FutureOrType(
- InterfaceType(finalizableClass, Nullability.nullable),
- Nullability.nullable);
- if (!env.isSubtypeOf(
- type,
- finalizableType,
- SubtypeCheckMode.withNullabilities,
- )) {
- _isFinalizableCache[type] = false;
- return false;
- }
-
- // Exclude never types.
- final futureOfNeverType =
- FutureOrType(NeverType.nullable(), Nullability.nullable);
- final result = !env.isSubtypeOf(
- type,
- futureOfNeverType,
- SubtypeCheckMode.ignoringNullabilities,
- );
- _isFinalizableCache[type] = result;
- return result;
- }
+ bool _isFinalizable(DartType type) => type.isFinalizable(
+ finalizableClass: finalizableClass,
+ typeEnvironment: env,
+ cache: _isFinalizableCache,
+ );
bool _thisIsFinalizableFromMember(Member member) {
final enclosingClass_ = member.enclosingClass;
@@ -896,3 +872,41 @@
return false;
}
}
+
+extension FinalizableDartType on DartType {
+ /// Whether `this` is something that subtypes `FutureOr<Finalizable?>?`.
+ bool isFinalizable({
+ required Class finalizableClass,
+ required TypeEnvironment typeEnvironment,
+ Map<DartType, bool>? cache,
+ }) {
+ final type = this;
+ final cached = cache?[type];
+ if (cached != null) {
+ return cached;
+ }
+
+ final finalizableType = FutureOrType(
+ InterfaceType(finalizableClass, Nullability.nullable),
+ Nullability.nullable);
+ if (!typeEnvironment.isSubtypeOf(
+ type,
+ finalizableType,
+ SubtypeCheckMode.withNullabilities,
+ )) {
+ cache?[type] = false;
+ return false;
+ }
+
+ // Exclude never types.
+ final futureOfNeverType =
+ FutureOrType(NeverType.nullable(), Nullability.nullable);
+ final result = !typeEnvironment.isSubtypeOf(
+ type,
+ futureOfNeverType,
+ SubtypeCheckMode.ignoringNullabilities,
+ );
+ cache?[type] = result;
+ return result;
+ }
+}
diff --git a/pkg/vm/lib/transformations/type_flow/finalizable_types.dart b/pkg/vm/lib/transformations/type_flow/finalizable_types.dart
new file mode 100644
index 0000000..6fe969c
--- /dev/null
+++ b/pkg/vm/lib/transformations/type_flow/finalizable_types.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/library_index.dart';
+import 'package:kernel/type_environment.dart';
+import 'package:vm/transformations/ffi/finalizable.dart'
+ show FinalizableDartType;
+
+/// Provides insights into `Finalizable`s.
+class FinalizableTypes {
+ final TypeEnvironment _env;
+ final Class _finalizableClass;
+
+ FinalizableTypes(
+ CoreTypes coreTypes,
+ LibraryIndex index,
+ ClassHierarchy classHierarchy,
+ ) : _env = TypeEnvironment(coreTypes, classHierarchy),
+ _finalizableClass = index.getClass('dart:ffi', 'Finalizable');
+
+ bool isFieldFinalizable(Field field) => _isFinalizable(field.type);
+
+ /// Cache for [_isFinalizable].
+ Map<DartType, bool> _isFinalizableCache = {};
+
+ /// Whether [type] is something that subtypes `FutureOr<Finalizable?>?`.
+ bool _isFinalizable(DartType type) => type.isFinalizable(
+ finalizableClass: _finalizableClass,
+ typeEnvironment: _env,
+ cache: _isFinalizableCache,
+ );
+}
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 5fc5ada..23a6fce 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -6,34 +6,35 @@
import 'dart:core' hide Type;
-import 'package:kernel/target/targets.dart';
import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
import 'package:kernel/ast.dart' as ast show Statement;
-import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
-import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClosedWorldClassHierarchy;
+import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
+import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/library_index.dart' show LibraryIndex;
+import 'package:kernel/target/targets.dart';
import 'package:kernel/type_environment.dart';
-import 'analysis.dart';
-import 'calls.dart';
-import 'signature_shaking.dart';
-import 'protobuf_handler.dart' show ProtobufHandler;
-import 'rta.dart' show RapidTypeAnalysis;
-import 'summary.dart';
-import 'table_selector_assigner.dart';
-import 'types.dart';
-import 'unboxing_info.dart';
-import 'utils.dart';
-import '../pragma.dart';
-import '../devirtualization.dart' show Devirtualization;
import '../../metadata/direct_call.dart';
import '../../metadata/inferred_type.dart';
import '../../metadata/procedure_attributes.dart';
import '../../metadata/table_selector.dart';
import '../../metadata/unboxing_info.dart';
import '../../metadata/unreachable.dart';
+import '../devirtualization.dart' show Devirtualization;
+import '../pragma.dart';
+import 'analysis.dart';
+import 'calls.dart';
+import 'finalizable_types.dart';
+import 'protobuf_handler.dart' show ProtobufHandler;
+import 'rta.dart' show RapidTypeAnalysis;
+import 'signature_shaking.dart';
+import 'summary.dart';
+import 'table_selector_assigner.dart';
+import 'types.dart';
+import 'unboxing_info.dart';
+import 'utils.dart';
const bool kDumpClassHierarchy =
const bool.fromEnvironment('global.type.flow.dump.class.hierarchy');
@@ -118,7 +119,8 @@
final transformsStopWatch = new Stopwatch()..start();
- final treeShaker = new TreeShaker(component, typeFlowAnalysis,
+ final treeShaker = new TreeShaker(
+ component, typeFlowAnalysis, coreTypes, hierarchy,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
treeShaker.transformComponent(component);
@@ -711,14 +713,21 @@
final Set<Member> _usedMembers = new Set<Member>();
final Set<Extension> _usedExtensions = new Set<Extension>();
final Set<Typedef> _usedTypedefs = new Set<Typedef>();
+ final FinalizableTypes _finalizableTypes;
late final FieldMorpher fieldMorpher;
late final _TreeShakerTypeVisitor typeVisitor;
late final _TreeShakerConstantVisitor constantVisitor;
late final _TreeShakerPass1 _pass1;
late final _TreeShakerPass2 _pass2;
- TreeShaker(Component component, this.typeFlowAnalysis,
- {this.treeShakeWriteOnlyFields: true}) {
+ TreeShaker(
+ Component component,
+ this.typeFlowAnalysis,
+ CoreTypes coreTypes,
+ ClassHierarchy hierarchy, {
+ this.treeShakeWriteOnlyFields: true,
+ }) : _finalizableTypes = new FinalizableTypes(
+ coreTypes, typeFlowAnalysis.libraryIndex, hierarchy) {
fieldMorpher = new FieldMorpher(this);
typeVisitor = new _TreeShakerTypeVisitor(this);
constantVisitor = new _TreeShakerConstantVisitor(this, typeVisitor);
@@ -747,6 +756,7 @@
bool isFieldSetterReachable(Field f) => typeFlowAnalysis.isFieldSetterUsed(f);
bool isMemberReferencedFromNativeCode(Member m) =>
typeFlowAnalysis.nativeCodeOracle.isMemberReferencedFromNativeCode(m);
+ bool isFieldFinalizable(Field f) => _finalizableTypes.isFieldFinalizable(f);
bool isTypedefUsed(Typedef t) => _usedTypedefs.contains(t);
bool retainField(Field f) =>
@@ -757,7 +767,8 @@
f.initializer != null &&
isFieldInitializerReachable(f) &&
mayHaveSideEffects(f.initializer!)) ||
- (f.isLate && f.isFinal)) ||
+ (f.isLate && f.isFinal) ||
+ isFieldFinalizable(f)) ||
isMemberReferencedFromNativeCode(f) ||
_isInstanceFieldOfAllocatedEnum(f);
diff --git a/pkg/vm/test/common_test_utils.dart b/pkg/vm/test/common_test_utils.dart
index b657777..cadd34c 100644
--- a/pkg/vm/test/common_test_utils.dart
+++ b/pkg/vm/test/common_test_utils.dart
@@ -154,8 +154,13 @@
i < expectedLines.length ? expectedLines[i] : '<END>');
}
-void compareResultWithExpectationsFile(Uri source, String actual) {
- final expectFile = new File(source.toFilePath() + '.expect');
+void compareResultWithExpectationsFile(
+ Uri source,
+ String actual, {
+ String expectFilePostfix = '',
+}) {
+ final expectFile =
+ new File('${source.toFilePath()}$expectFilePostfix.expect');
final expected = expectFile.existsSync() ? expectFile.readAsStringSync() : '';
if (actual != expected) {
diff --git a/pkg/vm/test/transformations/ffi_test.dart b/pkg/vm/test/transformations/ffi_test.dart
index 3f6dd75..a9fd860 100644
--- a/pkg/vm/test/transformations/ffi_test.dart
+++ b/pkg/vm/test/transformations/ffi_test.dart
@@ -10,9 +10,9 @@
import 'package:kernel/kernel.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart';
-
import 'package:test/test.dart';
-
+import 'package:vm/kernel_front_end.dart'
+ show runGlobalTransformations, ErrorDetector;
import 'package:vm/transformations/ffi/native.dart' show transformLibraries;
import '../common_test_utils.dart';
@@ -25,7 +25,7 @@
{List<Object>? context}) {/* nop */}
}
-runTestCase(Uri source) async {
+runTestCaseJit(Uri source) async {
final target = TestingVmTarget(TargetFlags());
Component component = await compileTestCaseToKernelProgram(source,
@@ -48,6 +48,33 @@
compareResultWithExpectationsFile(source, actual);
}
+runTestCaseAot(Uri source) async {
+ final target = TestingVmTarget(TargetFlags(supportMirrors: false));
+
+ Component component = await compileTestCaseToKernelProgram(source,
+ target: target, experimentalFlags: ['generic-metadata']);
+
+ const bool useGlobalTypeFlowAnalysis = true;
+ const bool enableAsserts = false;
+ const bool useProtobufAwareTreeShakerV2 = true;
+ final nopErrorDetector = ErrorDetector();
+ runGlobalTransformations(
+ target,
+ component,
+ useGlobalTypeFlowAnalysis,
+ enableAsserts,
+ useProtobufAwareTreeShakerV2,
+ nopErrorDetector,
+ treeShakeWriteOnlyFields: true,
+ );
+
+ verifyComponent(component);
+
+ final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
+
+ compareResultWithExpectationsFile(source, actual, expectFilePostfix: '.aot');
+}
+
void main(List<String> args) {
assert(args.isEmpty || args.length == 1);
String? filter;
@@ -63,7 +90,8 @@
.reversed) {
if (entry.path.endsWith(".dart") &&
(filter == null || entry.path.contains(filter))) {
- test(entry.path, () => runTestCase(entry.uri));
+ test(entry.path, () => runTestCaseJit(entry.uri));
+ test('${entry.path} aot', () => runTestCaseAot(entry.uri));
}
}
});
diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect
new file mode 100644
index 0000000..877dc32
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect
@@ -0,0 +1,129 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+@#C8
+abstract class WChar extends ffi::AbiSpecificInteger /*hasConstConstructor*/ {
+[@vm.unboxing-info.metadata=()->i] @#C11
+ static get #sizeOf() → core::int*
+ return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C20
+class WCharStruct extends ffi::Struct {
+ constructor #fromTypedDataBase([@vm.inferred-type.metadata=dart.ffi::Pointer] core::Object #typedDataBase) → self::WCharStruct
+ : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] get a0() → core::int
+ return [@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificInt<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=(i)->b] set a0([@vm.inferred-type.metadata=dart.core::_Smi] core::int #externalFieldValue) → void
+ return [@vm.inferred-type.metadata=int?] ffi::_storeAbiSpecificInt<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
+[@vm.unboxing-info.metadata=()->i] @#C11
+ static get #sizeOf() → core::int*
+ return #C24.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+@#C29
+class WCharArrayStruct extends ffi::Struct {
+ constructor #fromTypedDataBase([@vm.inferred-type.metadata=dart.ffi::Pointer] core::Object #typedDataBase) → self::WCharArrayStruct
+ : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get a0() → ffi::Array<self::WChar>
+ return new ffi::Array::_<self::WChar>( block {
+ core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object};
+ core::int #offset = #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<self::WChar>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::Pointer.address] [@vm.inferred-type.metadata=int?] #typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in throw "Attempt to execute code removed by Dart AOT compiler (TFA)", #C25, #C30);
+[@vm.unboxing-info.metadata=()->i] @#C11
+ static get #sizeOf() → core::int*
+ return #C34.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+}
+class _DummyAllocator extends core::Object implements ffi::Allocator /*hasConstConstructor*/ {
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:4,getterSelectorId:5] [@vm.unboxing-info.metadata=(i)->b] method allocate<T extends ffi::NativeType>([@vm.inferred-type.metadata=int] core::int byteCount) → ffi::Pointer<self::_DummyAllocator::allocate::T> {
+ return [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer::fromAddress<self::_DummyAllocator::allocate::T>(0);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6,getterSelectorId:7] method free([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer<ffi::NativeType> pointer) → void {}
+}
+static method main() → void {
+ self::testSizeOf();
+ self::testStoreLoad();
+ self::testStoreLoadIndexed();
+ self::testStruct();
+ self::testInlineArray();
+}
+static method testSizeOf() → void {
+ final core::int size = [@vm.inferred-type.metadata=dart.core::_Smi] self::WChar::#sizeOf;
+ core::print(size);
+}
+static method testStoreLoad() → void {
+ final ffi::Pointer<self::WChar> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C35.{ffi::Allocator::allocate}<self::WChar>([@vm.inferred-type.metadata=dart.core::_Smi] self::WChar::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
+ ffi::_storeAbiSpecificInt<self::WChar>(p, #C21, 10);
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificInt<self::WChar>(p, #C21));
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C35.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+static method testStoreLoadIndexed() → void {
+ final ffi::Pointer<self::WChar> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C35.{ffi::Allocator::allocate}<self::WChar>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] 2.{core::num::*}([@vm.inferred-type.metadata=dart.core::_Smi] self::WChar::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
+ ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, 0, 10);
+ ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, 1, 3);
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, 0));
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, 1));
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C35.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+static method testStruct() → void {
+ final ffi::Pointer<self::WCharStruct> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C35.{ffi::Allocator::allocate}<self::WCharStruct>([@vm.inferred-type.metadata=dart.core::_Smi] self::WCharStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharStruct>};
+ [@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=!? (skip check)] new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0} = 1;
+ core::print([@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=int?] new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0}{core::int});
+ [@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=!? (skip check)] new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0} = 2;
+ core::print([@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=int?] new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0}{core::int});
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C35.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+static method testInlineArray() → void {
+ final ffi::Pointer<self::WCharArrayStruct> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C35.{ffi::Allocator::allocate}<self::WCharArrayStruct>([@vm.inferred-type.metadata=dart.core::_Smi] self::WCharArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharArrayStruct>};
+ final ffi::Array<self::WChar> array = [@vm.direct-call.metadata=#lib::WCharArrayStruct.a0] [@vm.inferred-type.metadata=dart.ffi::Array<#lib::WChar>] new self::WCharArrayStruct::#fromTypedDataBase(p!).{self::WCharArrayStruct::a0}{ffi::Array<self::WChar>};
+ for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
+ ffi::_storeAbiSpecificIntAtIndex<self::WChar>([@vm.direct-call.metadata=dart.ffi::Array._typedDataBase] array.{ffi::Array::_typedDataBase}{core::Object}, i, i);
+ }
+ for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificIntAtIndex<self::WChar>([@vm.direct-call.metadata=dart.ffi::Array._typedDataBase] array.{ffi::Array::_typedDataBase}{core::Object}, i));
+ }
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C35.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+constants {
+ #C1 = "vm:ffi:abi-specific-mapping"
+ #C2 = TypeLiteralConstant(ffi::Uint32)
+ #C3 = TypeLiteralConstant(ffi::Uint64)
+ #C4 = TypeLiteralConstant(ffi::Int32)
+ #C5 = TypeLiteralConstant(ffi::Uint16)
+ #C6 = <core::Type?>[#C2, #C2, #C2, #C2, #C3, #C2, #C2, #C2, #C2, #C2, #C2, #C4, #C4, #C4, #C4, #C2, #C2, #C5, #C5, #C5]
+ #C7 = ffi::_FfiAbiSpecificMapping {nativeTypes:#C6}
+ #C8 = core::pragma {name:#C1, options:#C7}
+ #C9 = "vm:prefer-inline"
+ #C10 = null
+ #C11 = core::pragma {name:#C9, options:#C10}
+ #C12 = 4
+ #C13 = 8
+ #C14 = 2
+ #C15 = <core::int*>[#C12, #C12, #C12, #C12, #C13, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C14, #C14, #C14]
+ #C16 = "vm:ffi:struct-fields"
+ #C17 = TypeLiteralConstant(self::WChar)
+ #C18 = <core::Type>[#C17, #C17]
+ #C19 = ffi::_FfiStructLayout {fieldTypes:#C18, packing:#C10}
+ #C20 = core::pragma {name:#C16, options:#C19}
+ #C21 = 0
+ #C22 = <core::int*>[#C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21]
+ #C23 = 16
+ #C24 = <core::int*>[#C13, #C13, #C13, #C13, #C23, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C12, #C12, #C12]
+ #C25 = 100
+ #C26 = ffi::_FfiInlineArray {elementType:#C17, length:#C25}
+ #C27 = <core::Type>[#C26]
+ #C28 = ffi::_FfiStructLayout {fieldTypes:#C27, packing:#C10}
+ #C29 = core::pragma {name:#C16, options:#C28}
+ #C30 = <core::int*>[]
+ #C31 = 400
+ #C32 = 800
+ #C33 = 200
+ #C34 = <core::int*>[#C31, #C31, #C31, #C31, #C32, #C31, #C31, #C31, #C31, #C31, #C31, #C31, #C31, #C31, #C31, #C31, #C31, #C33, #C33, #C33]
+ #C35 = self::_DummyAllocator {}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect
new file mode 100644
index 0000000..0359b64
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect
@@ -0,0 +1,122 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+@#C6
+abstract class Incomplete extends ffi::AbiSpecificInteger /*hasConstConstructor*/ {
+[@vm.unboxing-info.metadata=()->i] @#C8
+ static get #sizeOf() → core::int*
+ return [@vm.inferred-type.metadata=dart.core::_Smi (value: 4)] ffi::_checkAbiSpecificIntegerMapping<core::int>(#C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+}
+@#C15
+class IncompleteStruct extends ffi::Struct {
+ constructor #fromTypedDataBase([@vm.inferred-type.metadata=dart.ffi::Pointer] core::Object #typedDataBase) → self::IncompleteStruct
+ : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] get a0() → core::int
+ return [@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificInt<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=(i)->b] set a0([@vm.inferred-type.metadata=dart.core::_Smi] core::int #externalFieldValue) → void
+ return [@vm.inferred-type.metadata=int?] ffi::_storeAbiSpecificInt<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
+[@vm.unboxing-info.metadata=()->i] @#C8
+ static get #sizeOf() → core::int*
+ return [@vm.inferred-type.metadata=dart.core::_Smi (value: 8)] ffi::_checkAbiSpecificIntegerMapping<core::int>(#C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+}
+@#C24
+class IncompleteArrayStruct extends ffi::Struct {
+ constructor #fromTypedDataBase([@vm.inferred-type.metadata=dart.ffi::Pointer] core::Object #typedDataBase) → self::IncompleteArrayStruct
+ : super ffi::Struct::_fromTypedDataBase(#typedDataBase)
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get a0() → ffi::Array<self::Incomplete>
+ return new ffi::Array::_<self::Incomplete>( block {
+ core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object};
+ core::int #offset = #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<self::Incomplete>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::Pointer.address] [@vm.inferred-type.metadata=int?] #typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in throw "Attempt to execute code removed by Dart AOT compiler (TFA)", #C20, #C25);
+[@vm.unboxing-info.metadata=()->i] @#C8
+ static get #sizeOf() → core::int*
+ return [@vm.inferred-type.metadata=dart.core::_Smi (value: 400)] ffi::_checkAbiSpecificIntegerMapping<core::int>(#C27.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
+}
+class _DummyAllocator extends core::Object implements ffi::Allocator /*hasConstConstructor*/ {
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:4,getterSelectorId:5] [@vm.unboxing-info.metadata=(i)->b] method allocate<T extends ffi::NativeType>([@vm.inferred-type.metadata=int] core::int byteCount) → ffi::Pointer<self::_DummyAllocator::allocate::T> {
+ return [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer::fromAddress<self::_DummyAllocator::allocate::T>(0);
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6,getterSelectorId:7] method free([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer<ffi::NativeType> pointer) → void {}
+}
+static method main() → void {
+ self::testSizeOf();
+ self::testStoreLoad();
+ self::testStoreLoadIndexed();
+ self::testStruct();
+ self::testInlineArray();
+}
+static method testSizeOf() → void {
+ final core::int size = [@vm.inferred-type.metadata=dart.core::_Smi (value: 4)] self::Incomplete::#sizeOf;
+ core::print(size);
+}
+static method testStoreLoad() → void {
+ final ffi::Pointer<self::Incomplete> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C28.{ffi::Allocator::allocate}<self::Incomplete>([@vm.inferred-type.metadata=dart.core::_Smi (value: 4)] self::Incomplete::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
+ ffi::_storeAbiSpecificInt<self::Incomplete>(p, #C16, 10);
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificInt<self::Incomplete>(p, #C16));
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C28.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+static method testStoreLoadIndexed() → void {
+ final ffi::Pointer<self::Incomplete> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C28.{ffi::Allocator::allocate}<self::Incomplete>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] 2.{core::num::*}([@vm.inferred-type.metadata=dart.core::_Smi (value: 4)] self::Incomplete::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
+ ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(p, 0, 10);
+ ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(p, 1, 3);
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(p, 0));
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(p, 1));
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C28.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+static method testStruct() → void {
+ final ffi::Pointer<self::IncompleteStruct> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C28.{ffi::Allocator::allocate}<self::IncompleteStruct>([@vm.inferred-type.metadata=dart.core::_Smi (value: 8)] self::IncompleteStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteStruct>};
+ [@vm.direct-call.metadata=#lib::IncompleteStruct.a0] [@vm.inferred-type.metadata=!? (skip check)] new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0} = 1;
+ core::print([@vm.direct-call.metadata=#lib::IncompleteStruct.a0] [@vm.inferred-type.metadata=int?] new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0}{core::int});
+ [@vm.direct-call.metadata=#lib::IncompleteStruct.a0] [@vm.inferred-type.metadata=!? (skip check)] new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0} = 2;
+ core::print([@vm.direct-call.metadata=#lib::IncompleteStruct.a0] [@vm.inferred-type.metadata=int?] new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0}{core::int});
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C28.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+static method testInlineArray() → void {
+ final ffi::Pointer<self::IncompleteArrayStruct> p = [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] #C28.{ffi::Allocator::allocate}<self::IncompleteArrayStruct>([@vm.inferred-type.metadata=dart.core::_Smi (value: 400)] self::IncompleteArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteArrayStruct>};
+ final ffi::Array<self::Incomplete> array = [@vm.direct-call.metadata=#lib::IncompleteArrayStruct.a0] [@vm.inferred-type.metadata=dart.ffi::Array<#lib::Incomplete>] new self::IncompleteArrayStruct::#fromTypedDataBase(p!).{self::IncompleteArrayStruct::a0}{ffi::Array<self::Incomplete>};
+ for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
+ ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::Array._typedDataBase] array.{ffi::Array::_typedDataBase}{core::Object}, i, i);
+ }
+ for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
+ core::print([@vm.inferred-type.metadata=int?] ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::Array._typedDataBase] array.{ffi::Array::_typedDataBase}{core::Object}, i));
+ }
+ [@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C28.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
+}
+constants {
+ #C1 = "vm:ffi:abi-specific-mapping"
+ #C2 = null
+ #C3 = TypeLiteralConstant(ffi::Uint32)
+ #C4 = <core::Type?>[#C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C3, #C3, #C3, #C3, #C2, #C2, #C2, #C2, #C2, #C2, #C2]
+ #C5 = ffi::_FfiAbiSpecificMapping {nativeTypes:#C4}
+ #C6 = core::pragma {name:#C1, options:#C5}
+ #C7 = "vm:prefer-inline"
+ #C8 = core::pragma {name:#C7, options:#C2}
+ #C9 = 4
+ #C10 = <core::int*>[#C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C9, #C9, #C9, #C9, #C2, #C2, #C2, #C2, #C2, #C2, #C2]
+ #C11 = "vm:ffi:struct-fields"
+ #C12 = TypeLiteralConstant(self::Incomplete)
+ #C13 = <core::Type>[#C12, #C12]
+ #C14 = ffi::_FfiStructLayout {fieldTypes:#C13, packing:#C2}
+ #C15 = core::pragma {name:#C11, options:#C14}
+ #C16 = 0
+ #C17 = <core::int*>[#C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16]
+ #C18 = 8
+ #C19 = <core::int*>[#C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C18, #C18, #C18, #C18, #C2, #C2, #C2, #C2, #C2, #C2, #C2]
+ #C20 = 100
+ #C21 = ffi::_FfiInlineArray {elementType:#C12, length:#C20}
+ #C22 = <core::Type>[#C21]
+ #C23 = ffi::_FfiStructLayout {fieldTypes:#C22, packing:#C2}
+ #C24 = core::pragma {name:#C11, options:#C23}
+ #C25 = <core::int*>[]
+ #C26 = 400
+ #C27 = <core::int*>[#C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C2, #C26, #C26, #C26, #C26, #C2, #C2, #C2, #C2, #C2, #C2, #C2]
+ #C28 = self::_DummyAllocator {}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/compound_copies.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/compound_copies.dart.aot.expect
new file mode 100644
index 0000000..e4dd62a
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/compound_copies.dart.aot.expect
@@ -0,0 +1,6 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+
+import "dart:ffi";
+
+static method main() → void {}
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect
new file mode 100644
index 0000000..b4e558d
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect
@@ -0,0 +1,121 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:nativewrappers" as nat;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+import "dart:nativewrappers";
+
+abstract class Classy extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (core::int) → core::int _returnIntPtrStatic$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ static method returnIntPtrStatic() → core::int
+ return self::Classy::_returnIntPtrStatic$FfiNative$Ptr(#C4){(core::int) → core::int};
+}
+class NativeClassy extends nat::NativeFieldWrapperClass1 {
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (ffi::Pointer<ffi::Void>, core::int) → void _goodHasReceiverPointer$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::int) → void, (ffi::Pointer<ffi::Void*>*, ffi::IntPtr*) →* ffi::Void*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::IntPtr*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (self::NativeClassy, core::int) → void _goodHasReceiverHandle$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::NativeClassy, core::int) → void, (ffi::Handle*, ffi::IntPtr*) →* ffi::Void*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::IntPtr*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle*, ffi::Pointer<ffi::Void*>*) →* ffi::Void*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::Pointer<ffi::Void*>*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle*, ffi::Handle*) →* ffi::Void*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::Handle*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void*>*, ffi::Handle*) →* ffi::Void*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::Handle*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (ffi::Pointer<ffi::Void>, core::bool) → core::Object? _meh$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → core::Object?, (ffi::Pointer<ffi::Void*>*, ffi::Bool*) →* ffi::Handle*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::Bool*) →* ffi::Handle*>*>(ffi::_ffi_resolver(#C1, #C5, #C6){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?] static final field (ffi::Pointer<ffi::Void>) → core::bool _blah$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void*>*) →* ffi::Bool*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*) →* ffi::Bool*>*>(ffi::_ffi_resolver(#C1, #C5, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ synthetic constructor •() → self::NativeClassy
+ : super nat::NativeFieldWrapperClass1::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method goodHasReceiverPointer() → void
+ return block {
+ final nat::NativeFieldWrapperClass1 #t1 = this;
+ final core::int #t2 = #C7;
+ final void #t3 = self::NativeClassy::_goodHasReceiverPointer$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
+ core::int #pointerAddress = [@vm.inferred-type.metadata=int?] nat::_getNativeField(#t1);
+ if([@vm.inferred-type.metadata=dart.core::bool] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
+ core::StateError::_throwNew(#C9);
+ else
+ ;
+ } =>#pointerAddress), #t2){(ffi::Pointer<ffi::Void>, core::int) → void};
+ _in::reachabilityFence(#t1);
+ } =>#t3;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] method goodHasReceiverHandle() → void
+ return self::NativeClassy::_goodHasReceiverHandle$FfiNative$Ptr(this, #C7){(self::NativeClassy, core::int) → void};
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] method goodHasReceiverHandleAndPtr([@vm.inferred-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
+ return block {
+ final self::NativeClassy #t4 = this;
+ final nat::NativeFieldWrapperClass1 #t5 = v;
+ final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>([@vm.inferred-type.metadata=int?] nat::_getNativeField(#t5))){(self::NativeClassy, ffi::Pointer<ffi::Void>) → void};
+ _in::reachabilityFence(#t5);
+ } =>#t6;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8] method goodHasReceiverHandleAndHandle([@vm.inferred-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
+ return self::NativeClassy::_goodHasReceiverHandleAndHandle$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void};
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10] method goodHasReceiverPtrAndHandle([@vm.inferred-type.metadata=#lib::NativeClassy] self::NativeClassy v) → void
+ return block {
+ final nat::NativeFieldWrapperClass1 #t7 = this;
+ final self::NativeClassy #t8 = v;
+ final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
+ core::int #pointerAddress = [@vm.inferred-type.metadata=int?] nat::_getNativeField(#t7);
+ if([@vm.inferred-type.metadata=dart.core::bool] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
+ core::StateError::_throwNew(#C9);
+ else
+ ;
+ } =>#pointerAddress), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void};
+ _in::reachabilityFence(#t7);
+ } =>#t9;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12] method meh() → core::String?
+ return block {
+ final nat::NativeFieldWrapperClass1 #t10 = this;
+ final core::bool #t11 = #C10;
+ final core::String? #t12 = _in::unsafeCast<core::String?>(self::NativeClassy::_meh$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
+ core::int #pointerAddress = [@vm.inferred-type.metadata=int?] nat::_getNativeField(#t10);
+ if([@vm.inferred-type.metadata=dart.core::bool] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
+ core::StateError::_throwNew(#C9);
+ else
+ ;
+ } =>#pointerAddress), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object?});
+ _in::reachabilityFence(#t10);
+ } =>#t12;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:13,getterSelectorId:14] method blah() → core::bool
+ return block {
+ final nat::NativeFieldWrapperClass1 #t13 = this;
+ final core::bool #t14 = self::NativeClassy::_blah$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>( block {
+ core::int #pointerAddress = [@vm.inferred-type.metadata=int?] nat::_getNativeField(#t13);
+ if([@vm.inferred-type.metadata=dart.core::bool] #pointerAddress.{core::Object::==}(#C8){(core::Object) → core::bool})
+ core::StateError::_throwNew(#C9);
+ else
+ ;
+ } =>#pointerAddress)){(ffi::Pointer<ffi::Void>) → core::bool};
+ _in::reachabilityFence(#t13);
+ } =>#t14;
+}
+[@vm.inferred-type.metadata=dart.core::_Closure?]static final field (core::int) → core::int _returnIntPtr$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+[@vm.inferred-type.metadata=dart.core::_Closure?]static final field (core::int) → core::int _returnIntPtrLeaf$FfiNative$Ptr = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>([@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
+static method returnIntPtr() → core::int
+ return self::_returnIntPtr$FfiNative$Ptr(#C11){(core::int) → core::int};
+static method returnIntPtrLeaf() → core::int
+ return self::_returnIntPtrLeaf$FfiNative$Ptr(#C12){(core::int) → core::int};
+static method main() → void {
+ self::returnIntPtr();
+ self::returnIntPtrLeaf();
+ self::Classy::returnIntPtrStatic();
+ [@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverPointer] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPointer}(){(core::int) → void};
+ [@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandle] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandle}(){(core::int) → void};
+ [@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandleAndPtr] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndPtr}(new self::NativeClassy::•()){(self::NativeClassy) → void};
+ [@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverHandleAndHandle] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndHandle}(new self::NativeClassy::•()){(self::NativeClassy) → void};
+ [@vm.direct-call.metadata=#lib::NativeClassy.goodHasReceiverPtrAndHandle] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPtrAndHandle}(new self::NativeClassy::•()){(self::NativeClassy) → void};
+ [@vm.direct-call.metadata=#lib::NativeClassy.meh] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::meh}(){(core::bool) → core::String?};
+ [@vm.direct-call.metadata=#lib::NativeClassy.blah] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::blah}(){() → core::bool};
+}
+constants {
+ #C1 = "#lib"
+ #C2 = "ReturnIntPtr"
+ #C3 = 1
+ #C4 = 222
+ #C5 = "doesntmatter"
+ #C6 = 2
+ #C7 = 175
+ #C8 = 0
+ #C9 = "Native field is nullptr."
+ #C10 = true
+ #C11 = 13
+ #C12 = 37
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_async.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_async.dart.aot.expect
new file mode 100644
index 0000000..0324cf7
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_async.dart.aot.expect
@@ -0,0 +1,50 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:async" as asy;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class MyFinalizable extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::MyFinalizable
+ : super core::Object::•()
+ ;
+}
+static method doSomething() → asy::Future<core::int> async /* futureValueType= core::int */
+ return 3;
+static method useFinalizableAsync([@vm.inferred-type.metadata=#lib::MyFinalizable] ffi::Finalizable finalizable) → asy::Future<core::int> async /* futureValueType= core::int */ {
+ await block {
+ final asy::Future<core::int> :expressionValueWrappedFinalizable = asy::Future::sync<core::int>(() → core::int => 6);
+ _in::reachabilityFence(finalizable);
+ } =>:expressionValueWrappedFinalizable;
+ final self::MyFinalizable finalizable2 = new self::MyFinalizable::•();
+ await block {
+ final asy::Future<core::int> :expressionValueWrappedFinalizable = asy::Future::sync<core::int>(() → core::int => 5);
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ } =>:expressionValueWrappedFinalizable;
+ final self::MyFinalizable finalizable3 = new self::MyFinalizable::•();
+ await block {
+ final asy::Future<core::int> :expressionValueWrappedFinalizable = asy::Future::sync<core::int>(() → core::int => 4);
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ } =>:expressionValueWrappedFinalizable;
+ return block {
+ final asy::Future<core::int> :expressionValueWrappedFinalizable = self::doSomething();
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ } =>:expressionValueWrappedFinalizable;
+}
+static method main() → void async /* futureValueType= void */ {
+ final self::MyFinalizable finalizable = new self::MyFinalizable::•();
+ final asy::Future<core::int> asyncResult = self::useFinalizableAsync(finalizable);
+ core::print(await block {
+ final asy::Future<core::int> :expressionValueWrappedFinalizable = asyncResult;
+ _in::reachabilityFence(finalizable);
+ } =>:expressionValueWrappedFinalizable);
+ _in::reachabilityFence(finalizable);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect
new file mode 100644
index 0000000..81e23f1
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_async_star.dart.aot.expect
@@ -0,0 +1,71 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:async" as asy;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class MyFinalizable extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::MyFinalizable
+ : super core::Object::•()
+ ;
+}
+[@vm.unboxing-info.metadata=()->i]static method doSomething() → core::int
+ return 3;
+static method useFinalizableAsyncStar([@vm.inferred-type.metadata=#lib::MyFinalizable] ffi::Finalizable finalizable) → asy::Stream<core::int> async* {
+ final self::MyFinalizable finalizable2 = new self::MyFinalizable::•();
+ yield block {
+ final core::int :expressionValueWrappedFinalizable = self::doSomething();
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ } =>:expressionValueWrappedFinalizable;
+ final self::MyFinalizable finalizable3 = new self::MyFinalizable::•();
+ await block {
+ final asy::Future<core::int> :expressionValueWrappedFinalizable = asy::Future::sync<core::int>(() → core::int => 3);
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ } =>:expressionValueWrappedFinalizable;
+ final self::MyFinalizable finalizable4 = new self::MyFinalizable::•();
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 4) {
+ {
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ _in::reachabilityFence(finalizable4);
+ return;
+ }
+ }
+ yield block {
+ final core::int :expressionValueWrappedFinalizable = 5;
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ _in::reachabilityFence(finalizable4);
+ } =>:expressionValueWrappedFinalizable;
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ _in::reachabilityFence(finalizable4);
+ _in::reachabilityFence(finalizable);
+}
+static method main() → void async /* futureValueType= void */ {
+ final self::MyFinalizable finalizable = new self::MyFinalizable::•();
+ final asy::Stream<core::int> asyncStarResult = [@vm.inferred-type.metadata=!] self::useFinalizableAsyncStar(finalizable);
+ {
+ asy::Stream<core::int> :stream = asyncStarResult;
+ asy::_StreamIterator<core::int>? :for-iterator = new asy::_StreamIterator::•<core::int>(:stream);
+ try
+ while (let dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream) in await [@vm.direct-call.metadata=dart.async::_StreamIterator.moveNext] [@vm.inferred-type.metadata=!? (skip check)] :for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}) {
+ final core::int element = [@vm.direct-call.metadata=dart.async::_StreamIterator.current] [@vm.inferred-type.metadata=int?] :for-iterator.{asy::_StreamIterator::current}{core::int};
+ {
+ core::print(element);
+ }
+ }
+ finally
+ if(!([@vm.direct-call.metadata=dart.async::_StreamIterator._subscription] :for-iterator.{asy::_StreamIterator::_subscription}{asy::StreamSubscription<core::int>?} == null))
+ await [@vm.direct-call.metadata=dart.async::_StreamIterator.cancel] [@vm.inferred-type.metadata=!? (skip check)] :for-iterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>};
+ }
+ _in::reachabilityFence(finalizable);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_extension_method.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_extension_method.dart.aot.expect
new file mode 100644
index 0000000..4c37d02
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_extension_method.dart.aot.expect
@@ -0,0 +1,39 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class Foo extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::Foo
+ : super core::Object::•()
+ ;
+}
+extension _extension#0 on ffi::Finalizable {
+ method bar = self::_extension#0|bar;
+}
+extension _extension#1 on core::Object {
+ method baz = self::_extension#1|baz;
+}
+static method main() → void {
+ final self::Foo foo = new self::Foo::•();
+ self::_extension#0|bar(foo);
+ let final core::Object #t1 = new core::Object::•() in self::_extension#1|baz(foo);
+ _in::reachabilityFence(foo);
+}
+[@vm.unboxing-info.metadata=(b)->i]static method _extension#0|bar([@vm.inferred-type.metadata=#lib::Foo] lowered final ffi::Finalizable #this) → core::int {
+ core::print("123");
+ return block {
+ final core::int :expressionValueWrappedFinalizable = 4;
+ _in::reachabilityFence(#this);
+ } =>:expressionValueWrappedFinalizable;
+}
+[@vm.unboxing-info.metadata=(b)->i]static method _extension#1|baz([@vm.inferred-type.metadata=#lib::Foo] self::Foo foo) → core::int {
+ core::print("456");
+ return block {
+ final core::int :expressionValueWrappedFinalizable = 5;
+ _in::reachabilityFence(foo);
+ } =>:expressionValueWrappedFinalizable;
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_late.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_late.dart.aot.expect
new file mode 100644
index 0000000..45321de
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_late.dart.aot.expect
@@ -0,0 +1,22 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class Foo extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::Foo
+ : super core::Object::•()
+ ;
+}
+static method main() → void {
+ late self::Foo foo;
+ foo = block {
+ final self::Foo :expressionValueWrappedFinalizable = new self::Foo::•();
+ _in::reachabilityFence(foo);
+ } =>:expressionValueWrappedFinalizable;
+ core::print(foo);
+ _in::reachabilityFence(foo);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_member.dart b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart
new file mode 100644
index 0000000..ce5e063
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.16
+
+import 'dart:ffi';
+import 'dart:io';
+
+typedef Free = NativeFunction<Void Function(Pointer)>;
+final free = DynamicLibrary.process().lookup<Free>('free');
+
+final _nativeFinalizer = NativeFinalizer(free);
+
+class A implements Finalizable {
+ A() {
+ _nativeFinalizer.attach(this, Pointer.fromAddress(1),
+ detach: this, externalSize: 1 << 32); // will crash, if it ever runs
+ }
+}
+
+class B implements Finalizable {
+ final A a;
+
+ B(this.a);
+}
+
+Future<void> main() async {
+ // ignore: unused_local_variable
+ final b = B(A()); // I would expect b.a to live as long as b
+ final l = <int>[];
+ Future.doWhile(() {
+ l.add(1); // put some pressure on GC
+ return true;
+ });
+ await ProcessSignal.sigint.watch().first;
+ // b still alive here, but what about b.a?
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect
new file mode 100644
index 0000000..aa60815
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect
@@ -0,0 +1,47 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+import "dart:async" as asy;
+import "dart:io" as io;
+
+import "dart:ffi";
+import "dart:io";
+
+class A extends core::Object implements ffi::Finalizable {
+ constructor •() → self::A
+ : super core::Object::•() {
+ let final ffi::NativeFinalizer #t1 = [@vm.inferred-type.metadata=dart.ffi::_NativeFinalizer?] self::_nativeFinalizer in let final ffi::Pointer<ffi::Void> #t2 = [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer::fromAddress<ffi::Void>(1) in let final core::int #t3 = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<<] [@vm.inferred-type.metadata=int (skip check)] 1.{core::int::<<}(32){(core::int) → core::int} in [@vm.direct-call.metadata=dart.ffi::_NativeFinalizer.attach??] [@vm.inferred-type.metadata=!? (skip check)] #t1.{ffi::NativeFinalizer::attach}(this, #t2, this, #t3){(ffi::Finalizable, ffi::Pointer<ffi::Void>, {detach: core::Object?, externalSize: core::int?}) → void};
+ _in::reachabilityFence(this);
+ }
+}
+class B extends core::Object implements ffi::Finalizable {
+[@vm.inferred-type.metadata=#lib::A] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] final field self::A a;
+ constructor •([@vm.inferred-type.metadata=#lib::A] self::A a) → self::B
+ : self::B::a = a, super core::Object::•() {
+ ;
+ _in::reachabilityFence(this);
+ _in::reachabilityFence(a);
+ }
+}
+[@vm.inferred-type.metadata=dart.ffi::Pointer?]static final field ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>> free = [@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup??] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary?] ffi::DynamicLibrary::process().{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>>("free"){(core::String) → ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>>};
+[@vm.inferred-type.metadata=dart.ffi::_NativeFinalizer?]static final field ffi::NativeFinalizer _nativeFinalizer = new ffi::_NativeFinalizer::•([@vm.inferred-type.metadata=dart.ffi::Pointer?] self::free);
+static method main() → asy::Future<void> async /* futureValueType= void */ {
+ final self::B b = new self::B::•(new self::A::•());
+ final core::List<core::int> l = [@vm.inferred-type.metadata=dart.core::_GrowableList<dart.core::int>] core::_GrowableList::•<core::int>(0);
+ asy::Future::doWhile(() → core::bool {
+ [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::int>] [@vm.direct-call.metadata=dart.core::_GrowableList.add] [@vm.inferred-type.metadata=!? (skip check)] l.{core::List::add}(1){(core::int) → void};
+ return true;
+ });
+ await block {
+ final asy::Future<io::ProcessSignal> :expressionValueWrappedFinalizable = [@vm.direct-call.metadata=dart.async::Stream.first] [@vm.direct-call.metadata=dart.io::ProcessSignal.watch] [@vm.inferred-type.metadata=dart.async::_BroadcastStream<dart.io::ProcessSignal> (skip check)] #C3.{io::ProcessSignal::watch}(){() → asy::Stream<io::ProcessSignal>}.{asy::Stream::first}{asy::Future<io::ProcessSignal>};
+ _in::reachabilityFence(b);
+ } =>:expressionValueWrappedFinalizable;
+ _in::reachabilityFence(b);
+}
+constants {
+ #C1 = 2
+ #C2 = "SIGINT"
+ #C3 = io::ProcessSignal {_signalNumber:#C1, _name:#C2}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.expect b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.expect
new file mode 100644
index 0000000..c702f9d
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.expect
@@ -0,0 +1,48 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:ffi" as ffi;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+import "dart:async" as asy;
+import "dart:io" as io;
+
+import "dart:ffi";
+import "dart:io";
+
+typedef Free = ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>;
+class A extends core::Object implements ffi::Finalizable {
+ constructor •() → self::A
+ : super core::Object::•() {
+ self::_nativeFinalizer.{ffi::NativeFinalizer::attach}(this, ffi::Pointer::fromAddress<ffi::Void>(1), detach: this, externalSize: 1.{core::int::<<}(32){(core::int) → core::int}){(ffi::Finalizable, ffi::Pointer<ffi::Void>, {detach: core::Object?, externalSize: core::int?}) → void};
+ _in::reachabilityFence(this);
+ }
+}
+class B extends core::Object implements ffi::Finalizable {
+ final field self::A a;
+ constructor •(self::A a) → self::B
+ : self::B::a = a, super core::Object::•() {
+ ;
+ _in::reachabilityFence(this);
+ _in::reachabilityFence(a);
+ }
+}
+static final field ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>> free = ffi::DynamicLibrary::process().{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>>("free"){(core::String) → ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Void>>};
+static final field ffi::NativeFinalizer _nativeFinalizer = new ffi::_NativeFinalizer::•(self::free);
+static method main() → asy::Future<void> async /* futureValueType= void */ {
+ final self::B b = new self::B::•(new self::A::•());
+ final core::List<core::int> l = core::_GrowableList::•<core::int>(0);
+ asy::Future::doWhile(() → core::bool {
+ [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::int>] l.{core::List::add}(1){(core::int) → void};
+ return true;
+ });
+ await block {
+ final asy::Future<io::ProcessSignal> :expressionValueWrappedFinalizable = #C3.{io::ProcessSignal::watch}(){() → asy::Stream<io::ProcessSignal>}.{asy::Stream::first}{asy::Future<io::ProcessSignal>};
+ _in::reachabilityFence(b);
+ } =>:expressionValueWrappedFinalizable;
+ _in::reachabilityFence(b);
+}
+constants {
+ #C1 = 2
+ #C2 = "SIGINT"
+ #C3 = io::ProcessSignal {_signalNumber:#C1, _name:#C2}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_sync.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_sync.dart.aot.expect
new file mode 100644
index 0000000..c7152ec
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_sync.dart.aot.expect
@@ -0,0 +1,26 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class MyFinalizable extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::MyFinalizable
+ : super core::Object::•()
+ ;
+}
+[@vm.unboxing-info.metadata=()->i]static method doSomething() → core::int
+ return 3;
+[@vm.unboxing-info.metadata=(b)->i]static method useFinalizableSync([@vm.inferred-type.metadata=#lib::MyFinalizable] ffi::Finalizable finalizable) → core::int {
+ return block {
+ final core::int :expressionValueWrappedFinalizable = [@vm.inferred-type.metadata=dart.core::_Smi (value: 3)] self::doSomething();
+ _in::reachabilityFence(finalizable);
+ } =>:expressionValueWrappedFinalizable;
+}
+static method main() → void {
+ final self::MyFinalizable finalizable = new self::MyFinalizable::•();
+ core::print([@vm.inferred-type.metadata=dart.core::_Smi (value: 3)] self::useFinalizableSync(finalizable));
+ _in::reachabilityFence(finalizable);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect
new file mode 100644
index 0000000..27fc81f
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_sync2.dart.aot.expect
@@ -0,0 +1,233 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class MyFinalizable extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::MyFinalizable
+ : super core::Object::•()
+ ;
+}
+static method main() → void {
+ final self::MyFinalizable finalizable = new self::MyFinalizable::•();
+ {
+ final self::MyFinalizable finalizable2 = new self::MyFinalizable::•();
+ _in::reachabilityFence(finalizable2);
+ }
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 42) {
+ {
+ _in::reachabilityFence(finalizable);
+ return;
+ }
+ }
+ else {
+ try
+ try {
+ final self::MyFinalizable finalizable3 = new self::MyFinalizable::•();
+ {}
+ _in::reachabilityFence(finalizable3);
+ }
+ on core::Exception catch(no-exception-var) {
+ final self::MyFinalizable finalizable4 = new self::MyFinalizable::•();
+ _in::reachabilityFence(finalizable4);
+ }
+ finally {
+ final self::MyFinalizable finalizable5 = new self::MyFinalizable::•();
+ _in::reachabilityFence(finalizable5);
+ }
+ try {
+ final self::MyFinalizable finalizable13 = new self::MyFinalizable::•();
+ try
+ try {
+ final self::MyFinalizable finalizable14 = new self::MyFinalizable::•();
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 100) {
+ throw block {
+ final core::Exception :expressionValueWrappedFinalizable = core::Exception::•("foo");
+ _in::reachabilityFence(finalizable14);
+ } =>:expressionValueWrappedFinalizable;
+ }
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 101) {
+ throw block {
+ final core::Error :expressionValueWrappedFinalizable = new core::Error::•();
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable13);
+ _in::reachabilityFence(finalizable14);
+ } =>:expressionValueWrappedFinalizable;
+ }
+ _in::reachabilityFence(finalizable14);
+ }
+ on core::Exception catch(final core::Exception e) {
+ core::print(e);
+ block {
+ _in::reachabilityFence(finalizable13);
+ } =>rethrow;
+ }
+ finally {
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 1000) {
+ throw block {
+ final core::Exception :expressionValueWrappedFinalizable = core::Exception::•("bar");
+ _in::reachabilityFence(finalizable13);
+ } =>:expressionValueWrappedFinalizable;
+ }
+ }
+ _in::reachabilityFence(finalizable13);
+ }
+ on core::Exception catch(final core::Exception e) {
+ core::print(e);
+ }
+ }
+ #L1:
+ switch([@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int}) {
+ #L2:
+ case #C1:
+ case #C2:
+ {
+ final self::MyFinalizable finalizable6 = new self::MyFinalizable::•();
+ {
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable6);
+ return;
+ }
+ }
+ #L3:
+ case #C3:
+ {
+ final self::MyFinalizable finalizable7 = new self::MyFinalizable::•();
+ {
+ _in::reachabilityFence(finalizable7);
+ break #L1;
+ }
+ }
+ #L4:
+ case #C4:
+ {
+ final self::MyFinalizable finalizable70 = new self::MyFinalizable::•();
+ #L5:
+ switch([@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int}) {
+ #L6:
+ case #C5:
+ {
+ final self::MyFinalizable finalizable71 = new self::MyFinalizable::•();
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 44) {
+ {
+ _in::reachabilityFence(finalizable70);
+ _in::reachabilityFence(finalizable71);
+ continue #L4;
+ }
+ }
+ {
+ _in::reachabilityFence(finalizable71);
+ break #L5;
+ }
+ }
+ }
+ {
+ _in::reachabilityFence(finalizable70);
+ continue #L3;
+ }
+ }
+ #L7:
+ default:
+ {
+ final self::MyFinalizable finalizable8 = new self::MyFinalizable::•();
+ _in::reachabilityFence(finalizable8);
+ }
+ }
+ #L8:
+ for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(10){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int})
+ #L9:
+ {
+ final self::MyFinalizable finalizable9 = new self::MyFinalizable::•();
+ for (core::int j = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] j.{core::num::<}(10){(core::num) → core::bool}; j = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] j.{core::num::+}(1){(core::num) → core::int})
+ #L10:
+ {
+ final self::MyFinalizable finalizable10 = new self::MyFinalizable::•();
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 42) {
+ {
+ _in::reachabilityFence(finalizable9);
+ _in::reachabilityFence(finalizable10);
+ break #L8;
+ }
+ }
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 1337) {
+ {
+ _in::reachabilityFence(finalizable9);
+ _in::reachabilityFence(finalizable10);
+ break #L8;
+ }
+ }
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 1) {
+ {
+ _in::reachabilityFence(finalizable10);
+ break #L10;
+ }
+ }
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 3) {
+ {
+ _in::reachabilityFence(finalizable9);
+ _in::reachabilityFence(finalizable10);
+ break #L9;
+ }
+ }
+ _in::reachabilityFence(finalizable10);
+ }
+ _in::reachabilityFence(finalizable9);
+ }
+ #L11:
+ {
+ final self::MyFinalizable finalizable11 = new self::MyFinalizable::•();
+ #L12:
+ {
+ final self::MyFinalizable finalizable12 = new self::MyFinalizable::•();
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 1) {
+ {
+ _in::reachabilityFence(finalizable11);
+ _in::reachabilityFence(finalizable12);
+ break #L11;
+ }
+ }
+ if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::DateTime.millisecondsSinceEpoch] [@vm.inferred-type.metadata=int] new core::DateTime::now().{core::DateTime::millisecondsSinceEpoch}{core::int} =={core::num::==}{(core::Object) → core::bool} 3) {
+ {
+ _in::reachabilityFence(finalizable12);
+ break #L12;
+ }
+ }
+ _in::reachabilityFence(finalizable12);
+ }
+ _in::reachabilityFence(finalizable11);
+ }
+ for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(10){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
+ final self::MyFinalizable finalizable15 = new self::MyFinalizable::•();
+ _in::reachabilityFence(finalizable15);
+ }
+ core::int i = 0;
+ while ([@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(10){(core::num) → core::bool}) {
+ final self::MyFinalizable finalizable16 = new self::MyFinalizable::•();
+ i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int};
+ _in::reachabilityFence(finalizable16);
+ }
+ {
+ core::Iterator<ffi::Finalizable> :sync-for-iterator = [@vm.inferred-type.metadata=!] [@vm.inferred-type.metadata=!] core::Iterable::generate<ffi::Finalizable>((core::int index) → self::MyFinalizable => new self::MyFinalizable::•()).{core::Iterable::iterator}{core::Iterator<ffi::Finalizable>};
+ for (; [@vm.inferred-type.metadata=dart.core::bool] :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
+ final ffi::Finalizable finalizable17 = [@vm.inferred-type.metadata=#lib::MyFinalizable?] :sync-for-iterator.{core::Iterator::current}{ffi::Finalizable};
+ {
+ _in::reachabilityFence(finalizable17);
+ }
+ }
+ }
+ i = 0;
+ for (ffi::Finalizable finalizable18 = new self::MyFinalizable::•(); [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(10){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
+ _in::reachabilityFence(finalizable18);
+ }
+ _in::reachabilityFence(finalizable);
+}
+constants {
+ #C1 = 1
+ #C2 = 2
+ #C3 = 3
+ #C4 = 4
+ #C5 = 5
+}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_sync3.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_sync3.dart.aot.expect
new file mode 100644
index 0000000..e4dd62a
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_sync3.dart.aot.expect
@@ -0,0 +1,6 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+
+import "dart:ffi";
+
+static method main() → void {}
diff --git a/pkg/vm/testcases/transformations/ffi/finalizable_sync_star.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/finalizable_sync_star.dart.aot.expect
new file mode 100644
index 0000000..714dc60
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/finalizable_sync_star.dart.aot.expect
@@ -0,0 +1,50 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+class MyFinalizable extends core::Object implements ffi::Finalizable {
+ synthetic constructor •() → self::MyFinalizable
+ : super core::Object::•()
+ ;
+}
+[@vm.unboxing-info.metadata=()->i]static method doSomething() → core::int
+ return 3;
+static method useFinalizableSyncStar([@vm.inferred-type.metadata=#lib::MyFinalizable] ffi::Finalizable finalizable) → core::Iterable<core::int> sync* {
+ yield block {
+ final core::int :expressionValueWrappedFinalizable = self::doSomething();
+ _in::reachabilityFence(finalizable);
+ } =>:expressionValueWrappedFinalizable;
+ final self::MyFinalizable finalizable2 = new self::MyFinalizable::•();
+ yield block {
+ final core::int :expressionValueWrappedFinalizable = 5;
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ } =>:expressionValueWrappedFinalizable;
+ final self::MyFinalizable finalizable3 = new self::MyFinalizable::•();
+ yield block {
+ final core::int :expressionValueWrappedFinalizable = 10;
+ _in::reachabilityFence(finalizable);
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ } =>:expressionValueWrappedFinalizable;
+ _in::reachabilityFence(finalizable2);
+ _in::reachabilityFence(finalizable3);
+ _in::reachabilityFence(finalizable);
+}
+static method main() → void {
+ final self::MyFinalizable finalizable = new self::MyFinalizable::•();
+ {
+ core::Iterator<core::int> :sync-for-iterator = [@vm.direct-call.metadata=dart.async::_SyncStarIterable.iterator] [@vm.inferred-type.metadata=dart.async::_SyncStarIterator] [@vm.inferred-type.metadata=dart.async::_SyncStarIterable] self::useFinalizableSyncStar(finalizable).{core::Iterable::iterator}{core::Iterator<core::int>};
+ for (; [@vm.direct-call.metadata=dart.async::_SyncStarIterator.moveNext] [@vm.inferred-type.metadata=dart.core::bool? (skip check)] :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
+ final core::int element = [@vm.direct-call.metadata=dart.async::_SyncStarIterator.current] [@vm.inferred-type.metadata=int?] :sync-for-iterator.{core::Iterator::current}{core::int};
+ {
+ core::print(element);
+ }
+ }
+ }
+ _in::reachabilityFence(finalizable);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/regress_49075.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/regress_49075.dart.aot.expect
new file mode 100644
index 0000000..fb88d53
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/regress_49075.dart.aot.expect
@@ -0,0 +1,20 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:_internal" as _in;
+import "dart:async" as asy;
+
+import "dart:ffi";
+
+class MyFinalizable extends core::Object implements ffi::Finalizable {
+ constructor •() → self::MyFinalizable
+ : super core::Object::•() {
+ ;
+ _in::reachabilityFence(this);
+ }
+}
+static method main(core::List<core::String> arguments) → asy::Future<void> async /* futureValueType= void */ {
+ final self::MyFinalizable myFinalizable = await new self::MyFinalizable::•();
+ _in::reachabilityFence(myFinalizable);
+}