diff --git a/runtime/bin/ffi_test_functions.cc b/runtime/bin/ffi_test_functions.cc index 5d95e49c165..28687ca6030 100644 --- a/runtime/bin/ffi_test_functions.cc +++ b/runtime/bin/ffi_test_functions.cc @@ -4,13 +4,17 @@ // This file contains test functions for the dart:ffi test cases. -// The tests which use this don't run on Windows yet. -#if !defined(_WIN32) - #include #include #include + +#include "platform/globals.h" +#if defined(HOST_OS_WINDOWS) +#include +#else #include +#endif + #include #include @@ -406,6 +410,7 @@ DART_EXPORT void* SmallPointer() { return reinterpret_cast(-0x80000000L); } +#if !defined(_WIN32) DART_EXPORT int RedirectStderr() { char filename[256]; snprintf(filename, sizeof(filename), "/tmp/captured_stderr_%d", getpid()); @@ -413,7 +418,6 @@ DART_EXPORT int RedirectStderr() { printf("Got file %s\n", filename); return getpid(); } +#endif } // namespace dart - -#endif diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc index 4186a1e6d1c..d084216e2ca 100644 --- a/runtime/lib/ffi.cc +++ b/runtime/lib/ffi.cc @@ -500,9 +500,9 @@ static RawCode* TrampolineCode(const Function& function, #elif !defined(TARGET_ARCH_X64) // https://github.com/dart-lang/sdk/issues/35774 UNREACHABLE(); -#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) +#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) && \ + !defined(TARGET_OS_WINDOWS) // https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android - // https://github.com/dart-lang/sdk/issues/35771 Windows // https://github.com/dart-lang/sdk/issues/35772 Arm64 // https://github.com/dart-lang/sdk/issues/35773 DBC UNREACHABLE(); @@ -588,9 +588,9 @@ static void* GenerateFfiInverseTrampoline(const Function& signature, #elif !defined(TARGET_ARCH_X64) // https://github.com/dart-lang/sdk/issues/35774 UNREACHABLE(); -#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) +#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) && \ + !defined(TARGET_OS_WINDOWS) // https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android - // https://github.com/dart-lang/sdk/issues/35771 Windows // https://github.com/dart-lang/sdk/issues/35772 Arm64 // https://github.com/dart-lang/sdk/issues/35773 DBC UNREACHABLE(); diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc index 3af92da7838..af4a52e4524 100644 --- a/runtime/lib/ffi_dynamic_library.cc +++ b/runtime/lib/ffi_dynamic_library.cc @@ -3,12 +3,12 @@ // BSD-style license that can be found in the LICENSE file. #if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) -// TODO(dacoharkes): implement dynamic libraries for other targets. -// see -// - runtime/vm/native_symbol.h -// - runtime/vm/native_symbol_linux.cc -// - runtime/bin/extensions.h (but we cannot import from bin) +// TODO(dacoharkes): Implement dynamic libraries for other targets & merge the +// implementation with: +// - runtime/bin/extensions.h // - runtime/bin/extensions_linux.cc +// TODO(dacoharkes): Make the code from bin available in a manner similar to +// runtime/vm/dart.h Dart_FileReadCallback. #else #include #endif @@ -19,29 +19,85 @@ namespace dart { -DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) { -#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) - UNREACHABLE(); -#else - GET_NON_NULL_NATIVE_ARGUMENT(String, lib_path, arguments->NativeArgAt(0)); - - dlerror(); // Clear any errors. - void* handle = dlopen(lib_path.ToCString(), RTLD_LAZY); +static void* LoadExtensionLibrary(const char* library_file) { +#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) + void* handle = dlopen(library_file, RTLD_LAZY); if (handle == nullptr) { char* error = dlerror(); const String& msg = String::Handle( - String::NewFormatted("Failed to load dynamic library(%s)", error)); + String::NewFormatted("Failed to load dynamic library (%s)", error)); Exceptions::ThrowArgumentError(msg); } + return handle; +#elif defined(TARGET_OS_WINDOWS) + SetLastError(0); // Clear any errors. + + // Convert to wchar_t string. + const int name_len = + MultiByteToWideChar(CP_UTF8, 0, library_file, -1, NULL, 0); + wchar_t* name = new wchar_t[name_len]; + MultiByteToWideChar(CP_UTF8, 0, library_file, -1, name, name_len); + + void* ext = LoadLibraryW(name); + delete[] name; + + if (ext == nullptr) { + const int error = GetLastError(); + const String& msg = String::Handle( + String::NewFormatted("Failed to load dynamic library (%i)", error)); + Exceptions::ThrowArgumentError(msg); + } + + return ext; +#else + const Array& args = Array::Handle(Array::New(1)); + args.SetAt(0, + String::Handle(String::New( + "The dart:ffi library is not available on this platform."))); + Exceptions::ThrowByType(Exceptions::kUnsupported, args); +#endif +} + +DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) { + GET_NON_NULL_NATIVE_ARGUMENT(String, lib_path, arguments->NativeArgAt(0)); + + void* handle = LoadExtensionLibrary(lib_path.ToCString()); + return DynamicLibrary::New(handle); +} + +static void* ResolveSymbol(void* handle, const char* symbol) { +#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) + dlerror(); // Clear any errors. + void* pointer = dlsym(handle, symbol); + if (pointer == nullptr) { + char* error = dlerror(); + const String& msg = String::Handle( + String::NewFormatted("Failed to lookup symbol (%s)", error)); + Exceptions::ThrowArgumentError(msg); + } + return pointer; +#elif defined(TARGET_OS_WINDOWS) + SetLastError(0); + void* pointer = GetProcAddress(reinterpret_cast(handle), symbol); + if (pointer == nullptr) { + const int error = GetLastError(); + const String& msg = String::Handle( + String::NewFormatted("Failed to lookup symbol (%i)", error)); + Exceptions::ThrowArgumentError(msg); + } + return pointer; +#else + const Array& args = Array::Handle(Array::New(1)); + args.SetAt(0, + String::Handle(String::New( + "The dart:ffi library is not available on this platform."))); + Exceptions::ThrowByType(Exceptions::kUnsupported, args); #endif } DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) { -#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) - UNREACHABLE(); -#else GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0)); @@ -50,22 +106,14 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) { void* handle = dlib.GetHandle(); - dlerror(); // Clear any errors. - intptr_t pointer = - reinterpret_cast(dlsym(handle, argSymbolName.ToCString())); - char* error; - if ((error = dlerror()) != NULL) { - const String& msg = String::Handle( - String::NewFormatted("Failed to lookup symbol (%s)", error)); - Exceptions::ThrowArgumentError(msg); - } + const intptr_t pointer = reinterpret_cast( + ResolveSymbol(handle, argSymbolName.ToCString())); - // TODO(dacoharkes): should this return NULL if addres is 0? + // TODO(dacoharkes): should this return Object::null() if address is 0? // https://github.com/dart-lang/sdk/issues/35756 RawPointer* result = Pointer::New(type_arg, Integer::Handle(zone, Integer::New(pointer))); return result; -#endif } DEFINE_NATIVE_ENTRY(Ffi_dl_getHandle, 0, 1) { diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h index f8263237da1..0bdeec520c9 100644 --- a/runtime/vm/dart_api_impl.h +++ b/runtime/vm/dart_api_impl.h @@ -300,9 +300,9 @@ class Api : AllStatic { #if !defined(TARGET_ARCH_X64) // https://github.com/dart-lang/sdk/issues/35774 return false; -#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) +#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) && \ + !defined(TARGET_OS_WINDOWS) // https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android - // https://github.com/dart-lang/sdk/issues/35771 Windows // https://github.com/dart-lang/sdk/issues/35772 Arm64 // https://github.com/dart-lang/sdk/issues/35773 DBC return false; diff --git a/samples/ffi/dylib_utils.dart b/samples/ffi/dylib_utils.dart index 1929961e35f..1c924d4a838 100644 --- a/samples/ffi/dylib_utils.dart +++ b/samples/ffi/dylib_utils.dart @@ -9,6 +9,7 @@ String _platformPath(String name, {String path}) { if (path == null) path = ""; if (Platform.isLinux) return path + "lib" + name + ".so"; if (Platform.isMacOS) return path + "lib" + name + ".dylib"; + if (Platform.isWindows) return path + name + ".dll"; throw Exception("Platform not implemented"); } diff --git a/tests/standalone_2/ffi/dylib_utils.dart b/tests/standalone_2/ffi/dylib_utils.dart index 1929961e35f..1c924d4a838 100644 --- a/tests/standalone_2/ffi/dylib_utils.dart +++ b/tests/standalone_2/ffi/dylib_utils.dart @@ -9,6 +9,7 @@ String _platformPath(String name, {String path}) { if (path == null) path = ""; if (Platform.isLinux) return path + "lib" + name + ".so"; if (Platform.isMacOS) return path + "lib" + name + ".dylib"; + if (Platform.isWindows) return path + name + ".dll"; throw Exception("Platform not implemented"); } diff --git a/tests/standalone_2/ffi/function_stress_test.dart b/tests/standalone_2/ffi/function_stress_test.dart index 2f5f02c5120..46e716fb1ed 100644 --- a/tests/standalone_2/ffi/function_stress_test.dart +++ b/tests/standalone_2/ffi/function_stress_test.dart @@ -32,7 +32,7 @@ test(GCWatcher watcher, void Function() testee, } main() async { - final watcher = GCWatcher(); + final watcher = GCWatcher.ifAvailable(); try { await test(watcher, testBoxInt64); // On 64-bit platforms this won't trigger GC because the result fits into a diff --git a/tests/standalone_2/ffi/gc_helper.dart b/tests/standalone_2/ffi/gc_helper.dart index 084a0ceaaee..5e9431d2d63 100644 --- a/tests/standalone_2/ffi/gc_helper.dart +++ b/tests/standalone_2/ffi/gc_helper.dart @@ -3,25 +3,48 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:io'; -import 'dart:convert'; import 'dylib_utils.dart'; import 'dart:ffi'; +import 'dart:io' show Platform; DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); +abstract class GCWatcher { + factory GCWatcher() => _GCWatcherImpl(); + factory GCWatcher.dummy() => _MockGCWatcher(); + factory GCWatcher.ifAvailable() => + Platform.isWindows ? GCWatcher.dummy() : GCWatcher(); + + Future size(); + void dispose(); +} + // Requires --verbose-gc. -class GCWatcher { +class _GCWatcherImpl implements GCWatcher { int _suffix; Future size() async { return await File("/tmp/captured_stderr_$_suffix").length(); } - GCWatcher() { + _GCWatcherImpl() { print("Starting..."); _suffix = ffiTestFunctions .lookupFunction("RedirectStderr")(); } - dispose() => File("/tmp/captured_stderr_$_suffix").deleteSync(); + dispose() { + try { + File("/tmp/captured_stderr_$_suffix").deleteSync(); + } catch (e) { + print("deleting file failed"); + } + } +} + +class _MockGCWatcher implements GCWatcher { + int _ctr = 0; + + Future size() async => ++_ctr; + dispose() {} } diff --git a/tests/standalone_2/ffi/subtype_test.dart b/tests/standalone_2/ffi/subtype_test.dart index dd466ae675d..22dbe131a2f 100644 --- a/tests/standalone_2/ffi/subtype_test.dart +++ b/tests/standalone_2/ffi/subtype_test.dart @@ -39,7 +39,7 @@ Future testGC() async { CString cs = ffi.fromAddress(11); bar = cs; foo = ""; - final watcher = GCWatcher(); + final watcher = GCWatcher.ifAvailable(); int counts = await watcher.size(); for (int i = 0; i < 1000000; ++i) { foo = new X(i); diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status index a68bdd79a5a..31ebd3db2ab 100644 --- a/tests/standalone_2/standalone_2_kernel.status +++ b/tests/standalone_2/standalone_2_kernel.status @@ -229,8 +229,8 @@ io/socket_many_connections_test: Skip # Timeout io/web_socket_compression_test: Skip # Timeout io/web_socket_test: Skip # Timeout -[ $arch != x64 || $compiler != dartk || $mode == product || $system != linux && $system != macos] -ffi/function_stress_test: SkipByDesign # FFI must be supported. Also requires --verbose-gc, which isn't included in product. +[ $arch != x64 || $compiler != dartk || $mode == product || $system != linux && $system != macos ] +ffi/function_stress_test: SkipByDesign # FFI must be supported. Also requires --verbose-gc, which isn't included in product. Windows issues will be fixed in IL CL: https://github.com/dart-lang/sdk/issues/36138 ffi/subtype_test: SkipByDesign # FFI must be supported. Also requires --verbose-gc, which isn't included in product. [ $mode == product || $mode != product ] diff --git a/tests/standalone_2/standalone_2_vm.status b/tests/standalone_2/standalone_2_vm.status index 49b61dc272a..1cb8be7b552 100644 --- a/tests/standalone_2/standalone_2_vm.status +++ b/tests/standalone_2/standalone_2_vm.status @@ -133,5 +133,5 @@ full_coverage_test: Skip # TODO(vegorov) SIMDBC interpreter doesn't support cove link_natives_lazily_test: SkipByDesign # SIMDBC interpreter doesn't support lazy linking of natives. no_lazy_dispatchers_test: SkipByDesign # SIMDBC interpreter doesn't support --no_lazy_dispatchers -[ $arch != x64 || $system != linux && $system != macos ] +[ $arch != x64 || $system != linux && $system != macos && $system != windows ] ffi: Skip # ffi not yet supported on other systems than linux/macos 64