[vm/ffi/test] Replaces FfiNative transform test with expect.

We can't rely on GC to trigger the finalizer of a given test object,
so the previous liveness test was unreliable.

Instead we add an expect test to verify we generate the necessary
`reachabilityFence(..)`s to ensure liveness.

TEST=Adds expect.
Bug: https://github.com/dart-lang/sdk/issues/47362
Change-Id: Ia57a07522c8b8265b24780f00f3339b50534eb60
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-mac-product-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/215542
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/pkg/vm/lib/transformations/ffi_native.dart b/pkg/vm/lib/transformations/ffi_native.dart
index 0752ed2..3427f28 100644
--- a/pkg/vm/lib/transformations/ffi_native.dart
+++ b/pkg/vm/lib/transformations/ffi_native.dart
@@ -103,9 +103,9 @@
         node.function.computeThisFunctionType(Nullability.nonNullable);
     // Double Function(Double)
     final nativeType = annotationConst.typeArguments[0] as FunctionType;
-    // InterfaceType(NativeFunction<Double Function(Double)>*)
-    final DartType nativeInterfaceType =
-        InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeType]);
+    // InterfaceType(NativeFunction<Double Function(Double)>)
+    final DartType nativeInterfaceType = InterfaceType(
+        nativeFunctionClass, Nullability.nonNullable, [nativeType]);
 
     // Derive number of arguments from the native function signature.
     final args_n = nativeType.positionalParameters.length;
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index a90f1ec..2dc593a 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -398,7 +398,7 @@
                   env.isSubtypeOf(ffiParams[i], pointerType,
                       SubtypeCheckMode.ignoringNullabilities)) {
                 // final NativeFieldWrapperClass1 #t1 = MyNFWC1();.
-                final tmpPtr = VariableDeclaration('',
+                final tmpPtr = VariableDeclaration(null,
                     initializer: origArgs[i],
                     type: nativeFieldWrapperClassType,
                     isFinal: true);
@@ -419,7 +419,7 @@
               }
               // Note: We also evaluate, and assign temporaries for, non-wrapped
               // arguments as we need to preserve the original evaluation order.
-              final tmpArg = VariableDeclaration('',
+              final tmpArg = VariableDeclaration(null,
                   initializer: origArgs[i], isFinal: true);
               tmpsArgs.add(tmpArg);
               callArgs.add(VariableGet(tmpArg));
@@ -434,7 +434,7 @@
             //   reachabilityFence(#t1);
             // } => #t0
             final tmpResult =
-                VariableDeclaration('', type: target.function.returnType);
+                VariableDeclaration(null, type: target.function.returnType);
             return BlockExpression(
               Block([
                 tmpResult,
diff --git a/pkg/vm/test/transformations/ffinative_test.dart b/pkg/vm/test/transformations/ffinative_test.dart
new file mode 100644
index 0000000..76eba47
--- /dev/null
+++ b/pkg/vm/test/transformations/ffinative_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/kernel.dart';
+import 'package:kernel/reference_from_index.dart';
+import 'package:kernel/target/targets.dart';
+import 'package:kernel/verifier.dart';
+
+import 'package:test/test.dart';
+
+import 'package:vm/transformations/ffi_native.dart' show transformLibraries;
+
+import '../common_test_utils.dart';
+
+final String pkgVmDir = Platform.script.resolve('../..').toFilePath();
+
+class TestDiagnosticReporter extends DiagnosticReporter<Object, Object> {
+  @override
+  void report(Object message, int charOffset, int length, Uri? fileUri,
+      {List<Object>? context}) {/* nop */}
+}
+
+runTestCase(Uri source) async {
+  final target = TestingVmTarget(TargetFlags());
+
+  Component component = await compileTestCaseToKernelProgram(source,
+      target: target, experimentalFlags: ['generic-metadata']);
+
+  final ReferenceFromIndex? referenceFromIndex = null;
+  final DiagnosticReporter diagnosticReporter = TestDiagnosticReporter();
+
+  transformLibraries(
+      component, component.libraries, diagnosticReporter, referenceFromIndex);
+
+  verifyComponent(component);
+
+  final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
+
+  compareResultWithExpectationsFile(source, actual);
+}
+
+main() {
+  group('ffi-transformations', () {
+    final testCasesDir = Directory(pkgVmDir + '/testcases/transformations/ffi');
+
+    for (var entry in testCasesDir
+        .listSync(recursive: true, followLinks: false)
+        .reversed) {
+      if (entry.path.endsWith(".dart")) {
+        test(entry.path, () => runTestCase(entry.uri));
+      }
+    }
+  });
+}
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart b/pkg/vm/testcases/transformations/ffi/ffinative.dart
new file mode 100644
index 0000000..5cb2397
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2019, 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.
+
+// Tests for @FfiNative related transformations.
+
+// @dart=2.14
+
+import 'dart:ffi';
+import 'dart:nativewrappers';
+
+@FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr')
+external int returnIntPtr(int x);
+
+@FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr', isLeaf: true)
+external int returnIntPtrLeaf(int x);
+
+class Classy {
+  @FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr')
+  external static int returnIntPtrStatic(int x);
+}
+
+void main() {
+  returnIntPtr(13);
+  returnIntPtrLeaf(37);
+  Classy.returnIntPtrStatic(0xDE);
+}
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
new file mode 100644
index 0000000..09cb43b
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
@@ -0,0 +1,55 @@
+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";
+import "dart:nativewrappers";
+
+class Classy extends core::Object {
+  static final field (core::int) → core::int _@FfiNative_returnIntPtrStatic = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+  synthetic constructor •() → self::Classy
+    : super core::Object::•()
+    ;
+  @#C5
+  static method returnIntPtrStatic(core::int x) → core::int
+    return self::Classy::_@FfiNative_returnIntPtrStatic(x){(core::int) → core::int};
+}
+static final field (core::int) → core::int _@FfiNative_returnIntPtr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(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 final field (core::int) → core::int _@FfiNative_returnIntPtrLeaf = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
+@#C5
+static method returnIntPtr(core::int x) → core::int
+  return self::_@FfiNative_returnIntPtr(x){(core::int) → core::int};
+@#C7
+static method returnIntPtrLeaf(core::int x) → core::int
+  return self::_@FfiNative_returnIntPtrLeaf(x){(core::int) → core::int};
+static method main() → void {
+  block {
+    core::int #t1;
+    final dynamic #t2 = 13;
+    #t1 = self::returnIntPtr(#t2);
+    _in::reachabilityFence(#t2);
+  } =>#t1;
+  block {
+    core::int #t3;
+    final dynamic #t4 = 37;
+    #t3 = self::returnIntPtrLeaf(#t4);
+    _in::reachabilityFence(#t4);
+  } =>#t3;
+  block {
+    core::int #t5;
+    final dynamic #t6 = 222;
+    #t5 = self::Classy::returnIntPtrStatic(#t6);
+    _in::reachabilityFence(#t6);
+  } =>#t5;
+}
+constants  {
+  #C1 = "#lib"
+  #C2 = "ReturnIntPtr"
+  #C3 = 1
+  #C4 = false
+  #C5 = ffi::FfiNative<(ffi::IntPtr*) →* ffi::IntPtr*> {nativeName:#C2, isLeaf:#C4}
+  #C6 = true
+  #C7 = ffi::FfiNative<(ffi::IntPtr*) →* ffi::IntPtr*> {nativeName:#C2, isLeaf:#C6}
+}
diff --git a/tests/ffi/vmspecific_ffi_native_test.dart b/tests/ffi/vmspecific_ffi_native_test.dart
index 8838ebc..9caa53d 100644
--- a/tests/ffi/vmspecific_ffi_native_test.dart
+++ b/tests/ffi/vmspecific_ffi_native_test.dart
@@ -23,9 +23,6 @@
     nativeLib.lookupFunction<Void Function(Handle), void Function(Object)>(
         'SetFfiNativeResolverForTest');
 
-final triggerGC = nativeLib
-    .lookupFunction<Void Function(IntPtr), void Function(int)>('TriggerGC');
-
 @FfiNative<Handle Function(Handle, IntPtr, IntPtr)>(
     'Dart_SetNativeInstanceField')
 external Object setNativeInstanceField(Object obj, int index, int ptr);
@@ -71,38 +68,6 @@
 @FfiNative<IntPtr Function(IntPtr, Pointer<Void>)>('PassAsValueAndPointer')
 external int passAsValueAndPointer(int value, NativeFieldWrapperClass1 obj);
 
-// Allocate new native resource we can use to keep track of whether the
-// finalizer has run.
-@FfiNative<Pointer<Void> Function(IntPtr)>('AllocateResource')
-external Pointer<Void> allocateResource(int value);
-
-@FfiNative<Void Function(Pointer<Void>)>('DeleteResource')
-external void deleteResource(Pointer<Void> resource);
-
-// Set up the object's finalizer to reset the resource.
-@FfiNative<Void Function(Handle, Pointer<Void>)>('SetResourceFinalizer')
-external void setResourceFinalizer(
-    NativeFieldWrapperClass1 obj, Pointer<Void> resource);
-
-// Return the native resource's value.
-@FfiNative<IntPtr Function(Pointer<Void>)>('GetResourceValue')
-external int getResourceValue(Pointer<Void> resource);
-
-// Class which ties itself to a resource, resetting the value of the resource
-// when the instance gets collected.
-class ResourceResetter extends NativeFieldWrapperClass1 {
-  ResourceResetter(Pointer<Void> resource) {
-    setNativeInstanceField(this, 0, 0);
-    setResourceFinalizer(this, resource);
-  }
-}
-
-// Helper to embed triggerGC(..) as an expression.
-int triggerGCWrap() {
-  triggerGC(0);
-  return 0;
-}
-
 // Helpers for testing argumnent evaluation order is preserved.
 int state = 0;
 int setState(int value) {
@@ -141,22 +106,6 @@
     Expect.equals(123456, passAsPointer(cwnf));
   }
 
-  // Test that the transform to wrap NativeFieldWrapperClass1 objects in
-  // _getNativeField(..) doesn't violate the original argument's liveness.
-  final resource = allocateResource(314159);
-  Expect.equals(
-      314159,
-      passAsPointerAndValue(
-          // 1: Locally alloc. instance.
-          // If this gets wrapped in another call the instance does not live
-          // past the return of the wrapper call.
-          ResourceResetter(resource),
-          // 2: Force GC, to collect the above if it isn't being kept alive.
-          // 3: Check that the underlying (dummy) resource hasn't been
-          // "collected" (i.e. reset to 0).
-          triggerGCWrap() + getResourceValue(resource)));
-  deleteResource(resource);
-
   // Test that the order of argument evaluation is being preserved through the
   // transform wrapping NativeFieldWrapperClass1 objects.
   state = 0;