mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
[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:
parent
f9327d3bac
commit
b0155a72a7
|
@ -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" ]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
267
runtime/bin/ffi_test/ffi_test_functions_special.cc
Normal file
267
runtime/bin/ffi_test/ffi_test_functions_special.cc
Normal 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
|
|
@ -8,8 +8,6 @@
|
|||
// SharedObjects=ffi_test_functions
|
||||
// VMOptions=--deterministic --optimization-counter-threshold=50
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import "package:ffi/ffi.dart";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
//
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
//
|
||||
// SharedObjects=ffi_test_dynamic_library ffi_test_functions
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:ffi';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
//
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'dylib_utils.dart';
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
//
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
|
221
tests/ffi/prepare_flutter_bundle.dart
Normal file
221
tests/ffi/prepare_flutter_bundle.dart
Normal 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;
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
//
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'dylib_utils.dart';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
//
|
||||
// SharedObjects=ffi_test_dynamic_library
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import "package:ffi/ffi.dart";
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
//
|
||||
// VMOptions=--deterministic --optimization-counter-threshold=50 --enable-inlining-annotations
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
|
Loading…
Reference in a new issue