[vm/ffi] Expose a subset of dart_(native_)api.h for dynamic linking.

This CL introduces dart_api_dl.h which exposes a subset of dart_api.h
and dart_native_api.h for dynamic linking at runtime through the FFI.
Dynamic linking is done through including dart_api_dl.cc in a shared
library and passing NativeApi.initializeApiDLData to the init function.

This CL also includes Native API versioning to deal with possible
version skew between native api version against which native libraries
are compiled and the version in the DartVM the code is run on.

The subset of symbols in the CL includes handle related symbols, error
related symbols, handle scope symbols, and native port sumbols.

Design: http://go/dart-ffi-expose-dart-api

Closes: https://github.com/dart-lang/sdk/issues/40607
Closes: https://github.com/dart-lang/sdk/issues/36858
Closes: https://github.com/dart-lang/sdk/issues/41319
Closes: https://github.com/flutter/flutter/issues/46887
Closes: https://github.com/flutter/flutter/issues/47061

Misc:
Closes: https://github.com/dart-lang/sdk/issues/42260

Change-Id: I9e557808dbc99b341f23964cbddbb05f26d7a6c5
Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-nnbd-linux-debug-x64-try,analyzer-nnbd-linux-release-try,front-end-nnbd-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/145592
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Daco Harkes 2020-06-18 07:30:43 +00:00 committed by commit-bot@chromium.org
parent 7d7c1298e2
commit 7eac9f355e
23 changed files with 613 additions and 110 deletions

View file

@ -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)

View file

@ -216,6 +216,8 @@ class FfiTransformer extends Transformer {
final Map<NativeType, Procedure> storeMethods;
final Map<NativeType, Procedure> elementAtMethods;
final Procedure loadStructMethod;
final Procedure asFunctionTearoff;
final Procedure lookupFunctionTearoff;
/// Classes corresponding to [NativeType], indexed by [NativeType].
final List<Class> 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.

View file

@ -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]]);

View file

@ -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",
]
}

View file

@ -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",
]

View file

@ -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<std::mutex> 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<intptr_t>(&c_request),
reinterpret_cast<intptr_t>(&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<void*>(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

View file

@ -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,

View file

@ -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 <string.h>
#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<DartApi*>(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<name##Type>( \
FindFunctionPointer(dart_api_function_pointers, #name));
DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT)
#undef DART_API_DL_INIT
return 0;
}

View file

@ -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 */

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

@ -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<int64_t>(Dart_PostCObject));
} else if (strcmp(name, "Dart_NewNativePort") == 0) {
return Integer::New(reinterpret_cast<int64_t>(Dart_NewNativePort));
} else if (strcmp(name, "Dart_CloseNativePort") == 0) {
return Integer::New(reinterpret_cast<int64_t>(Dart_CloseNativePort));
#define RETURN_FUNCTION_ADDRESS(function_name) \
if (strcmp(name, #function_name) == 0) { \
return Integer::New(reinterpret_cast<intptr_t>(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<void (*)()>(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<intptr_t>(&dart_api_data));
}
} // namespace dart

View file

@ -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) \

View file

@ -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<IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("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<Work>),
void Function(Pointer<Work>)>('ExecuteCallback');
final registerDart_PostCObject = dl.lookupFunction<
Void Function(
Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>
functionPointer),
void Function(
Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>
functionPointer)>('RegisterDart_PostCObject');
class Work extends Struct {}
Future asyncSleep(int ms) {

View file

@ -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<IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("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<Void Function(), void Function()>('StopWorkSimulator2');
final registerDart_PostCObject = dl.lookupFunction<
Void Function(
Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>
functionPointer),
void Function(
Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>
functionPointer)>('RegisterDart_PostCObject');
final registerDart_NewNativePort = dl.lookupFunction<
Void Function(
Pointer<
NativeFunction<
Int64 Function(
Pointer<Uint8>,
Pointer<NativeFunction<Dart_NativeMessageHandler>>,
Int8)>>
functionPointer),
void Function(
Pointer<
NativeFunction<
Int64 Function(
Pointer<Uint8>,
Pointer<NativeFunction<Dart_NativeMessageHandler>>,
Int8)>>
functionPointer)>('RegisterDart_NewNativePort');
final registerDart_CloseNativePort = dl.lookupFunction<
Void Function(
Pointer<NativeFunction<Int8 Function(Int64)>> functionPointer),
void Function(
Pointer<NativeFunction<Int8 Function(Int64)>> functionPointer)>(
'RegisterDart_CloseNativePort');
Future asyncSleep(int ms) {
return new Future.delayed(Duration(milliseconds: ms), () => true);
}

View file

@ -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<Void Function(Handle), void Function(Object)>(
"RegisterClosureCallback");
final invokeClosureCallback = testLibrary
.lookupFunction<Void Function(), void Function()>("InvokeClosureCallback");
final releaseClosureCallback = testLibrary
.lookupFunction<Void Function(), void Function()>("ReleaseClosureCallback");
void doClosureCallback(Object callback) {
final callback_as_function = callback as void Function();
callback_as_function();
}
final closureCallbackPointer =
Pointer.fromFunction<Void Function(Handle)>(doClosureCallback);
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 0);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
final registerClosureCallback = testLibrary.lookupFunction<
Void Function(Pointer),
void Function(Pointer)>("RegisterClosureCallbackFP");
registerClosureCallback(closureCallbackPointer);
}

View file

@ -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();
}

View file

@ -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}}" ]
}

View file

@ -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<NativeFunction<Int8 Function(Int64)>> get closeNativePort =>
Pointer.fromAddress(_nativeApiFunctionPointer("Dart_CloseNativePort"));
@patch
static int get majorVersion => _dartApiMajorVersion();
@patch
static int get minorVersion => _dartApiMinorVersion();
@patch
static Pointer<Void> get initializeApiDLData =>
Pointer.fromAddress(_initializeApiDLData());
}

View file

@ -562,8 +562,19 @@ class Dart_CObject extends Struct {}
typedef Dart_NativeMessageHandler = Void Function(Int64, Pointer<Dart_CObject>);
/// 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<NativeFunction<Int8 Function(Int64)>>
get closeNativePort;
/// Pass this to `Dart_InitializeApiDL` in your native code to enable using the
/// symbols in `dart_api_dl.h`.
external static Pointer<Void> get initializeApiDLData;
}

View file

@ -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<Void>),
int Function(Pointer<Void>)>("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<Handle Function(Handle),
Object Function(Object)>("PassObjectToCUseDynamicLinking");

View file

@ -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<Void>),
int Function(Pointer<Void>)>("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<Handle Function(Handle),
Object Function(Object)>("PassObjectToCUseDynamicLinking");