diff --git a/pkg/kernel/lib/library_index.dart b/pkg/kernel/lib/library_index.dart index 3a05805fdc2e..94db880b2d6b 100644 --- a/pkg/kernel/lib/library_index.dart +++ b/pkg/kernel/lib/library_index.dart @@ -12,6 +12,7 @@ import 'ast.dart'; class LibraryIndex { static const String getterPrefix = 'get:'; static const String setterPrefix = 'set:'; + static const String tearoffPrefix = 'get#'; /// A special class name that can be used to access the top-level members /// of a library. @@ -237,7 +238,7 @@ class _MemberTable { String getDisambiguatedExtensionName( ExtensionMemberDescriptor extensionMember) { if (extensionMember.kind == ExtensionMemberKind.TearOff) - return 'get#' + extensionMember.name.name; + return LibraryIndex.tearoffPrefix + extensionMember.name.name; if (extensionMember.kind == ExtensionMemberKind.Getter) return LibraryIndex.getterPrefix + extensionMember.name.name; if (extensionMember.kind == ExtensionMemberKind.Setter) diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart index e8ad4420d513..cc02953a55ad 100644 --- a/pkg/vm/lib/transformations/ffi.dart +++ b/pkg/vm/lib/transformations/ffi.dart @@ -216,6 +216,8 @@ class FfiTransformer extends Transformer { final Map storeMethods; final Map elementAtMethods; final Procedure loadStructMethod; + final Procedure asFunctionTearoff; + final Procedure lookupFunctionTearoff; /// Classes corresponding to [NativeType], indexed by [NativeType]. final List nativeTypesClasses; @@ -274,7 +276,13 @@ class FfiTransformer extends Transformer { final name = nativeTypeClassNames[t.index]; return index.getTopLevelMember('dart:ffi', "_elementAt$name"); }), - loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct'); + loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct'), + asFunctionTearoff = index.getMember('dart:ffi', 'NativeFunctionPointer', + LibraryIndex.tearoffPrefix + 'asFunction'), + lookupFunctionTearoff = index.getMember( + 'dart:ffi', + 'DynamicLibraryExtension', + LibraryIndex.tearoffPrefix + 'lookupFunction'); /// Computes the Dart type corresponding to a ffi.[NativeType], returns null /// if it is not a valid NativeType. diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart index 96830bfb68c0..f23c6696dc84 100644 --- a/pkg/vm/lib/transformations/ffi_use_sites.dart +++ b/pkg/vm/lib/transformations/ffi_use_sites.dart @@ -122,8 +122,15 @@ class _FfiUseSiteTransformer extends FfiTransformer { @override visitProcedure(Procedure node) { + if (isFfiLibrary && node.isExtensionMember) { + if (node == asFunctionTearoff || node == lookupFunctionTearoff) { + // Skip static checks and transformation for the tearoffs. + return node; + } + } + _staticTypeContext = new StaticTypeContext(node, env); - var result = super.visitProcedure(node); + final result = super.visitProcedure(node); _staticTypeContext = null; return result; } @@ -159,15 +166,16 @@ class _FfiUseSiteTransformer extends FfiTransformer { final Member target = node.target; try { - if (target == lookupFunctionMethod && !isFfiLibrary) { + if (target == lookupFunctionMethod) { final DartType nativeType = InterfaceType( nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]); final DartType dartType = node.arguments.types[1]; _ensureNativeTypeValid(nativeType, node); _ensureNativeTypeToDartType(nativeType, dartType, node); + return _replaceLookupFunction(node); - } else if (target == asFunctionMethod && !isFfiLibrary) { + } else if (target == asFunctionMethod) { final DartType dartType = node.arguments.types[1]; final DartType nativeType = InterfaceType( nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]); diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index da22c4452037..6bf8c3996d1b 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -210,8 +210,11 @@ source_set("dart_api") { public_configs = [ ":dart_public_config" ] sources = [ "include/dart_api.h", + "include/dart_api_dl.h", "include/dart_native_api.h", "include/dart_tools_api.h", + "include/dart_version.h", + "include/internal/dart_api_dl_impl.h", ] } diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index ce8ec7afb6af..0fd5acd5ab02 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -1139,11 +1139,14 @@ shared_library("ffi_test_dynamic_library") { shared_library("ffi_test_functions") { deps = [ ":dart" ] - # The two files here do not depend on each other. - # flutter/flutter integration tests will only use `ffi_test_functions.cc` - - # any test functionality using `dart_api.h` has to go into - # `ffi_test_functions_vmspecific.cc`. sources = [ + # This file must be compiled in for dynamic linking. + "../include/dart_api_dl.cc", + + # The two files here do not depend on each other. + # flutter/flutter integration tests will only use `ffi_test_functions.cc` - + # any test functionality using `dart_api.h` has to go into + # `ffi_test_functions_vmspecific.cc`. "ffi_test/ffi_test_functions.cc", "ffi_test/ffi_test_functions_vmspecific.cc", ] diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc index a80882b4dc16..05b8952354c7 100644 --- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc @@ -35,6 +35,8 @@ #include "include/dart_api.h" #include "include/dart_native_api.h" +#include "include/dart_api_dl.h" + namespace dart { #define CHECK(X) \ @@ -270,31 +272,9 @@ DART_EXPORT intptr_t TestCallbackWrongIsolate(void (*fn)()) { #endif // defined(TARGET_OS_LINUX) //////////////////////////////////////////////////////////////////////////////// -// Dynamic linking of dart_native_api.h for the next two samples. -typedef bool (*Dart_PostCObjectType)(Dart_Port port_id, Dart_CObject* message); -Dart_PostCObjectType Dart_PostCObject_ = nullptr; - -DART_EXPORT void RegisterDart_PostCObject( - Dart_PostCObjectType function_pointer) { - Dart_PostCObject_ = function_pointer; -} - -typedef Dart_Port (*Dart_NewNativePortType)(const char* name, - Dart_NativeMessageHandler handler, - bool handle_concurrently); -Dart_NewNativePortType Dart_NewNativePort_ = nullptr; - -DART_EXPORT void RegisterDart_NewNativePort( - Dart_NewNativePortType function_pointer) { - Dart_NewNativePort_ = function_pointer; -} - -typedef bool (*Dart_CloseNativePortType)(Dart_Port native_port_id); -Dart_CloseNativePortType Dart_CloseNativePort_ = nullptr; - -DART_EXPORT void RegisterDart_CloseNativePort( - Dart_CloseNativePortType function_pointer) { - Dart_CloseNativePort_ = function_pointer; +// Initialize `dart_api_dl.h` +DART_EXPORT intptr_t InitDartApiDL(void* data) { + return Dart_InitializeApiDL(data); } //////////////////////////////////////////////////////////////////////////////// @@ -342,7 +322,7 @@ void NotifyDart(Dart_Port send_port, const Work* work) { dart_object.type = Dart_CObject_kInt64; dart_object.value.as_int64 = work_addr; - const bool result = Dart_PostCObject_(send_port, &dart_object); + const bool result = Dart_PostCObject_DL(send_port, &dart_object); if (!result) { FATAL("C : Posting message to port failed."); } @@ -504,16 +484,16 @@ class PendingCall { PendingCall(void** buffer, size_t* length) : response_buffer_(buffer), response_length_(length) { receive_port_ = - Dart_NewNativePort_("cpp-response", &PendingCall::HandleResponse, - /*handle_concurrently=*/false); + Dart_NewNativePort_DL("cpp-response", &PendingCall::HandleResponse, + /*handle_concurrently=*/false); } - ~PendingCall() { Dart_CloseNativePort_(receive_port_); } + ~PendingCall() { Dart_CloseNativePort_DL(receive_port_); } Dart_Port port() const { return receive_port_; } void PostAndWait(Dart_Port port, Dart_CObject* object) { std::unique_lock lock(mutex); - const bool success = Dart_PostCObject_(send_port_, object); + const bool success = Dart_PostCObject_DL(send_port_, object); if (!success) FATAL("Failed to send message, invalid port or isolate died"); printf("C : Waiting for result.\n"); @@ -668,7 +648,7 @@ void MyCallback2(uint8_t a) { printf("C : Dart_PostCObject_(request: %" Px ", call: %" Px ").\n", reinterpret_cast(&c_request), reinterpret_cast(&c_pending_call)); - Dart_PostCObject_(send_port_, &c_request); + Dart_PostCObject_DL(send_port_, &c_request); } // Simulated work for Thread #1. @@ -793,7 +773,8 @@ DART_EXPORT void ThreadPoolTest_BarrierSync( //////////////////////////////////////////////////////////////////////////////// // Functions for handle tests. // -// vmspecific_handle_test.dart +// vmspecific_handle_test.dart (statically linked). +// vmspecific_handle_dynamically_linked_test.dart (dynamically linked). static void RunFinalizer(void* isolate_callback_data, Dart_WeakPersistentHandle handle, @@ -912,4 +893,46 @@ DART_EXPORT Dart_Handle TrueHandle() { return Dart_True(); } +DART_EXPORT Dart_Handle PassObjectToCUseDynamicLinking(Dart_Handle h) { + auto persistent_handle = Dart_NewPersistentHandle_DL(h); + + Dart_Handle handle_2 = Dart_HandleFromPersistent_DL(persistent_handle); + Dart_SetPersistentHandle_DL(persistent_handle, h); + Dart_DeletePersistentHandle_DL(persistent_handle); + + auto weak_handle = Dart_NewWeakPersistentHandle_DL( + handle_2, reinterpret_cast(0x1234), 64, RunFinalizer); + Dart_Handle return_value = Dart_HandleFromWeakPersistent_DL(weak_handle); + + Dart_DeleteWeakPersistentHandle_DL(weak_handle); + + return return_value; +} + +//////////////////////////////////////////////////////////////////////////////// +// Example for doing closure callbacks with help of `dart_api.h`. +// +// sample_ffi_functions_callbacks_closures.dart + +void (*callback_)(Dart_Handle); +Dart_PersistentHandle closure_to_callback_; + +DART_EXPORT void RegisterClosureCallbackFP(void (*callback)(Dart_Handle)) { + callback_ = callback; +} + +DART_EXPORT void RegisterClosureCallback(Dart_Handle h) { + closure_to_callback_ = Dart_NewPersistentHandle_DL(h); +} + +DART_EXPORT void InvokeClosureCallback() { + Dart_Handle closure_handle = + Dart_HandleFromPersistent_DL(closure_to_callback_); + callback_(closure_handle); +} + +DART_EXPORT void ReleaseClosureCallback() { + Dart_DeletePersistentHandle_DL(closure_to_callback_); +} + } // namespace dart diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 56120e78fa48..a58095cf3067 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -243,6 +243,8 @@ typedef struct _Dart_IsolateGroup* Dart_IsolateGroup; typedef struct _Dart_Handle* Dart_Handle; typedef Dart_Handle Dart_PersistentHandle; typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle; +// These three structs are versioned by DART_API_DL_MAJOR_VERSION, bump the +// version when changing this struct. typedef void (*Dart_WeakPersistentHandleFinalizer)( void* isolate_callback_data, diff --git a/runtime/include/dart_api_dl.cc b/runtime/include/dart_api_dl.cc new file mode 100644 index 000000000000..5ab685d921ef --- /dev/null +++ b/runtime/include/dart_api_dl.cc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020, 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. + */ + +#include "include/dart_api_dl.h" +#include "include/dart_version.h" +#include "include/internal/dart_api_dl_impl.h" + +#include + +#define DART_API_DL_DEFINITIONS(name) \ + using name##Type = decltype(&name); \ + name##Type name##_DL = nullptr; +DART_API_ALL_DL_SYMBOLS(DART_API_DL_DEFINITIONS) +#undef DART_API_DL_DEFINITIONS + +typedef void (*DartApiEntry_function)(); + +DartApiEntry_function FindFunctionPointer(const DartApiEntry* entries, + const char* name) { + while (entries->name != nullptr) { + if (strcmp(entries->name, name) == 0) return entries->function; + entries++; + } + return nullptr; +} + +intptr_t Dart_InitializeApiDL(void* data) { + DartApi* dart_api_data = reinterpret_cast(data); + + if (dart_api_data->major != DART_API_DL_MAJOR_VERSION) { + // If the DartVM we're running on does not have the same version as this + // file was compiled against, refuse to initialize. The symbols are not + // compatible. + return -1; + } + // Minor versions are allowed to be different. + // If the DartVM has a higher minor version, it will provide more symbols + // than we initialize here. + // If the DartVM has a lower minor version, it will not provide all symbols. + // In that case, we leave the missing symbols un-initialized. Those symbols + // should not be used by the Dart and native code. The client is responsible + // for checking the minor version number himself based on which symbols it + // is using. + // (If we would error out on this case, recompiling native code against a + // newer SDK would break all uses on older SDKs, which is too strict.) + + const DartApiEntry* dart_api_function_pointers = dart_api_data->functions; + +#define DART_API_DL_INIT(name) \ + name##_DL = reinterpret_cast( \ + FindFunctionPointer(dart_api_function_pointers, #name)); + DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT) +#undef DART_API_DL_INIT + + return 0; +} diff --git a/runtime/include/dart_api_dl.h b/runtime/include/dart_api_dl.h new file mode 100644 index 000000000000..ba8424dd1ec1 --- /dev/null +++ b/runtime/include/dart_api_dl.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020, 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. + */ + +#ifndef RUNTIME_INCLUDE_DART_API_DL_H_ +#define RUNTIME_INCLUDE_DART_API_DL_H_ + +#include "include/dart_api.h" +#include "include/dart_native_api.h" + +/** \mainpage Dynamically Linked Dart API + * + * This exposes a subset of symbols from dart_api.h and dart_native_api.h + * available in every Dart embedder through dynamic linking. + * + * All symbols are postfixed with _DL to indicate that they are dynamically + * linked and to prevent conflicts with the original symbol. + * + * Link `dart_api_dl.cc` file into your library and invoke + * `Dart_InitializeApiDL` with `NativeApi.initializeApiDLData`. + */ + +intptr_t Dart_InitializeApiDL(void* data); + +// IMPORTANT! Never update these signatures without properly updating +// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION. +// +// Verbatim copy of `dart_native_api.h` and `dart_api.h` symbols to trigger +// compile-time errors if the sybols in those files are updated without +// updating these. +// +// Function signatures and typedefs are carbon copied. Structs are typechecked +// nominally in C/C++, so they are not copied, instead a comment is added to +// their definition. +typedef int64_t Dart_Port_DL; + +typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id, + Dart_CObject* message); + +DART_EXTERN_C bool (*Dart_PostCObject_DL)(Dart_Port_DL port_id, + Dart_CObject* message); + +DART_EXTERN_C bool (*Dart_PostInteger_DL)(Dart_Port_DL port_id, + int64_t message); + +DART_EXTERN_C Dart_Port_DL (*Dart_NewNativePort_DL)( + const char* name, + Dart_NativeMessageHandler_DL handler, + bool handle_concurrently); + +DART_EXTERN_C bool (*Dart_CloseNativePort_DL)(Dart_Port_DL native_port_id); + +DART_EXTERN_C bool (*Dart_IsError_DL)(Dart_Handle handle); + +DART_EXTERN_C bool (*Dart_IsApiError_DL)(Dart_Handle handle); + +DART_EXTERN_C bool (*Dart_IsUnhandledExceptionError_DL)(Dart_Handle handle); + +DART_EXTERN_C bool (*Dart_IsCompilationError_DL)(Dart_Handle handle); + +DART_EXTERN_C bool (*Dart_IsFatalError_DL)(Dart_Handle handle); + +DART_EXTERN_C const char* (*Dart_GetError_DL)(Dart_Handle handle); + +DART_EXTERN_C bool (*Dart_ErrorHasException_DL)(Dart_Handle handle); + +DART_EXTERN_C Dart_Handle (*Dart_ErrorGetException_DL)(Dart_Handle handle); + +DART_EXTERN_C Dart_Handle (*Dart_ErrorGetStackTrace_DL)(Dart_Handle handle); + +DART_EXTERN_C Dart_Handle (*Dart_NewApiError_DL)(const char* error); + +DART_EXTERN_C Dart_Handle (*Dart_NewCompilationError_DL)(const char* error); + +DART_EXTERN_C Dart_Handle (*Dart_NewUnhandledExceptionError_DL)( + Dart_Handle exception); + +DART_EXTERN_C void (*Dart_PropagateError_DL)(Dart_Handle handle); + +DART_EXTERN_C Dart_Handle (*Dart_ToString_DL)(Dart_Handle object); + +DART_EXTERN_C bool (*Dart_IdentityEquals_DL)(Dart_Handle obj1, + Dart_Handle obj2); + +DART_EXTERN_C Dart_Handle (*Dart_HandleFromPersistent_DL)( + Dart_PersistentHandle object); + +DART_EXTERN_C Dart_Handle (*Dart_HandleFromWeakPersistent_DL)( + Dart_WeakPersistentHandle object); + +DART_EXTERN_C Dart_PersistentHandle (*Dart_NewPersistentHandle_DL)( + Dart_Handle object); + +DART_EXTERN_C void (*Dart_SetPersistentHandle_DL)(Dart_PersistentHandle obj1, + Dart_Handle obj2); + +DART_EXTERN_C void (*Dart_DeletePersistentHandle_DL)( + Dart_PersistentHandle object); + +DART_EXTERN_C Dart_WeakPersistentHandle (*Dart_NewWeakPersistentHandle_DL)( + Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_WeakPersistentHandleFinalizer callback); + +DART_EXTERN_C void (*Dart_DeleteWeakPersistentHandle_DL)( + Dart_WeakPersistentHandle object); + +DART_EXTERN_C bool (*Dart_Post_DL)(Dart_Port_DL port_id, Dart_Handle object); + +DART_EXTERN_C Dart_Handle (*Dart_NewSendPort_DL)(Dart_Port_DL port_id); + +DART_EXTERN_C Dart_Handle (*Dart_SendPortGetId_DL)(Dart_Handle port, + Dart_Port_DL* port_id); + +DART_EXTERN_C void (*Dart_EnterScope_DL)(); + +DART_EXTERN_C void (*Dart_ExitScope_DL)(); +// IMPORTANT! Never update these signatures without properly updating +// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION. +// +// End of verbatim copy. + +#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */ \ No newline at end of file diff --git a/runtime/include/dart_native_api.h b/runtime/include/dart_native_api.h index 7e941182f8e2..495d45485662 100644 --- a/runtime/include/dart_native_api.h +++ b/runtime/include/dart_native_api.h @@ -83,6 +83,8 @@ typedef struct _Dart_CObject { } as_external_typed_data; } value; } Dart_CObject; +// This struct is versioned by DART_API_DL_MAJOR_VERSION, bump the version when +// changing this struct. /** * Posts a message on some port. The message will contain the Dart_CObject diff --git a/runtime/include/dart_version.h b/runtime/include/dart_version.h new file mode 100644 index 000000000000..777620be9466 --- /dev/null +++ b/runtime/include/dart_version.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020, 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. + */ + +#ifndef RUNTIME_INCLUDE_DART_VERSION_H_ +#define RUNTIME_INCLUDE_DART_VERSION_H_ + +// On breaking changes the major version is increased. +// On backwards compatible changes the minor version is increased. +// The versioning covers the symbols exposed in dart_api_dl.h +#define DART_API_DL_MAJOR_VERSION 1 +#define DART_API_DL_MINOR_VERSION 0 + +#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */ diff --git a/runtime/include/internal/dart_api_dl_impl.h b/runtime/include/internal/dart_api_dl_impl.h new file mode 100644 index 000000000000..f77063d99bf1 --- /dev/null +++ b/runtime/include/internal/dart_api_dl_impl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, 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. + */ + +#ifndef RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ +#define RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ + +// dart_native_api.h symbols can be called on any thread. +#define DART_NATIVE_API_DL_SYMBOLS(F) \ + /***** dart_native_api.h *****/ \ + /* Dart_Port */ \ + F(Dart_PostCObject) \ + F(Dart_PostInteger) \ + F(Dart_NewNativePort) \ + F(Dart_CloseNativePort) + +// dart_api.h symbols can only be called on Dart threads. +#define DART_API_DL_SYMBOLS(F) \ + /***** dart_api.h *****/ \ + /* Errors */ \ + F(Dart_IsError) \ + F(Dart_IsApiError) \ + F(Dart_IsUnhandledExceptionError) \ + F(Dart_IsCompilationError) \ + F(Dart_IsFatalError) \ + F(Dart_GetError) \ + F(Dart_ErrorHasException) \ + F(Dart_ErrorGetException) \ + F(Dart_ErrorGetStackTrace) \ + F(Dart_NewApiError) \ + F(Dart_NewCompilationError) \ + F(Dart_NewUnhandledExceptionError) \ + F(Dart_PropagateError) \ + /* Dart_Handle, Dart_PersistentHandle, Dart_WeakPersistentHandle */ \ + F(Dart_NewPersistentHandle) \ + F(Dart_SetPersistentHandle) \ + F(Dart_HandleFromPersistent) \ + F(Dart_DeletePersistentHandle) \ + F(Dart_NewWeakPersistentHandle) \ + F(Dart_HandleFromWeakPersistent) \ + F(Dart_DeleteWeakPersistentHandle) \ + /* Dart_Port */ \ + F(Dart_Post) \ + F(Dart_NewSendPort) \ + F(Dart_SendPortGetId) \ + /* Scopes */ \ + F(Dart_EnterScope) \ + F(Dart_ExitScope) + +#define DART_API_ALL_DL_SYMBOLS(F) \ + DART_NATIVE_API_DL_SYMBOLS(F) \ + DART_API_DL_SYMBOLS(F) + +struct DartApiEntry { + const char* name; + void (*function)(); +}; + +struct DartApi { + const int major; + const int minor; + const DartApiEntry* const functions; +}; + +#endif /* RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ */ /* NOLINT */ diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc index d4c6c431f639..1a8bc20facff 100644 --- a/runtime/lib/ffi.cc +++ b/runtime/lib/ffi.cc @@ -4,6 +4,8 @@ #include "include/dart_api.h" #include "include/dart_native_api.h" +#include "include/dart_version.h" +#include "include/internal/dart_api_dl_impl.h" #include "platform/globals.h" #include "vm/bootstrap_natives.h" #include "vm/class_finalizer.h" @@ -495,21 +497,41 @@ DEFINE_NATIVE_ENTRY(Ffi_pointerFromFunction, 1, 1) { return Pointer::New(type_arg, entry_point); } -DEFINE_NATIVE_ENTRY(NativeApiFunctionPointer, 0, 1) { +DEFINE_NATIVE_ENTRY(DartNativeApiFunctionPointer, 0, 1) { GET_NON_NULL_NATIVE_ARGUMENT(String, name_dart, arguments->NativeArgAt(0)); const char* name = name_dart.ToCString(); - if (strcmp(name, "Dart_PostCObject") == 0) { - return Integer::New(reinterpret_cast(Dart_PostCObject)); - } else if (strcmp(name, "Dart_NewNativePort") == 0) { - return Integer::New(reinterpret_cast(Dart_NewNativePort)); - } else if (strcmp(name, "Dart_CloseNativePort") == 0) { - return Integer::New(reinterpret_cast(Dart_CloseNativePort)); +#define RETURN_FUNCTION_ADDRESS(function_name) \ + if (strcmp(name, #function_name) == 0) { \ + return Integer::New(reinterpret_cast(function_name)); \ } + DART_NATIVE_API_DL_SYMBOLS(RETURN_FUNCTION_ADDRESS) +#undef RETURN_FUNCTION_ADDRESS const String& error = String::Handle( String::NewFormatted("Unknown dart_native_api.h symbol: %s.", name)); Exceptions::ThrowArgumentError(error); } +DEFINE_NATIVE_ENTRY(DartApiDLMajorVersion, 0, 0) { + return Integer::New(DART_API_DL_MAJOR_VERSION); +} + +DEFINE_NATIVE_ENTRY(DartApiDLMinorVersion, 0, 0) { + return Integer::New(DART_API_DL_MINOR_VERSION); +} + +static const DartApiEntry dart_api_entries[] = { +#define ENTRY(name) DartApiEntry{#name, reinterpret_cast(name)}, + DART_API_ALL_DL_SYMBOLS(ENTRY) +#undef ENTRY + DartApiEntry{nullptr, nullptr}}; + +static const DartApi dart_api_data = { + DART_API_DL_MAJOR_VERSION, DART_API_DL_MINOR_VERSION, dart_api_entries}; + +DEFINE_NATIVE_ENTRY(DartApiDLInitializeData, 0, 0) { + return Integer::New(reinterpret_cast(&dart_api_data)); +} + } // namespace dart diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 6ce5668eb487..5ed3f684200d 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -413,7 +413,10 @@ namespace dart { V(Ffi_asExternalTypedData, 2) \ V(Ffi_dl_processLibrary, 0) \ V(Ffi_dl_executableLibrary, 0) \ - V(NativeApiFunctionPointer, 1) \ + V(DartApiDLInitializeData, 0) \ + V(DartApiDLMajorVersion, 0) \ + V(DartApiDLMinorVersion, 0) \ + V(DartNativeApiFunctionPointer, 1) \ V(TransferableTypedData_factory, 2) \ V(TransferableTypedData_materialize, 1) \ V(Wasm_initModule, 2) \ diff --git a/samples/ffi/async/sample_async_callback.dart b/samples/ffi/async/sample_async_callback.dart index f9d8ea46539c..432a2ebfdbea 100644 --- a/samples/ffi/async/sample_async_callback.dart +++ b/samples/ffi/async/sample_async_callback.dart @@ -26,7 +26,11 @@ main() async { print("C T2 = Some C thread executing C."); print("C = C T1 or C T2."); print("Dart: Setup."); - registerDart_PostCObject(NativeApi.postCObject); + Expect.isTrue(NativeApi.majorVersion == 1); + Expect.isTrue(NativeApi.minorVersion >= 0); + final initializeApi = dl.lookupFunction), + int Function(Pointer)>("InitDartApiDL"); + Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); final interactiveCppRequests = ReceivePort()..listen(requestExecuteCallback); final int nativePort = interactiveCppRequests.sendPort.nativePort; @@ -101,14 +105,6 @@ final stopWorkSimulator = final executeCallback = dl.lookupFunction), void Function(Pointer)>('ExecuteCallback'); -final registerDart_PostCObject = dl.lookupFunction< - Void Function( - Pointer)>> - functionPointer), - void Function( - Pointer)>> - functionPointer)>('RegisterDart_PostCObject'); - class Work extends Struct {} Future asyncSleep(int ms) { diff --git a/samples/ffi/async/sample_native_port_call.dart b/samples/ffi/async/sample_native_port_call.dart index 1e654ef84988..aa4b48e96d53 100644 --- a/samples/ffi/async/sample_native_port_call.dart +++ b/samples/ffi/async/sample_native_port_call.dart @@ -35,9 +35,11 @@ main() async { print("C T2 = Some C thread executing C."); print("C = C T1 or C T2."); print("Dart: Setup."); - registerDart_PostCObject(NativeApi.postCObject); - registerDart_NewNativePort(NativeApi.newNativePort); - registerDart_CloseNativePort(NativeApi.closeNativePort); + Expect.isTrue(NativeApi.majorVersion == 1); + Expect.isTrue(NativeApi.minorVersion >= 0); + final initializeApi = dl.lookupFunction), + int Function(Pointer)>("InitDartApiDL"); + Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); final interactiveCppRequests = ReceivePort()..listen(handleCppRequests); final int nativePort = interactiveCppRequests.sendPort.nativePort; @@ -130,39 +132,6 @@ final startWorkSimulator2 = final stopWorkSimulator2 = dl.lookupFunction('StopWorkSimulator2'); -final registerDart_PostCObject = dl.lookupFunction< - Void Function( - Pointer)>> - functionPointer), - void Function( - Pointer)>> - functionPointer)>('RegisterDart_PostCObject'); - -final registerDart_NewNativePort = dl.lookupFunction< - Void Function( - Pointer< - NativeFunction< - Int64 Function( - Pointer, - Pointer>, - Int8)>> - functionPointer), - void Function( - Pointer< - NativeFunction< - Int64 Function( - Pointer, - Pointer>, - Int8)>> - functionPointer)>('RegisterDart_NewNativePort'); - -final registerDart_CloseNativePort = dl.lookupFunction< - Void Function( - Pointer> functionPointer), - void Function( - Pointer> functionPointer)>( - 'RegisterDart_CloseNativePort'); - Future asyncSleep(int ms) { return new Future.delayed(Duration(milliseconds: ms), () => true); } diff --git a/samples/ffi/sample_ffi_functions_callbacks_closures.dart b/samples/ffi/sample_ffi_functions_callbacks_closures.dart new file mode 100644 index 000000000000..ba876da7d444 --- /dev/null +++ b/samples/ffi/sample_ffi_functions_callbacks_closures.dart @@ -0,0 +1,67 @@ +// Copyright (c) 2020, 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:ffi'; + +import 'package:expect/expect.dart'; + +import 'dylib_utils.dart'; + +void main() { + print('start main'); + + doDynamicLinking(); + + int counter = 0; + void closure() { + counter++; + } + + // C holds on to this closure through a `Dart_PersistenHandle`. + registerClosureCallback(closure); + + // Some time later this closure can be invoked. + invokeClosureCallback(); + Expect.equals(1, counter); + + // When C is done it needs to stop holding on to the closure such that the + // Dart GC can collect the closure. + releaseClosureCallback(); + + print('end main'); +} + +final testLibrary = dlopenPlatformSpecific("ffi_test_functions"); + +final registerClosureCallback = + testLibrary.lookupFunction( + "RegisterClosureCallback"); + +final invokeClosureCallback = testLibrary + .lookupFunction("InvokeClosureCallback"); + +final releaseClosureCallback = testLibrary + .lookupFunction("ReleaseClosureCallback"); + +void doClosureCallback(Object callback) { + final callback_as_function = callback as void Function(); + callback_as_function(); +} + +final closureCallbackPointer = + Pointer.fromFunction(doClosureCallback); + +void doDynamicLinking() { + Expect.isTrue(NativeApi.majorVersion == 1); + Expect.isTrue(NativeApi.minorVersion >= 0); + final initializeApi = testLibrary.lookupFunction< + IntPtr Function(Pointer), + int Function(Pointer)>("InitDartApiDL"); + Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); + + final registerClosureCallback = testLibrary.lookupFunction< + Void Function(Pointer), + void Function(Pointer)>("RegisterClosureCallbackFP"); + registerClosureCallback(closureCallbackPointer); +} diff --git a/samples/ffi/samples_test.dart b/samples/ffi/samples_test.dart index 98144638ed0a..954aa9cd7229 100644 --- a/samples/ffi/samples_test.dart +++ b/samples/ffi/samples_test.dart @@ -6,20 +6,23 @@ // // SharedObjects=ffi_test_dynamic_library ffi_test_functions -import 'sample_ffi_bitfield.dart' as sample0; -import 'sample_ffi_data.dart' as sample1; -import 'sample_ffi_dynamic_library.dart' as sample2; -import 'sample_ffi_functions_callbacks.dart' as sample3; -import 'sample_ffi_functions_structs.dart' as sample4; -import 'sample_ffi_functions.dart' as sample5; -import 'sample_ffi_structs.dart' as sample6; +import 'sample_ffi_bitfield.dart' as bitfield; +import 'sample_ffi_data.dart' as data; +import 'sample_ffi_dynamic_library.dart' as dynamic_library; +import 'sample_ffi_functions_callbacks_closures.dart' + as functions_callbacks_closures; +import 'sample_ffi_functions_callbacks.dart' as functions_callbacks; +import 'sample_ffi_functions_structs.dart' as functions_structs; +import 'sample_ffi_functions.dart' as functions; +import 'sample_ffi_structs.dart' as structs; main() { - sample0.main(); - sample1.main(); - sample2.main(); - sample3.main(); - sample4.main(); - sample5.main(); - sample6.main(); + bitfield.main(); + data.main(); + dynamic_library.main(); + functions_callbacks_closures.main(); + functions_callbacks.main(); + functions_structs.main(); + functions.main(); + structs.main(); } diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 313ba52226c3..bd50f4232d4b 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -742,8 +742,11 @@ copy("copy_headers") { visibility = [ ":create_common_sdk" ] sources = [ "../runtime/include/dart_api.h", + "../runtime/include/dart_api_dl.h", "../runtime/include/dart_native_api.h", "../runtime/include/dart_tools_api.h", + "../runtime/include/dart_version.h", + "../runtime/include/internal/dart_api_dl_impl.h", ] outputs = [ "$root_out_dir/dart-sdk/include/{{source_file_part}}" ] } diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart index d42689ce0d3f..735c7e28ff8f 100644 --- a/sdk/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart @@ -454,7 +454,14 @@ extension NativePort on SendPort { int get nativePort native "SendPortImpl_get_id"; } -int _nativeApiFunctionPointer(String symbol) native "NativeApiFunctionPointer"; +int _nativeApiFunctionPointer(String symbol) + native "DartNativeApiFunctionPointer"; + +int _initializeApiDLData() native "DartApiDLInitializeData"; + +int _dartApiMajorVersion() native "DartApiDLMajorVersion"; + +int _dartApiMinorVersion() native "DartApiDLMinorVersion"; @patch abstract class NativeApi { @@ -475,4 +482,14 @@ abstract class NativeApi { @patch static Pointer> get closeNativePort => Pointer.fromAddress(_nativeApiFunctionPointer("Dart_CloseNativePort")); + + @patch + static int get majorVersion => _dartApiMajorVersion(); + + @patch + static int get minorVersion => _dartApiMinorVersion(); + + @patch + static Pointer get initializeApiDLData => + Pointer.fromAddress(_initializeApiDLData()); } diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart index 3b6e236b74b5..68424f86632f 100644 --- a/sdk/lib/ffi/ffi.dart +++ b/sdk/lib/ffi/ffi.dart @@ -562,8 +562,19 @@ class Dart_CObject extends Struct {} typedef Dart_NativeMessageHandler = Void Function(Int64, Pointer); -/// Exposes function pointers to functions in `dart_native_api.h`. +/// Utilities for accessing the Dart VM API from Dart code or +/// from C code via `dart_api_dl.h`. abstract class NativeApi { + /// On breaking changes the major version is increased. + /// + /// The versioning covers the API surface in `dart_api_dl.h`. + external static int get majorVersion; + + /// On backwards compatible changes the minor version is increased. + /// + /// The versioning covers the API surface in `dart_api_dl.h`. + external static int get minorVersion; + /// A function pointer to /// `bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message)` /// in `dart_native_api.h`. @@ -590,4 +601,8 @@ abstract class NativeApi { /// in `dart_native_api.h`. external static Pointer> get closeNativePort; + + /// Pass this to `Dart_InitializeApiDL` in your native code to enable using the + /// symbols in `dart_api_dl.h`. + external static Pointer get initializeApiDLData; } diff --git a/tests/ffi/vmspecific_handle_dynamically_linked_test.dart b/tests/ffi/vmspecific_handle_dynamically_linked_test.dart new file mode 100644 index 000000000000..e2237e86399f --- /dev/null +++ b/tests/ffi/vmspecific_handle_dynamically_linked_test.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2020, 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. +// +// SharedObjects=ffi_test_functions + +import 'dart:ffi'; + +import 'package:expect/expect.dart'; + +import 'dylib_utils.dart'; + +void main() { + doDynamicLinking(); + testHandle(); +} + +void doDynamicLinking() { + Expect.isTrue(NativeApi.majorVersion == 1); + Expect.isTrue(NativeApi.minorVersion >= 0); + final initializeApi = testLibrary.lookupFunction< + IntPtr Function(Pointer), + int Function(Pointer)>("InitDartApiDL"); + Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); +} + +void testHandle() { + final s = SomeClass(123); + print("passObjectToC($s)"); + final result = passObjectToC(s); + print("result = $result"); + Expect.isTrue(identical(s, result)); +} + +class SomeClass { + // We use this getter in the native api, don't tree shake it. + @pragma("vm:entry-point") + final int a; + SomeClass(this.a); +} + +final testLibrary = dlopenPlatformSpecific("ffi_test_functions"); + +final passObjectToC = testLibrary.lookupFunction("PassObjectToCUseDynamicLinking"); diff --git a/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart b/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart new file mode 100644 index 000000000000..e2237e86399f --- /dev/null +++ b/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2020, 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. +// +// SharedObjects=ffi_test_functions + +import 'dart:ffi'; + +import 'package:expect/expect.dart'; + +import 'dylib_utils.dart'; + +void main() { + doDynamicLinking(); + testHandle(); +} + +void doDynamicLinking() { + Expect.isTrue(NativeApi.majorVersion == 1); + Expect.isTrue(NativeApi.minorVersion >= 0); + final initializeApi = testLibrary.lookupFunction< + IntPtr Function(Pointer), + int Function(Pointer)>("InitDartApiDL"); + Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); +} + +void testHandle() { + final s = SomeClass(123); + print("passObjectToC($s)"); + final result = passObjectToC(s); + print("result = $result"); + Expect.isTrue(identical(s, result)); +} + +class SomeClass { + // We use this getter in the native api, don't tree shake it. + @pragma("vm:entry-point") + final int a; + SomeClass(this.a); +} + +final testLibrary = dlopenPlatformSpecific("ffi_test_functions"); + +final passObjectToC = testLibrary.lookupFunction("PassObjectToCUseDynamicLinking");