Skip to content

Commit 7eac9f3

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[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: #40607 Closes: #36858 Closes: #41319 Closes: flutter/flutter#46887 Closes: flutter/flutter#47061 Misc: Closes: #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 <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent 7d7c129 commit 7eac9f3

23 files changed

+613
-110
lines changed

pkg/kernel/lib/library_index.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'ast.dart';
1212
class LibraryIndex {
1313
static const String getterPrefix = 'get:';
1414
static const String setterPrefix = 'set:';
15+
static const String tearoffPrefix = 'get#';
1516

1617
/// A special class name that can be used to access the top-level members
1718
/// of a library.
@@ -237,7 +238,7 @@ class _MemberTable {
237238
String getDisambiguatedExtensionName(
238239
ExtensionMemberDescriptor extensionMember) {
239240
if (extensionMember.kind == ExtensionMemberKind.TearOff)
240-
return 'get#' + extensionMember.name.name;
241+
return LibraryIndex.tearoffPrefix + extensionMember.name.name;
241242
if (extensionMember.kind == ExtensionMemberKind.Getter)
242243
return LibraryIndex.getterPrefix + extensionMember.name.name;
243244
if (extensionMember.kind == ExtensionMemberKind.Setter)

pkg/vm/lib/transformations/ffi.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ class FfiTransformer extends Transformer {
216216
final Map<NativeType, Procedure> storeMethods;
217217
final Map<NativeType, Procedure> elementAtMethods;
218218
final Procedure loadStructMethod;
219+
final Procedure asFunctionTearoff;
220+
final Procedure lookupFunctionTearoff;
219221

220222
/// Classes corresponding to [NativeType], indexed by [NativeType].
221223
final List<Class> nativeTypesClasses;
@@ -274,7 +276,13 @@ class FfiTransformer extends Transformer {
274276
final name = nativeTypeClassNames[t.index];
275277
return index.getTopLevelMember('dart:ffi', "_elementAt$name");
276278
}),
277-
loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct');
279+
loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct'),
280+
asFunctionTearoff = index.getMember('dart:ffi', 'NativeFunctionPointer',
281+
LibraryIndex.tearoffPrefix + 'asFunction'),
282+
lookupFunctionTearoff = index.getMember(
283+
'dart:ffi',
284+
'DynamicLibraryExtension',
285+
LibraryIndex.tearoffPrefix + 'lookupFunction');
278286

279287
/// Computes the Dart type corresponding to a ffi.[NativeType], returns null
280288
/// if it is not a valid NativeType.

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,15 @@ class _FfiUseSiteTransformer extends FfiTransformer {
122122

123123
@override
124124
visitProcedure(Procedure node) {
125+
if (isFfiLibrary && node.isExtensionMember) {
126+
if (node == asFunctionTearoff || node == lookupFunctionTearoff) {
127+
// Skip static checks and transformation for the tearoffs.
128+
return node;
129+
}
130+
}
131+
125132
_staticTypeContext = new StaticTypeContext(node, env);
126-
var result = super.visitProcedure(node);
133+
final result = super.visitProcedure(node);
127134
_staticTypeContext = null;
128135
return result;
129136
}
@@ -159,15 +166,16 @@ class _FfiUseSiteTransformer extends FfiTransformer {
159166

160167
final Member target = node.target;
161168
try {
162-
if (target == lookupFunctionMethod && !isFfiLibrary) {
169+
if (target == lookupFunctionMethod) {
163170
final DartType nativeType = InterfaceType(
164171
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
165172
final DartType dartType = node.arguments.types[1];
166173

167174
_ensureNativeTypeValid(nativeType, node);
168175
_ensureNativeTypeToDartType(nativeType, dartType, node);
176+
169177
return _replaceLookupFunction(node);
170-
} else if (target == asFunctionMethod && !isFfiLibrary) {
178+
} else if (target == asFunctionMethod) {
171179
final DartType dartType = node.arguments.types[1];
172180
final DartType nativeType = InterfaceType(
173181
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);

runtime/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,11 @@ source_set("dart_api") {
210210
public_configs = [ ":dart_public_config" ]
211211
sources = [
212212
"include/dart_api.h",
213+
"include/dart_api_dl.h",
213214
"include/dart_native_api.h",
214215
"include/dart_tools_api.h",
216+
"include/dart_version.h",
217+
"include/internal/dart_api_dl_impl.h",
215218
]
216219
}
217220

runtime/bin/BUILD.gn

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,11 +1139,14 @@ shared_library("ffi_test_dynamic_library") {
11391139
shared_library("ffi_test_functions") {
11401140
deps = [ ":dart" ]
11411141

1142-
# The two files here do not depend on each other.
1143-
# flutter/flutter integration tests will only use `ffi_test_functions.cc` -
1144-
# any test functionality using `dart_api.h` has to go into
1145-
# `ffi_test_functions_vmspecific.cc`.
11461142
sources = [
1143+
# This file must be compiled in for dynamic linking.
1144+
"../include/dart_api_dl.cc",
1145+
1146+
# The two files here do not depend on each other.
1147+
# flutter/flutter integration tests will only use `ffi_test_functions.cc` -
1148+
# any test functionality using `dart_api.h` has to go into
1149+
# `ffi_test_functions_vmspecific.cc`.
11471150
"ffi_test/ffi_test_functions.cc",
11481151
"ffi_test/ffi_test_functions_vmspecific.cc",
11491152
]

runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include "include/dart_api.h"
3636
#include "include/dart_native_api.h"
3737

38+
#include "include/dart_api_dl.h"
39+
3840
namespace dart {
3941

4042
#define CHECK(X) \
@@ -270,31 +272,9 @@ DART_EXPORT intptr_t TestCallbackWrongIsolate(void (*fn)()) {
270272
#endif // defined(TARGET_OS_LINUX)
271273

272274
////////////////////////////////////////////////////////////////////////////////
273-
// Dynamic linking of dart_native_api.h for the next two samples.
274-
typedef bool (*Dart_PostCObjectType)(Dart_Port port_id, Dart_CObject* message);
275-
Dart_PostCObjectType Dart_PostCObject_ = nullptr;
276-
277-
DART_EXPORT void RegisterDart_PostCObject(
278-
Dart_PostCObjectType function_pointer) {
279-
Dart_PostCObject_ = function_pointer;
280-
}
281-
282-
typedef Dart_Port (*Dart_NewNativePortType)(const char* name,
283-
Dart_NativeMessageHandler handler,
284-
bool handle_concurrently);
285-
Dart_NewNativePortType Dart_NewNativePort_ = nullptr;
286-
287-
DART_EXPORT void RegisterDart_NewNativePort(
288-
Dart_NewNativePortType function_pointer) {
289-
Dart_NewNativePort_ = function_pointer;
290-
}
291-
292-
typedef bool (*Dart_CloseNativePortType)(Dart_Port native_port_id);
293-
Dart_CloseNativePortType Dart_CloseNativePort_ = nullptr;
294-
295-
DART_EXPORT void RegisterDart_CloseNativePort(
296-
Dart_CloseNativePortType function_pointer) {
297-
Dart_CloseNativePort_ = function_pointer;
275+
// Initialize `dart_api_dl.h`
276+
DART_EXPORT intptr_t InitDartApiDL(void* data) {
277+
return Dart_InitializeApiDL(data);
298278
}
299279

300280
////////////////////////////////////////////////////////////////////////////////
@@ -342,7 +322,7 @@ void NotifyDart(Dart_Port send_port, const Work* work) {
342322
dart_object.type = Dart_CObject_kInt64;
343323
dart_object.value.as_int64 = work_addr;
344324

345-
const bool result = Dart_PostCObject_(send_port, &dart_object);
325+
const bool result = Dart_PostCObject_DL(send_port, &dart_object);
346326
if (!result) {
347327
FATAL("C : Posting message to port failed.");
348328
}
@@ -504,16 +484,16 @@ class PendingCall {
504484
PendingCall(void** buffer, size_t* length)
505485
: response_buffer_(buffer), response_length_(length) {
506486
receive_port_ =
507-
Dart_NewNativePort_("cpp-response", &PendingCall::HandleResponse,
508-
/*handle_concurrently=*/false);
487+
Dart_NewNativePort_DL("cpp-response", &PendingCall::HandleResponse,
488+
/*handle_concurrently=*/false);
509489
}
510-
~PendingCall() { Dart_CloseNativePort_(receive_port_); }
490+
~PendingCall() { Dart_CloseNativePort_DL(receive_port_); }
511491

512492
Dart_Port port() const { return receive_port_; }
513493

514494
void PostAndWait(Dart_Port port, Dart_CObject* object) {
515495
std::unique_lock<std::mutex> lock(mutex);
516-
const bool success = Dart_PostCObject_(send_port_, object);
496+
const bool success = Dart_PostCObject_DL(send_port_, object);
517497
if (!success) FATAL("Failed to send message, invalid port or isolate died");
518498

519499
printf("C : Waiting for result.\n");
@@ -668,7 +648,7 @@ void MyCallback2(uint8_t a) {
668648
printf("C : Dart_PostCObject_(request: %" Px ", call: %" Px ").\n",
669649
reinterpret_cast<intptr_t>(&c_request),
670650
reinterpret_cast<intptr_t>(&c_pending_call));
671-
Dart_PostCObject_(send_port_, &c_request);
651+
Dart_PostCObject_DL(send_port_, &c_request);
672652
}
673653

674654
// Simulated work for Thread #1.
@@ -793,7 +773,8 @@ DART_EXPORT void ThreadPoolTest_BarrierSync(
793773
////////////////////////////////////////////////////////////////////////////////
794774
// Functions for handle tests.
795775
//
796-
// vmspecific_handle_test.dart
776+
// vmspecific_handle_test.dart (statically linked).
777+
// vmspecific_handle_dynamically_linked_test.dart (dynamically linked).
797778

798779
static void RunFinalizer(void* isolate_callback_data,
799780
Dart_WeakPersistentHandle handle,
@@ -912,4 +893,46 @@ DART_EXPORT Dart_Handle TrueHandle() {
912893
return Dart_True();
913894
}
914895

896+
DART_EXPORT Dart_Handle PassObjectToCUseDynamicLinking(Dart_Handle h) {
897+
auto persistent_handle = Dart_NewPersistentHandle_DL(h);
898+
899+
Dart_Handle handle_2 = Dart_HandleFromPersistent_DL(persistent_handle);
900+
Dart_SetPersistentHandle_DL(persistent_handle, h);
901+
Dart_DeletePersistentHandle_DL(persistent_handle);
902+
903+
auto weak_handle = Dart_NewWeakPersistentHandle_DL(
904+
handle_2, reinterpret_cast<void*>(0x1234), 64, RunFinalizer);
905+
Dart_Handle return_value = Dart_HandleFromWeakPersistent_DL(weak_handle);
906+
907+
Dart_DeleteWeakPersistentHandle_DL(weak_handle);
908+
909+
return return_value;
910+
}
911+
912+
////////////////////////////////////////////////////////////////////////////////
913+
// Example for doing closure callbacks with help of `dart_api.h`.
914+
//
915+
// sample_ffi_functions_callbacks_closures.dart
916+
917+
void (*callback_)(Dart_Handle);
918+
Dart_PersistentHandle closure_to_callback_;
919+
920+
DART_EXPORT void RegisterClosureCallbackFP(void (*callback)(Dart_Handle)) {
921+
callback_ = callback;
922+
}
923+
924+
DART_EXPORT void RegisterClosureCallback(Dart_Handle h) {
925+
closure_to_callback_ = Dart_NewPersistentHandle_DL(h);
926+
}
927+
928+
DART_EXPORT void InvokeClosureCallback() {
929+
Dart_Handle closure_handle =
930+
Dart_HandleFromPersistent_DL(closure_to_callback_);
931+
callback_(closure_handle);
932+
}
933+
934+
DART_EXPORT void ReleaseClosureCallback() {
935+
Dart_DeletePersistentHandle_DL(closure_to_callback_);
936+
}
937+
915938
} // namespace dart

runtime/include/dart_api.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ typedef struct _Dart_IsolateGroup* Dart_IsolateGroup;
243243
typedef struct _Dart_Handle* Dart_Handle;
244244
typedef Dart_Handle Dart_PersistentHandle;
245245
typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle;
246+
// These three structs are versioned by DART_API_DL_MAJOR_VERSION, bump the
247+
// version when changing this struct.
246248

247249
typedef void (*Dart_WeakPersistentHandleFinalizer)(
248250
void* isolate_callback_data,

runtime/include/dart_api_dl.cc

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
3+
* for details. All rights reserved. Use of this source code is governed by a
4+
* BSD-style license that can be found in the LICENSE file.
5+
*/
6+
7+
#include "include/dart_api_dl.h"
8+
#include "include/dart_version.h"
9+
#include "include/internal/dart_api_dl_impl.h"
10+
11+
#include <string.h>
12+
13+
#define DART_API_DL_DEFINITIONS(name) \
14+
using name##Type = decltype(&name); \
15+
name##Type name##_DL = nullptr;
16+
DART_API_ALL_DL_SYMBOLS(DART_API_DL_DEFINITIONS)
17+
#undef DART_API_DL_DEFINITIONS
18+
19+
typedef void (*DartApiEntry_function)();
20+
21+
DartApiEntry_function FindFunctionPointer(const DartApiEntry* entries,
22+
const char* name) {
23+
while (entries->name != nullptr) {
24+
if (strcmp(entries->name, name) == 0) return entries->function;
25+
entries++;
26+
}
27+
return nullptr;
28+
}
29+
30+
intptr_t Dart_InitializeApiDL(void* data) {
31+
DartApi* dart_api_data = reinterpret_cast<DartApi*>(data);
32+
33+
if (dart_api_data->major != DART_API_DL_MAJOR_VERSION) {
34+
// If the DartVM we're running on does not have the same version as this
35+
// file was compiled against, refuse to initialize. The symbols are not
36+
// compatible.
37+
return -1;
38+
}
39+
// Minor versions are allowed to be different.
40+
// If the DartVM has a higher minor version, it will provide more symbols
41+
// than we initialize here.
42+
// If the DartVM has a lower minor version, it will not provide all symbols.
43+
// In that case, we leave the missing symbols un-initialized. Those symbols
44+
// should not be used by the Dart and native code. The client is responsible
45+
// for checking the minor version number himself based on which symbols it
46+
// is using.
47+
// (If we would error out on this case, recompiling native code against a
48+
// newer SDK would break all uses on older SDKs, which is too strict.)
49+
50+
const DartApiEntry* dart_api_function_pointers = dart_api_data->functions;
51+
52+
#define DART_API_DL_INIT(name) \
53+
name##_DL = reinterpret_cast<name##Type>( \
54+
FindFunctionPointer(dart_api_function_pointers, #name));
55+
DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT)
56+
#undef DART_API_DL_INIT
57+
58+
return 0;
59+
}

0 commit comments

Comments
 (0)