[vm/ffi] Add script to extract existing positive ffi tests into bundle to be used for flutter/flutter integration test

The `tests/ffi/prepare_flutter_bundle.dart` script will try to discover
all synchronous, positive test from the "ffi" test suite, rewrite them
slightly and output a directory which can be used in a flutter/flutter
FFI integration test.

We split the ffi test functions into two parts, because a subset of the
C functions will issue calls to the VM via `dart_api.h`, which is not
available for the flutter/flutter integration test.

=> We make `runtime/bin/ffi_test/ffi_test_functions.cc` a pure C
   library, usable without `dart_api.h` and move the remaining VM
   specific code to .../ffi_test_functions_special.cc contains.

All tests from `tests/ffi/*_test.dart` will be included in the generated
bundle, which

   * don't use async/isolates
   * don't rely on VM api
   * don't rely on DynamicLibrary.{process,executable}
   * don't take too long to execute

The script can be used as follows:

   sdk % dart tests/ffi/prepare_flutter_bundle.dart foo
   Using SDK root: .../sdk
   The following tests will be included:
      aliasing_test.dart
      data_not_asan_test.dart
      data_test.dart
      extension_methods_test.dart
      external_typed_data_test.dart
      function_structs_test.dart
      negative_function_test.dart
      regress_37254_test.dart
      regress_39044_test.dart
      regress_39063_test.dart
      regress_39068_test.dart
      stacktrace_regress_37910_test.dart
      structs_test.dart
      variance_function_test.dart
   The following tests were filtered due to using dart_api.h/async/DynamicLibrary.{process,executable}/...
      function_callbacks_test.dart
      function_gc_test.dart
      function_test.dart
      object_gc_test.dart
      regress_37100_test.dart
      regress_37511_callbacks_test.dart
      regress_37511_test.dart
      regress_37780_test.dart

   Please copy generated files into FFI flutter test application
      * foo/lib/src/generated
      * foo/ios/Classes

Change-Id: Ia13f97df3bbc90829bb8fde8265a7e1d2c0f8260
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/127006
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2019-12-04 13:02:48 +00:00 committed by commit-bot@chromium.org
parent f9327d3bac
commit b0155a72a7
18 changed files with 507 additions and 269 deletions

View file

@ -1122,6 +1122,7 @@ shared_library("ffi_test_functions") {
]
sources = [
"ffi_test/ffi_test_functions.cc",
"ffi_test/ffi_test_functions_special.cc",
]
if (is_win && current_cpu == "x64") {
sources += [ "ffi_test/clobber_x64_win.S" ]

View file

@ -2,7 +2,12 @@
// 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.
#include "include/dart_api.h"
#if defined(_WIN32)
#define DART_EXPORT extern "C" __declspec(dllexport)
#else
#define DART_EXPORT \
extern "C" __attribute__((visibility("default"))) __attribute((used))
#endif
DART_EXPORT int return42() {
return 42;

View file

@ -7,28 +7,17 @@
#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
#include <csignal>
#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)
#include <psapi.h>
#else
#include <unistd.h>
// Only OK to use here because this is test code.
#include <condition_variable> // NOLINT(build/c++11)
#include <functional> // NOLINT(build/c++11)
#include <mutex> // NOLINT(build/c++11)
#include <thread> // NOLINT(build/c++11)
#endif
#include <setjmp.h>
#include <signal.h>
#include <cmath>
#include <iostream>
#include <limits>
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#if defined(_WIN32)
#define DART_EXPORT extern "C" __declspec(dllexport)
#else
#define DART_EXPORT \
extern "C" __attribute__((visibility("default"))) __attribute((used))
#endif
namespace dart {
@ -108,7 +97,7 @@ DART_EXPORT intptr_t TakeMinInt16(int16_t x) {
}
DART_EXPORT intptr_t TakeMinInt32(int32_t x) {
const int64_t expected = kMinInt32;
const int64_t expected = INT32_MIN;
const int64_t received = x;
return expected == received ? 1 : 0;
}
@ -509,125 +498,6 @@ DART_EXPORT float InventFloatValue() {
return retval;
}
////////////////////////////////////////////////////////////////////////////////
// Functions for stress-testing.
DART_EXPORT int64_t MinInt64() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x8000000000000000;
}
DART_EXPORT int64_t MinInt32() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x80000000;
}
DART_EXPORT double SmallDouble() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x80000000 * -1.0;
}
// Requires boxing on 32-bit and 64-bit systems, even if the top 32-bits are
// truncated.
DART_EXPORT void* LargePointer() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
uint64_t origin = 0x8100000082000000;
return reinterpret_cast<void*>(origin);
}
DART_EXPORT void TriggerGC(uint64_t count) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
}
DART_EXPORT void CollectOnNthAllocation(intptr_t num_allocations) {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(num_allocations));
}
// Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be
// ignored by GC.
DART_EXPORT void Regress37069(uint64_t a,
uint64_t b,
uint64_t c,
uint64_t d,
uint64_t e,
uint64_t f,
uint64_t g,
uint64_t h,
uint64_t i,
uint64_t j,
uint64_t k) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
}
#if !defined(HOST_OS_WINDOWS)
DART_EXPORT void* UnprotectCodeOtherThread(void* isolate,
std::condition_variable* var,
std::mutex* mut) {
std::function<void()> callback = [&]() {
mut->lock();
var->notify_all();
mut->unlock();
// Wait for mutator thread to continue (and block) before leaving the
// safepoint.
while (Dart_ExecuteInternalCommand("is-mutator-in-native", isolate) !=
nullptr) {
usleep(10 * 1000 /*10 ms*/);
}
};
struct {
void* isolate;
std::function<void()>* callback;
} args = {.isolate = isolate, .callback = &callback};
Dart_ExecuteInternalCommand("run-in-safepoint-and-rw-code", &args);
return nullptr;
}
struct HelperThreadState {
std::mutex mutex;
std::condition_variable cvar;
std::unique_ptr<std::thread> helper;
};
DART_EXPORT void* TestUnprotectCode(void (*fn)(void*)) {
HelperThreadState* state = new HelperThreadState;
{
std::unique_lock<std::mutex> lock(state->mutex); // locks the mutex
state->helper.reset(new std::thread(UnprotectCodeOtherThread,
Dart_CurrentIsolate(), &state->cvar,
&state->mutex));
state->cvar.wait(lock);
}
if (fn != nullptr) {
fn(state);
return nullptr;
} else {
return state;
}
}
DART_EXPORT void WaitForHelper(HelperThreadState* helper) {
helper->helper->join();
delete helper;
}
#else
// Our version of VSC++ doesn't support std::thread yet.
DART_EXPORT void WaitForHelper(void* helper) {}
DART_EXPORT void* TestUnprotectCode(void (*fn)(void)) {
return nullptr;
}
#endif
////////////////////////////////////////////////////////////////////////////////
// Tests for callbacks.
@ -663,7 +533,7 @@ DART_EXPORT int TestSimpleMultiply(double (*fn)(double)) {
}
DART_EXPORT int TestSimpleMultiplyFloat(float (*fn)(float)) {
CHECK(std::abs(fn(2.0) - 2.0 * 1.337) < 0.001);
CHECK(::std::abs(fn(2.0) - 2.0 * 1.337) < 0.001);
return 0;
}
@ -741,24 +611,6 @@ DART_EXPORT int TestNullPointers(int64_t* (*fn)(int64_t* ptr)) {
return 0;
}
// Defined in ffi_test_functions.S.
//
// Clobbers some registers with special meaning in Dart before re-entry, for
// stress-testing. Not used on 32-bit Windows due to complications with Windows
// "safeseh".
#if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32)
void ClobberAndCall(void (*fn)()) {
fn();
}
#else
extern "C" void ClobberAndCall(void (*fn)());
#endif
DART_EXPORT int TestGC(void (*do_gc)()) {
ClobberAndCall(do_gc);
return 0;
}
DART_EXPORT int TestReturnVoid(int (*return_void)()) {
CHECK_EQ(return_void(), 0);
return 0;
@ -779,93 +631,6 @@ DART_EXPORT int TestThrowException(int (*fn)()) {
return 0;
}
struct CallbackTestData {
int success;
void (*callback)();
};
#if defined(TARGET_OS_LINUX)
thread_local sigjmp_buf buf;
void CallbackTestSignalHandler(int) {
siglongjmp(buf, 1);
}
int ExpectAbort(void (*fn)()) {
fprintf(stderr, "**** EXPECT STACKTRACE TO FOLLOW. THIS IS OK. ****\n");
struct sigaction old_action = {};
int result = __sigsetjmp(buf, /*savesigs=*/1);
if (result == 0) {
// Install signal handler.
struct sigaction handler = {};
handler.sa_handler = CallbackTestSignalHandler;
sigemptyset(&handler.sa_mask);
handler.sa_flags = 0;
sigaction(SIGABRT, &handler, &old_action);
fn();
} else {
// Caught the setjmp.
sigaction(SIGABRT, &old_action, NULL);
exit(0);
}
fprintf(stderr, "Expected abort!!!\n");
exit(1);
}
void* TestCallbackOnThreadOutsideIsolate(void* parameter) {
CallbackTestData* data = reinterpret_cast<CallbackTestData*>(parameter);
data->success = ExpectAbort(data->callback);
return NULL;
}
int TestCallbackOtherThreadHelper(void* (*tester)(void*), void (*fn)()) {
CallbackTestData data = {1, fn};
pthread_attr_t attr;
int result = pthread_attr_init(&attr);
CHECK_EQ(result, 0);
pthread_t tid;
result = pthread_create(&tid, &attr, tester, &data);
CHECK_EQ(result, 0);
result = pthread_attr_destroy(&attr);
CHECK_EQ(result, 0);
void* retval;
result = pthread_join(tid, &retval);
// Doesn't actually return because the other thread will exit when the test is
// finished.
return 1;
}
// Run a callback on another thread and verify that it triggers SIGABRT.
DART_EXPORT int TestCallbackWrongThread(void (*fn)()) {
return TestCallbackOtherThreadHelper(&TestCallbackOnThreadOutsideIsolate, fn);
}
// Verify that we get SIGABRT when invoking a native callback outside an
// isolate.
DART_EXPORT int TestCallbackOutsideIsolate(void (*fn)()) {
Dart_Isolate current = Dart_CurrentIsolate();
Dart_ExitIsolate();
CallbackTestData data = {1, fn};
TestCallbackOnThreadOutsideIsolate(&data);
Dart_EnterIsolate(current);
return data.success;
}
DART_EXPORT int TestCallbackWrongIsolate(void (*fn)()) {
return ExpectAbort(fn);
}
#endif // defined(TARGET_OS_LINUX)
// Receives some pointer (Pointer<NativeType> in Dart) and writes some bits.
DART_EXPORT void NativeTypePointerParam(void* p) {
uint8_t* p2 = reinterpret_cast<uint8_t*>(p);

View file

@ -0,0 +1,267 @@
// 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.
// This file contains test functions for the dart:ffi test cases.
#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
#include <csignal>
#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)
#include <psapi.h>
#else
#include <unistd.h>
// Only OK to use here because this is test code.
#include <condition_variable> // NOLINT(build/c++11)
#include <functional> // NOLINT(build/c++11)
#include <mutex> // NOLINT(build/c++11)
#include <thread> // NOLINT(build/c++11)
#endif
#include <setjmp.h>
#include <signal.h>
#include <iostream>
#include <limits>
#include "include/dart_api.h"
#include "include/dart_native_api.h"
namespace dart {
#define CHECK(X) \
if (!(X)) { \
fprintf(stderr, "%s\n", "Check failed: " #X); \
return 1; \
}
#define CHECK_EQ(X, Y) CHECK((X) == (Y))
////////////////////////////////////////////////////////////////////////////////
// Functions for stress-testing.
DART_EXPORT int64_t MinInt64() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x8000000000000000;
}
DART_EXPORT int64_t MinInt32() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x80000000;
}
DART_EXPORT double SmallDouble() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x80000000 * -1.0;
}
// Requires boxing on 32-bit and 64-bit systems, even if the top 32-bits are
// truncated.
DART_EXPORT void* LargePointer() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
uint64_t origin = 0x8100000082000000;
return reinterpret_cast<void*>(origin);
}
DART_EXPORT void TriggerGC(uint64_t count) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
}
DART_EXPORT void CollectOnNthAllocation(intptr_t num_allocations) {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(num_allocations));
}
// Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be
// ignored by GC.
DART_EXPORT void Regress37069(uint64_t a,
uint64_t b,
uint64_t c,
uint64_t d,
uint64_t e,
uint64_t f,
uint64_t g,
uint64_t h,
uint64_t i,
uint64_t j,
uint64_t k) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
}
#if !defined(HOST_OS_WINDOWS)
DART_EXPORT void* UnprotectCodeOtherThread(void* isolate,
std::condition_variable* var,
std::mutex* mut) {
std::function<void()> callback = [&]() {
mut->lock();
var->notify_all();
mut->unlock();
// Wait for mutator thread to continue (and block) before leaving the
// safepoint.
while (Dart_ExecuteInternalCommand("is-mutator-in-native", isolate) !=
nullptr) {
usleep(10 * 1000 /*10 ms*/);
}
};
struct {
void* isolate;
std::function<void()>* callback;
} args = {.isolate = isolate, .callback = &callback};
Dart_ExecuteInternalCommand("run-in-safepoint-and-rw-code", &args);
return nullptr;
}
struct HelperThreadState {
std::mutex mutex;
std::condition_variable cvar;
std::unique_ptr<std::thread> helper;
};
DART_EXPORT void* TestUnprotectCode(void (*fn)(void*)) {
HelperThreadState* state = new HelperThreadState;
{
std::unique_lock<std::mutex> lock(state->mutex); // locks the mutex
state->helper.reset(new std::thread(UnprotectCodeOtherThread,
Dart_CurrentIsolate(), &state->cvar,
&state->mutex));
state->cvar.wait(lock);
}
if (fn != nullptr) {
fn(state);
return nullptr;
} else {
return state;
}
}
DART_EXPORT void WaitForHelper(HelperThreadState* helper) {
helper->helper->join();
delete helper;
}
#else
// Our version of VSC++ doesn't support std::thread yet.
DART_EXPORT void WaitForHelper(void* helper) {}
DART_EXPORT void* TestUnprotectCode(void (*fn)(void)) {
return nullptr;
}
#endif
// Defined in ffi_test_functions.S.
//
// Clobbers some registers with special meaning in Dart before re-entry, for
// stress-testing. Not used on 32-bit Windows due to complications with Windows
// "safeseh".
#if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32)
void ClobberAndCall(void (*fn)()) {
fn();
}
#else
extern "C" void ClobberAndCall(void (*fn)());
#endif
DART_EXPORT int TestGC(void (*do_gc)()) {
ClobberAndCall(do_gc);
return 0;
}
struct CallbackTestData {
int success;
void (*callback)();
};
#if defined(TARGET_OS_LINUX)
thread_local sigjmp_buf buf;
void CallbackTestSignalHandler(int) {
siglongjmp(buf, 1);
}
int ExpectAbort(void (*fn)()) {
fprintf(stderr, "**** EXPECT STACKTRACE TO FOLLOW. THIS IS OK. ****\n");
struct sigaction old_action = {};
int result = __sigsetjmp(buf, /*savesigs=*/1);
if (result == 0) {
// Install signal handler.
struct sigaction handler = {};
handler.sa_handler = CallbackTestSignalHandler;
sigemptyset(&handler.sa_mask);
handler.sa_flags = 0;
sigaction(SIGABRT, &handler, &old_action);
fn();
} else {
// Caught the setjmp.
sigaction(SIGABRT, &old_action, NULL);
exit(0);
}
fprintf(stderr, "Expected abort!!!\n");
exit(1);
}
void* TestCallbackOnThreadOutsideIsolate(void* parameter) {
CallbackTestData* data = reinterpret_cast<CallbackTestData*>(parameter);
data->success = ExpectAbort(data->callback);
return NULL;
}
int TestCallbackOtherThreadHelper(void* (*tester)(void*), void (*fn)()) {
CallbackTestData data = {1, fn};
pthread_attr_t attr;
int result = pthread_attr_init(&attr);
CHECK_EQ(result, 0);
pthread_t tid;
result = pthread_create(&tid, &attr, tester, &data);
CHECK_EQ(result, 0);
result = pthread_attr_destroy(&attr);
CHECK_EQ(result, 0);
void* retval;
result = pthread_join(tid, &retval);
// Doesn't actually return because the other thread will exit when the test is
// finished.
return 1;
}
// Run a callback on another thread and verify that it triggers SIGABRT.
DART_EXPORT int TestCallbackWrongThread(void (*fn)()) {
return TestCallbackOtherThreadHelper(&TestCallbackOnThreadOutsideIsolate, fn);
}
// Verify that we get SIGABRT when invoking a native callback outside an
// isolate.
DART_EXPORT int TestCallbackOutsideIsolate(void (*fn)()) {
Dart_Isolate current = Dart_CurrentIsolate();
Dart_ExitIsolate();
CallbackTestData data = {1, fn};
TestCallbackOnThreadOutsideIsolate(&data);
Dart_EnterIsolate(current);
return data.success;
}
DART_EXPORT int TestCallbackWrongIsolate(void (*fn)()) {
return ExpectAbort(fn);
}
#endif // defined(TARGET_OS_LINUX)
} // namespace dart

View file

@ -8,8 +8,6 @@
// SharedObjects=ffi_test_functions
// VMOptions=--deterministic --optimization-counter-threshold=50
library FfiTest;
import 'dart:ffi';
import "package:ffi/ffi.dart";

View file

@ -7,8 +7,6 @@
// These mallocs trigger an asan alarm, so these tests are in a separate file
// which is excluded in asan mode.
library FfiTest;
import 'dart:ffi';
import "package:ffi/ffi.dart";

View file

@ -6,8 +6,6 @@
//
// SharedObjects=ffi_test_functions
library FfiTest;
import 'dart:ffi';
import "package:expect/expect.dart";

View file

@ -6,8 +6,6 @@
//
// SharedObjects=ffi_test_dynamic_library ffi_test_functions
library FfiTest;
import 'dart:io';
import 'dart:ffi';

View file

@ -17,8 +17,6 @@
// VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code --stacktrace-every=100
// SharedObjects=ffi_test_functions
library FfiTest;
import 'dart:io';
import 'dart:ffi';
import 'dart:isolate';

View file

@ -7,8 +7,6 @@
//
// SharedObjects=ffi_test_functions
library FfiTest;
import 'dart:ffi';
import 'dylib_utils.dart';

View file

@ -6,8 +6,6 @@
//
// SharedObjects=ffi_test_functions
library FfiTest;
import 'dart:ffi';
import "package:expect/expect.dart";

View file

@ -0,0 +1,221 @@
// 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:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:args/args.dart';
main(List<String> args) async {
if (args.length != 1) {
print('Usage ${Platform.executable} ${Platform.script} <output-dir>');
exit(1);
}
final sdkRoot =
path.canonicalize(path.join(Platform.script.path, '../../..'));
final flutterTestsDir = args.single;
print('Using SDK root: $sdkRoot');
final testFiles = <String>[];
final failedOrTimedOut = <String>[];
final filteredTests = <String>[];
await for (final testFile in listTestFiles(sdkRoot, filteredTests)) {
final duration = await run(sdkRoot, testFile);
if (duration != null && duration.inSeconds < 5) {
testFiles.add(testFile);
} else {
failedOrTimedOut.add(testFile);
}
}
testFiles.sort();
failedOrTimedOut.sort();
filteredTests.sort();
dumpTestList(testFiles, 'The following tests will be included:');
dumpTestList(failedOrTimedOut,
'The following tests will be excluded due to timeout or test failure:');
dumpTestList(
filteredTests,
'The following tests were filtered due to using '
'dart_api.h/async/DynamicLibrary.{process,executable}/...');
final allFiles = <String>{};
allFiles.add(path.join(sdkRoot, 'pkg/expect/lib/expect.dart'));
for (final testFile in testFiles) {
allFiles.add(testFile);
await addImportedFilesTo(allFiles, testFile);
}
await generateCleanDir(flutterTestsDir);
final dartTestsDir = path.join(flutterTestsDir, 'lib/src/generated');
await generateDartTests(dartTestsDir, allFiles, testFiles);
final ccDir = path.join(flutterTestsDir, 'ios/Classes');
await generateCLibs(sdkRoot, ccDir, allFiles, testFiles);
print('');
print('Please copy generated files into FFI flutter test application');
print(' * $dartTestsDir');
print(' * $ccDir');
}
void dumpTestList(List<String> testFiles, String message) {
if (testFiles.isEmpty) return;
print(message);
for (final testFile in testFiles) {
print(' ${path.basename(testFile)}');
}
}
final importRegExp = RegExp(r'''^import.*['"](.+)['"].*;''');
Future addImportedFilesTo(Set<String> allFiles, String testFile) async {
final content = await File(testFile).readAsString();
for (final line in content.split('\n')) {
final match = importRegExp.matchAsPrefix(line);
if (match != null) {
final filename = match.group(1);
if (!filename.contains('dart:') &&
!filename.contains('package:expect') &&
!filename.contains('package:ffi')) {
final importedFile = Uri.file(testFile).resolve(filename).toFilePath();
if (allFiles.add(importedFile)) {
addImportedFilesTo(allFiles, importedFile);
}
}
}
}
}
Future generateCLibs(String sdkRoot, String destDir, Set<String> allFiles,
List<String> testFiles) async {
final dir = await generateCleanDir(destDir);
String destinationFile;
final lib1 =
path.join(sdkRoot, 'runtime/bin/ffi_test/ffi_test_dynamic_library.cc');
destinationFile =
path.join(dir.path, path.basename(lib1)).replaceAll('.cc', '.cpp');
File(destinationFile)
.writeAsStringSync(cleanCC(File(lib1).readAsStringSync()));
final lib2 = path.join(sdkRoot, 'runtime/bin/ffi_test/ffi_test_functions.cc');
destinationFile =
path.join(dir.path, path.basename(lib2)).replaceAll('.cc', '.cpp');
File(destinationFile)
.writeAsStringSync(cleanCC(File(lib2).readAsStringSync()));
}
String cleanCC(String content) {
return content.replaceAll('DART_EXPORT', 'extern "C" ');
}
String cleanDart(String content) {
return content.replaceAll('package:expect/expect.dart', 'expect.dart');
}
Future generateDartTests(
String destDir, Set<String> allFiles, List<String> testFiles) async {
final dir = await generateCleanDir(destDir);
final sink = File(path.join(dir.path, 'all.dart')).openWrite();
for (int i = 0; i < testFiles.length; ++i) {
sink.writeln('import "${path.basename(testFiles[i])}" as main$i;');
}
sink.writeln('');
sink.writeln('invoke(fn) {');
sink.writeln(' if (fn is void Function()) {');
sink.writeln(' fn();');
sink.writeln(' } else {');
sink.writeln(' fn(<String>[]);');
sink.writeln(' }');
sink.writeln('}');
sink.writeln('');
sink.writeln('main() {');
for (int i = 0; i < testFiles.length; ++i) {
sink.writeln(' invoke(main$i.main);');
}
sink.writeln('}');
await sink.close();
for (final file in allFiles) {
File(path.join(dir.path, path.basename(file)))
.writeAsStringSync(cleanDart(File(file).readAsStringSync()));
}
File(path.join(dir.path, 'dylib_utils.dart')).writeAsStringSync('''
import 'dart:ffi' as ffi;
import 'dart:io' show Platform;
ffi.DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
return Platform.isAndroid
? ffi.DynamicLibrary.open('libffi_tests.so')
: ffi.DynamicLibrary.process();
}
''');
}
Stream<String> listTestFiles(
String sdkRoot, List<String> filteredTests) async* {
await for (final file in Directory(path.join(sdkRoot, 'tests/ffi')).list()) {
if (file is File && file.path.endsWith('_test.dart')) {
// These tests are VM specific and cannot necessarily be run on Flutter.
final blacklistedTests = const [
'function_callbacks_test.dart',
'function_callbacks_test.dart',
'function_gc_test.dart',
'function_test.dart',
'object_gc_test.dart',
'regress_37100_test.dart',
'regress_37511_callbacks_test.dart',
'regress_37511_test.dart',
'regress_37780_test.dart',
];
if (blacklistedTests.contains(path.basename(file.path))) {
filteredTests.add(file.path);
continue;
}
final contents = file.readAsStringSync();
if (!contents.contains('//#') &&
!contents.contains('dart:async') &&
!contents.contains('dart:isolate') &&
!contents.contains('async') &&
!contents.contains('Future') &&
!contents.contains('DynamicLibrary.process') &&
!contents.contains('DynamicLibrary.executable') &&
!contents.contains('Future')) {
yield file.path;
}
}
}
}
Future<Duration> run(String sdkRoot, String testFile) async {
final env = Map<String, String>.from(Platform.environment);
env['LD_LIBRARY_PATH'] = path.join(sdkRoot, 'out/ReleaseX64');
final sw = Stopwatch()..start();
final Process process = await Process.start(
Platform.executable, <String>[testFile],
environment: env);
final timer = Timer(const Duration(seconds: 3), () => process.kill());
process.stdout.listen((_) {});
process.stderr.listen((_) {});
if (await process.exitCode != 0) return null;
timer.cancel();
return sw.elapsed;
}
Future<Directory> generateCleanDir(String dirname) async {
final directory = Directory(dirname);
if (await directory.exists()) {
await directory.delete(recursive: true);
}
await directory.create(recursive: true);
return directory;
}

View file

@ -240,7 +240,6 @@ void load6() {
void main() {
// Trigger both the runtime entry and the IL in bytecode.
for (int i = 0; i < 100; i++) {
print(i);
store1();
store2();
store3();

View file

@ -11,8 +11,6 @@
// TODO(37295): Merge this file with regress_37511_test.dart when callback
// support lands.
library FfiTest;
import 'dart:ffi';
import 'ffi_test_helpers.dart';

View file

@ -8,8 +8,6 @@
//
// SharedObjects=ffi_test_functions
library FfiTest;
import 'dart:ffi';
import 'dylib_utils.dart';

View file

@ -60,10 +60,12 @@ main() {
final sumManyNumbers = ffiTestFunctions
.lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
bool gotException = false;
try {
sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0,
15, 16.0, 17, 18.0, null, 20.0);
} on Error {
print('Expected exception on passing null for int');
gotException = true;
}
Expect.isTrue(gotException);
}

View file

@ -6,8 +6,6 @@
//
// SharedObjects=ffi_test_dynamic_library
library FfiTest;
import 'dart:ffi';
import "package:ffi/ffi.dart";

View file

@ -6,8 +6,6 @@
//
// VMOptions=--deterministic --optimization-counter-threshold=50 --enable-inlining-annotations
library FfiTest;
import 'dart:ffi';
import "package:expect/expect.dart";