From 6de2588d56220c93e212c9674712e1f01548d5f9 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 11 Jun 2026 07:25:59 -0700 Subject: [PATCH] [vm/ffi] Migrate legacy multi-test files to Process.run Migrates vmspecific_enable_ffi_test.dart and abi_specific_int_incomplete_aot_test.dart away from legacy multi-test markers. Since these tests verify VM/gen_snapshot compile-time errors (not CFE errors), they cannot use // [cfe] expectations. Instead, they now spawn a subprocess and verify the expected error is produced. - vmspecific_enable_ffi_test: Runs helper with --enable-ffi=false, checks for error - abi_specific_int_incomplete_aot_test: Compiles helper with `dart compile aot-snapshot`, checks for error Both tests self-spawn AOT tooling (gen_snapshot) or the JIT `dart` CLI, so they only run on the dartkp host bots and are skipped elsewhere (ia32, android, fuchsia, qemu) via tests/ffi/ffi.status. Fixes https://github.com/dart-lang/sdk/issues/60212 Change-Id: Ib2482c3172b20a4366f984da33544fc515b540c4 Cq-Include-Trybots: dart/try:vm-aot-android-debug-arm64c-try,vm-aot-android-debug-arm_x64-try,vm-aot-linux-debug-arm64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-debug-arm64-try,vm-aot-mac-debug-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-asan-mac-release-arm64-try,vm-asan-win-release-x64-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-tsan-mac-release-arm64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-ubsan-mac-release-arm64-try,vm-ubsan-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508020 Commit-Queue: Samuel Rawlins Auto-Submit: Ankit Ranjan Reviewed-by: Daco Harkes Reviewed-by: Samuel Rawlins --- .../abi_specific_int_incomplete_aot_test.dart | 95 ++++++++++++++++--- ...ecific_int_incomplete_aot_test_helper.dart | 24 +++++ tests/ffi/ffi.status | 20 ++-- tests/ffi/vmspecific_enable_ffi_test.dart | 37 ++++++-- .../vmspecific_enable_ffi_test_helper.dart | 16 ++++ 5 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 tests/ffi/abi_specific_int_incomplete_aot_test_helper.dart create mode 100644 tests/ffi/vmspecific_enable_ffi_test_helper.dart diff --git a/tests/ffi/abi_specific_int_incomplete_aot_test.dart b/tests/ffi/abi_specific_int_incomplete_aot_test.dart index e2b14ccb2e7..4d7997cab7a 100644 --- a/tests/ffi/abi_specific_int_incomplete_aot_test.dart +++ b/tests/ffi/abi_specific_int_incomplete_aot_test.dart @@ -1,26 +1,91 @@ // Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// +// Tests that using an AbiSpecificInteger with an incomplete mapping produces +// a compile-time error during AOT compilation. +// +// Uses pkg/vm/tool/gen_kernel and the build directory's gen_snapshot rather +// than `dart compile aot-snapshot`, because the test bots do not build the +// full SDK (gen_kernel_aot.dart.snapshot). -// Formatting can break multitests, so don't format them. -// dart format off +import 'dart:io'; -// SharedObjects=ffi_test_functions +import 'package:expect/expect.dart'; +import 'package:path/path.dart' as path; -import 'dart:ffi'; +final _execSuffix = Platform.isWindows ? '.exe' : ''; +final _batSuffix = Platform.isWindows ? '.bat' : ''; -// We want at least 1 mapping to satisfy the static checks. -const notTestingOn = Abi.fuchsiaArm64; - -@AbiSpecificIntegerMapping({ - notTestingOn: Int8(), -}) -final class Incomplete extends AbiSpecificInteger { - const Incomplete(); +String _findGenSnapshot(String buildDir) { + final possiblePaths = [ + // No cross compilation. + path.join(buildDir, 'gen_snapshot$_execSuffix'), + // ${MODE}SIMARM_X64 for X64->SIMARM cross compilation. + path.join('${buildDir}_X64', 'gen_snapshot$_execSuffix'), + // ${MODE}XARM64/clang_x64 for X64->ARM64 cross compilation. + path.join(buildDir, 'clang_x64', 'gen_snapshot$_execSuffix'), + ]; + for (final p in possiblePaths) { + if (File(p).existsSync()) { + return p; + } + } + throw 'Could not find gen_snapshot for build directory $buildDir'; } void main() { - // Any use that causes the class to be used, causes a compile-time error - // during loading of the class. - nullptr.cast(); //# 1: compile-time error + final buildDir = path.dirname(Platform.executable); + final sdkDir = path.dirname(path.dirname(buildDir)); + final genKernel = path.join( + sdkDir, + 'pkg', + 'vm', + 'tool', + 'gen_kernel$_batSuffix', + ); + final genSnapshot = _findGenSnapshot(buildDir); + final platformDill = path.join(buildDir, 'vm_platform.dill'); + final helperPath = path.join( + sdkDir, + 'tests', + 'ffi', + 'abi_specific_int_incomplete_aot_test_helper.dart', + ); + + final tempDir = Directory.systemTemp.createTempSync('abi_incomplete_aot'); + try { + final dillPath = path.join(tempDir.path, 'out.dill'); + + // Compiling the helper to kernel should succeed; the incomplete ABI + // mapping is only detected by the VM precompiler. + final kernelResult = Process.runSync(genKernel, [ + '--aot', + '--platform=$platformDill', + '-o', + dillPath, + helperPath, + ]); + Expect.equals( + 0, + kernelResult.exitCode, + 'gen_kernel failed: ${kernelResult.stdout}\n${kernelResult.stderr}', + ); + + // AOT compiling the kernel file should fail with a compile-time error + // about the incomplete ABI mapping. + final snapshotResult = Process.runSync(genSnapshot, [ + '--snapshot-kind=app-aot-elf', + '--elf=${path.join(tempDir.path, 'out.so')}', + dillPath, + ]); + Expect.notEquals(0, snapshotResult.exitCode); + final stderr = snapshotResult.stderr.toString(); + Expect.isTrue( + stderr.contains("AbiSpecificInteger 'Incomplete' is missing mapping for"), + 'Expected error about missing ABI mapping, got: $stderr', + ); + } finally { + tempDir.deleteSync(recursive: true); + } } diff --git a/tests/ffi/abi_specific_int_incomplete_aot_test_helper.dart b/tests/ffi/abi_specific_int_incomplete_aot_test_helper.dart new file mode 100644 index 00000000000..27286f8e28d --- /dev/null +++ b/tests/ffi/abi_specific_int_incomplete_aot_test_helper.dart @@ -0,0 +1,24 @@ +// Copyright (c) 2026, 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. +// +// Helper for abi_specific_int_incomplete_aot_test.dart. +// This file has an AbiSpecificInteger with an incomplete mapping. +// During AOT compilation for any ABI other than fuchsiaArm64, gen_snapshot +// should fail with an error about the missing mapping. + +import 'dart:ffi'; + +// We want at least 1 mapping to satisfy the static checks. +const notTestingOn = Abi.fuchsiaArm64; + +@AbiSpecificIntegerMapping({notTestingOn: Int8()}) +final class Incomplete extends AbiSpecificInteger { + const Incomplete(); +} + +void main() { + // Any use that causes the class to be used, causes a compile-time error + // during loading of the class. + nullptr.cast(); +} diff --git a/tests/ffi/ffi.status b/tests/ffi/ffi.status index bedd031b544..be8696ec05f 100644 --- a/tests/ffi/ffi.status +++ b/tests/ffi/ffi.status @@ -12,6 +12,9 @@ function_structs_by_value_generated_ret_leaf_test: Pass, Slow # https://dartbug. function_structs_by_value_generated_ret_test: Pass, Slow # https://dartbug.com/47303 https://dartbug.com/45007 native_assets/asset_*: Pass, Slow # https://dartbug.com/56330 +[ $arch == ia32 ] +abi_specific_int_incomplete_aot_test: SkipByDesign # No AOT mode on IA32. + [ $arch == simarm64_arm64 ] function_structs_by_value_generated_args_native_test: Pass, Slow @@ -41,6 +44,10 @@ native_assets/asset_*: SkipByDesign # Only intended to run from source. https:// native_assets/infer_native_assets_yaml_*: SkipByDesign # Only intended to run from source. https://dartbug.com/51265 vmspecific_leaf_call_test: Skip # https://dartbug.com/46125: Snapshot fails to generate. +[ $compiler != dartkp ] +abi_specific_int_incomplete_aot_test: SkipByDesign # Self-spawns gen_kernel/gen_snapshot, which are only built on AOT (dartkp) bots. +vmspecific_enable_ffi_test: SkipByDesign # Self-spawns the JIT dart executable, so the harness compiler is irrelevant; runs once on the dartkp bots. + [ $mode == debug ] function_callbacks_structs_by_value_generated_test: Pass, Slow @@ -49,10 +56,13 @@ regress_47594_test: Skip # Profiler is not available in Product. [ $system == android ] *: Pass, Slow # https://github.com/dart-lang/sdk/issues/38489 +abi_specific_int_incomplete_aot_test: Skip # Needs gen_kernel and gen_snapshot. regress_47594_test: Skip # DartDev is not available on Android. +vmspecific_enable_ffi_test: Skip # Needs access to Dart executable. vmspecific_native_finalizer_isolate_groups_test: Skip # SpawnUri not available on Android tester. [ $system == fuchsia ] +abi_specific_int_incomplete_aot_test: SkipByDesign # Needs gen_kernel and gen_snapshot. async_void_function_callbacks_test/*: Skip # Test harness doesn't support multitest with Fuchsia exceptional_return_const_test/*: Skip # Test harness doesn't support multitest with Fuchsia ffi_induce_a_crash_test/*: Skip # Test harness doesn't support multitest with Fuchsia @@ -72,7 +82,7 @@ run_isolate_group_run_test: Skip # gen_snapshot requires experimental-shared-dat static_checks/*: SkipByDesign # Expecting compile time failures in multi tests doesn't work on the Fuchsia test runner. threading_test: Skip # gen_snapshot requires experimental-shared-data flag unaligned_test/*: Skip # Test harness doesn't support multitest with Fuchsia -vmspecific_enable_ffi_test/*: Skip # Test harness doesn't support multitest with Fuchsia +vmspecific_enable_ffi_test: SkipByDesign # Needs access to Dart executable. vmspecific_function_callbacks_exit_test/*: Skip # Test harness doesn't support multitest with Fuchsia vmspecific_highmem_32bit_test/*: Skip # Test harness doesn't support multitest with Fuchsia vmspecific_leaf_call_test/*: Skip # Test harness doesn't support multitest with Fuchsia @@ -85,8 +95,10 @@ regress_47594_test: Skip # DynamicLibrary.process() is not available on Windows. ffi_induce_a_crash_test: SkipByDesign [ $qemu ] +abi_specific_int_incomplete_aot_test: SkipByDesign # Needs gen_kernel and gen_snapshot. function_callbacks_leaf_test: SkipByDesign # Needs access to Dart executable native_assets/*: SkipByDesign # Only intended to run on host oses with AOT binaries available, not available on QEMU. +vmspecific_enable_ffi_test: SkipByDesign # Needs access to Dart executable. [ $arch != simarm64_arm64 && $simulator ] *: Skip # FFI not yet supported on the arm simulator. @@ -103,9 +115,6 @@ many_listener_callbacks_test: Pass, Slow [ $compiler == app_jitk || $sanitizer == asan ] vmspecific_function_callbacks_exit_test: SkipByDesign -[ $compiler == dart2analyzer || $compiler == fasta ] -vmspecific_enable_ffi_test: SkipByDesign # This is a check for VM only. - [ $compiler == dartk || $system == android ] native_assets/asset_*: SkipByDesign # Only intended to run on host oses with AOT binaries available. @@ -115,9 +124,6 @@ native_assets/infer_native_assets_yaml_*: SkipByDesign # Only intended to run fr [ $compiler == dartkp || $arch == arm64 && $system == fuchsia ] abi_specific_int_incomplete_jit_test: SkipByDesign # Only intended to run in JIT mode. -[ $compiler != dartkp || $arch == arm64 && $system == fuchsia ] -abi_specific_int_incomplete_aot_test: SkipByDesign # Only intended to run in AOT mode. - [ $runtime == dart_precompiled || $runtime == vm ] callback_unwind_error_test: Crash # https://github.com/dart-lang/sdk/issues/39487 diff --git a/tests/ffi/vmspecific_enable_ffi_test.dart b/tests/ffi/vmspecific_enable_ffi_test.dart index 14716a21e10..5d504893778 100644 --- a/tests/ffi/vmspecific_enable_ffi_test.dart +++ b/tests/ffi/vmspecific_enable_ffi_test.dart @@ -3,19 +3,36 @@ // BSD-style license that can be found in the LICENSE file. // // Dart test program for testing the --enable-ffi=false flag. -// -// VMOptions=--enable-ffi=false -// Formatting can break multitests, so don't format them. -// dart format off +import 'dart:io'; -import 'dart:ffi'; //# 01: compile-time error +import 'package:expect/expect.dart'; +import 'package:path/path.dart' as path; -import 'package:ffi/ffi.dart'; //# 01: compile-time error +final _execSuffix = Platform.isWindows ? '.exe' : ''; void main() { - Pointer p = //# 01: compile-time error - calloc(); //# 01: compile-time error - print(p.address); //# 01: compile-time error - calloc.free(p); //# 01: compile-time error + final buildDir = path.dirname(Platform.executable); + final sdkDir = path.dirname(path.dirname(buildDir)); + // Use the JIT `dart` executable from the build directory rather than + // `Platform.executable`, which is the AOT runtime under dartkp and cannot run + // the source helper. + final dartExecutable = path.join(buildDir, 'dart$_execSuffix'); + final helperPath = path.join( + sdkDir, + 'tests', + 'ffi', + 'vmspecific_enable_ffi_test_helper.dart', + ); + + final result = Process.runSync(dartExecutable, [ + '--enable-ffi=false', + helperPath, + ]); + + Expect.equals(254, result.exitCode); + Expect.contains( + 'import of dart:ffi is not supported in the current Dart runtime', + result.stderr, + ); } diff --git a/tests/ffi/vmspecific_enable_ffi_test_helper.dart b/tests/ffi/vmspecific_enable_ffi_test_helper.dart new file mode 100644 index 00000000000..eaf1384b394 --- /dev/null +++ b/tests/ffi/vmspecific_enable_ffi_test_helper.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2026, 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. +// +// Helper for vmspecific_enable_ffi_test.dart. +// This file imports dart:ffi and should fail to load when --enable-ffi=false. + +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; + +void main() { + Pointer p = calloc(); + print(p.address); + calloc.free(p); +}