Skip to content

Commit c1467ab

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Change asFunction and lookFunction to extension methods
This prevents them from being called dynamically. Moreover, it prevents asFunction from being called on a non-NativeFunction type argument, simplifying the amount of manual checks. Note that this CL had to change the CFE and analzyer, and their tests (including mock_sdk) as well. This can potentially be a breaking change, as the extension methods are only visible when `dart:ffi` is imported, while methods on objects are always visible. Issue: #35903 Change-Id: I1e291f154228d5d9a34b21a022088bf493f6557d 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-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,analyzer-nnbd-linux-release-try,dart2js-nnbd-linux-x64-chrome-try,ddc-nnbd-linux-release-chrome-try,front-end-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-nnbd-linux-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135463 Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent eceb249 commit c1467ab

17 files changed

+128
-116
lines changed

pkg/analyzer/lib/src/generated/ffi_verifier.dart

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,17 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
126126
Element enclosingElement = element.enclosingElement;
127127
if (enclosingElement is ClassElement) {
128128
if (_isPointer(enclosingElement)) {
129+
if (element.name == 'fromFunction') {
130+
_validateFromFunction(node, element);
131+
}
132+
}
133+
}
134+
if (enclosingElement is ExtensionElement) {
135+
if (_isNativeFunctionPointerExtension(enclosingElement)) {
129136
if (element.name == 'asFunction') {
130137
_validateAsFunction(node, element);
131-
} else if (element.name == 'fromFunction') {
132-
_validateFromFunction(node, element);
133138
}
134-
} else if (_isDynamicLibrary(enclosingElement) &&
139+
} else if (_isDynamicLibraryExtension(enclosingElement) &&
135140
element.name == 'lookupFunction') {
136141
_validateLookupFunction(node);
137142
}
@@ -152,10 +157,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
152157
return element is ClassElement && element.library.name == 'dart.ffi';
153158
}
154159

155-
/// Return `true` if the given [element] represents the class
156-
/// `DynamicLibrary`.
157-
bool _isDynamicLibrary(ClassElement element) =>
158-
element.name == 'DynamicLibrary' && element.library.name == 'dart.ffi';
160+
/// Return `true` if the given [element] represents the extension
161+
/// `LibraryExtension`.
162+
bool _isDynamicLibraryExtension(Element element) =>
163+
element.name == 'LibraryExtension' && element.library.name == 'dart.ffi';
159164

160165
/// Returns `true` iff [nativeType] is a `ffi.NativeFunction<???>` type.
161166
bool _isNativeFunctionInterfaceType(DartType nativeType) {
@@ -184,6 +189,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
184189
bool _isPointer(Element element) =>
185190
element.name == 'Pointer' && element.library.name == 'dart.ffi';
186191

192+
bool _isNativeFunctionPointerExtension(Element element) =>
193+
element.name == 'NativeFunctionPointer' &&
194+
element.library.name == 'dart.ffi';
195+
187196
/// Returns `true` iff [nativeType] is a `ffi.Pointer<???>` type.
188197
bool _isPointerInterfaceType(DartType nativeType) {
189198
if (nativeType is InterfaceType) {

pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,13 +532,19 @@ class Pointer<T extends NativeType> extends NativeType {
532532
static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
533533
@DartRepresentationOf("T") Function f,
534534
[Object exceptionalReturn]);
535-
R asFunction<@DartRepresentationOf("T") R extends Function>();
535+
}
536+
extension NativeFunctionPointer<NF extends Function>
537+
on Pointer<NativeFunction<NF>> {
538+
external DF asFunction<DF extends Function>();
536539
}
537540
class Struct extends NativeType {}
538541
539-
abstract class DynamicLibrary {
540-
F lookupFunction<T extends Function, F extends Function>(String symbolName);
542+
abstract class DynamicLibrary {}
543+
extension LibraryExtension on DynamicLibrary {
544+
external F lookupFunction<T extends Function, F extends Function>(
545+
String symbolName);
541546
}
547+
542548
abstract class NativeFunction<T extends Function> extends NativeType {}
543549
544550
class DartRepresentationOf {

pkg/analyzer/test/src/diagnostics/non_native_function_type_argument_to_pointer_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analyzer/src/dart/error/ffi_code.dart';
6+
import 'package:analyzer/src/error/codes.dart';
67
import 'package:test_reflective_loader/test_reflective_loader.dart';
78

89
import '../dart/resolution/driver_resolution.dart';
@@ -25,7 +26,9 @@ class C {
2526
}
2627
}
2728
''', [
28-
error(FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER, 109, 1),
29+
// This changed from a method to a extension method, uses Dart semantics
30+
// instead of manual check now.
31+
error(StaticTypeWarningCode.UNDEFINED_METHOD, 98, 10),
2932
]);
3033
}
3134

pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ class C extends Int8 {}
7777
import 'dart:ffi';
7878
class C extends Pointer {}
7979
''', [
80-
// TODO(brianwilkerson) The following diagnostic should not be generated.
81-
error(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE,
82-
25, 1),
8380
error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS, 35, 7),
8481
]);
8582
}
@@ -198,9 +195,6 @@ class C implements Int8 {}
198195
import 'dart:ffi';
199196
class C implements Pointer {}
200197
''', [
201-
// TODO(brianwilkerson) The following diagnostic should not be generated.
202-
error(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE,
203-
25, 1),
204198
error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS, 38, 7),
205199
]);
206200
}
@@ -333,9 +327,6 @@ class C with Int8 {}
333327
import 'dart:ffi';
334328
class C with Pointer {}
335329
''', [
336-
// TODO(brianwilkerson) The following diagnostic should not be generated.
337-
error(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE,
338-
25, 1),
339330
error(CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, 32, 7),
340331
error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_WITH, 32, 7),
341332
]);

pkg/vm/lib/transformations/ffi.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,12 @@ class FfiTransformer extends Transformer {
234234
addressOfField = index.getMember('dart:ffi', 'Struct', '_addressOf'),
235235
structFromPointer =
236236
index.getMember('dart:ffi', 'Struct', '_fromPointer'),
237-
asFunctionMethod = index.getMember('dart:ffi', 'Pointer', 'asFunction'),
237+
asFunctionMethod =
238+
index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'),
238239
asFunctionInternal =
239240
index.getTopLevelMember('dart:ffi', '_asFunctionInternal'),
240241
lookupFunctionMethod =
241-
index.getMember('dart:ffi', 'DynamicLibrary', 'lookupFunction'),
242+
index.getMember('dart:ffi', 'LibraryExtension', 'lookupFunction'),
242243
fromFunctionMethod =
243244
index.getMember('dart:ffi', 'Pointer', 'fromFunction'),
244245
libraryLookupMethod =

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,30 @@ class _FfiUseSiteTransformer extends FfiTransformer {
153153

154154
final Member target = node.target;
155155
try {
156-
if (target == fromFunctionMethod) {
156+
if (target == lookupFunctionMethod && !isFfiLibrary) {
157+
final DartType nativeType = InterfaceType(
158+
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
159+
final DartType dartType = node.arguments.types[1];
160+
161+
_ensureNativeTypeValid(nativeType, node);
162+
_ensureNativeTypeToDartType(nativeType, dartType, node);
163+
return _replaceLookupFunction(node);
164+
} else if (target == asFunctionMethod && !isFfiLibrary) {
165+
final DartType dartType = node.arguments.types[1];
166+
final DartType nativeType = InterfaceType(
167+
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
168+
169+
_ensureNativeTypeValid(nativeType, node);
170+
_ensureNativeTypeToDartType(nativeType, dartType, node);
171+
172+
final DartType nativeSignature =
173+
(nativeType as InterfaceType).typeArguments[0];
174+
// Inline function body to make all type arguments instatiated.
175+
return StaticInvocation(
176+
asFunctionInternal,
177+
Arguments([node.arguments.positional[0]],
178+
types: [dartType, nativeSignature]));
179+
} else if (target == fromFunctionMethod) {
157180
final DartType nativeType = InterfaceType(
158181
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
159182
final Expression func = node.arguments.positional[0];
@@ -246,15 +269,10 @@ class _FfiUseSiteTransformer extends FfiTransformer {
246269
// We need to replace calls to 'DynamicLibrary.lookupFunction' with explicit
247270
// Kernel, because we cannot have a generic call to 'asFunction' in its body.
248271
//
249-
// Below, in 'visitMethodInvocation', we ensure that the type arguments to
272+
// Above, in 'visitStaticInvocation', we ensure that the type arguments to
250273
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
251274
// the call-site, we ensure that there are no generic calls to 'asFunction'.
252-
//
253-
// We will not detect dynamic invocations of 'asFunction' and
254-
// 'lookupFunction': these are handled by the stubs in 'ffi_patch.dart' and
255-
// 'dynamic_library_patch.dart'. Dynamic invocations of 'lookupFunction' (and
256-
// 'asFunction') are not legal and throw a runtime exception.
257-
Expression _replaceLookupFunction(MethodInvocation node) {
275+
Expression _replaceLookupFunction(StaticInvocation node) {
258276
// The generated code looks like:
259277
//
260278
// _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName))
@@ -263,13 +281,16 @@ class _FfiUseSiteTransformer extends FfiTransformer {
263281
final DartType dartSignature = node.arguments.types[1];
264282

265283
final Arguments args = Arguments([
266-
node.arguments.positional.single
284+
node.arguments.positional[1]
267285
], types: [
268286
InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeSignature])
269287
]);
270288

271289
final Expression lookupResult = MethodInvocation(
272-
node.receiver, Name("lookup"), args, libraryLookupMethod);
290+
node.arguments.positional[0],
291+
Name("lookup"),
292+
args,
293+
libraryLookupMethod);
273294

274295
return StaticInvocation(asFunctionInternal,
275296
Arguments([lookupResult], types: [dartSignature, nativeSignature]));
@@ -318,35 +339,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
318339

319340
final Member target = node.interfaceTarget;
320341
try {
321-
// We will not detect dynamic invocations of 'asFunction' and
322-
// 'lookupFunction' -- these are handled by the 'asFunctionInternal' stub
323-
// in 'dynamic_library_patch.dart'. Dynamic invocations of 'asFunction'
324-
// and 'lookupFunction' are not legal and throw a runtime exception.
325-
if (target == lookupFunctionMethod) {
326-
final DartType nativeType = InterfaceType(
327-
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
328-
final DartType dartType = node.arguments.types[1];
329-
330-
_ensureNativeTypeValid(nativeType, node);
331-
_ensureNativeTypeToDartType(nativeType, dartType, node);
332-
return _replaceLookupFunction(node);
333-
} else if (target == asFunctionMethod) {
334-
final DartType dartType = node.arguments.types[0];
335-
final DartType pointerType =
336-
node.receiver.getStaticType(_staticTypeContext);
337-
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
338-
339-
_ensureNativeTypeValid(pointerType, node);
340-
_ensureNativeTypeValid(nativeType, node);
341-
_ensureNativeTypeToDartType(nativeType, dartType, node);
342-
343-
final DartType nativeSignature =
344-
(nativeType as InterfaceType).typeArguments[0];
345-
return StaticInvocation(asFunctionInternal,
346-
Arguments([node.receiver], types: [dartType, nativeSignature]));
347-
} else if (target == elementAtMethod) {
348-
// TODO(37773): When moving to extension methods we can get rid of
349-
// this rewiring.
342+
if (target == elementAtMethod) {
350343
final DartType pointerType =
351344
node.receiver.getStaticType(_staticTypeContext);
352345
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);

runtime/vm/compiler/frontend/base_flow_graph_builder.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,8 @@ Fragment BaseFlowGraphBuilder::AllocateObject(TokenPosition position,
884884

885885
Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
886886
const TypeArguments& signatures) {
887-
ASSERT(signatures.IsInstantiated() && signatures.Length() == 2);
887+
ASSERT(signatures.IsInstantiated());
888+
ASSERT(signatures.Length() == 2);
888889

889890
const AbstractType& dart_type = AbstractType::Handle(signatures.TypeAt(0));
890891
const AbstractType& native_type = AbstractType::Handle(signatures.TypeAt(1));

sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,6 @@ class DynamicLibrary {
3232
Pointer<T> lookup<T extends NativeType>(String symbolName)
3333
native "Ffi_dl_lookup";
3434

35-
// The real implementation of this function lives in FfiUseSiteTransformer
36-
// for interface calls. Only dynamic calls (which are illegal) reach this
37-
// implementation.
38-
@patch
39-
F lookupFunction<T extends Function, F extends Function>(String symbolName) {
40-
throw UnsupportedError(
41-
"Dynamic invocation of lookupFunction is not supported.");
42-
}
43-
4435
// TODO(dacoharkes): Expose this to users, or extend Pointer?
4536
// https://github.com/dart-lang/sdk/issues/35881
4637
int getHandle() native "Ffi_dl_getHandle";
@@ -59,3 +50,10 @@ class DynamicLibrary {
5950
@patch
6051
Pointer<Void> get handle => Pointer.fromAddress(getHandle());
6152
}
53+
54+
extension LibraryExtension on DynamicLibrary {
55+
@patch
56+
DS lookupFunction<NS extends Function, DS extends Function>(
57+
String symbolName) =>
58+
throw UnsupportedError("The body is inlined in the frontend.");
59+
}

sdk/lib/_internal/vm/lib/ffi_patch.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,6 @@ class Pointer<T extends NativeType> {
106106

107107
@patch
108108
Pointer<U> cast<U extends NativeType>() => Pointer.fromAddress(address);
109-
110-
@patch
111-
R asFunction<R extends Function>() {
112-
throw UnsupportedError("Pointer.asFunction cannot be called dynamically.");
113-
}
114109
}
115110

116111
/// Returns an integer encoding the ABI used for size and alignment
@@ -229,6 +224,13 @@ Pointer<Pointer<S>> _elementAtPointer<S extends NativeType>(
229224
Pointer<Pointer<S>> pointer, int index) =>
230225
Pointer.fromAddress(pointer.address + _intPtrSize * index);
231226

227+
extension NativeFunctionPointer<NF extends Function>
228+
on Pointer<NativeFunction<NF>> {
229+
@patch
230+
DF asFunction<DF extends Function>() =>
231+
throw UnsupportedError("The body is inlined in the frontend.");
232+
}
233+
232234
//
233235
// The following code is generated, do not edit by hand.
234236
//

sdk/lib/ffi/dynamic_library.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ class DynamicLibrary {
3030
/// Throws an [ArgumentError] if it fails to lookup the symbol.
3131
external Pointer<T> lookup<T extends NativeType>(String symbolName);
3232

33-
/// Helper that combines lookup and cast to a Dart function.
34-
external F lookupFunction<T extends Function, F extends Function>(
35-
String symbolName);
36-
3733
/// Dynamic libraries are equal if they load the same library.
3834
external bool operator ==(other);
3935

@@ -43,3 +39,10 @@ class DynamicLibrary {
4339
/// The handle to the dynamic library.
4440
external Pointer<Void> get handle;
4541
}
42+
43+
/// Methods which cannot be invoked dynamically.
44+
extension LibraryExtension on DynamicLibrary {
45+
/// Helper that combines lookup and cast to a Dart function.
46+
external F lookupFunction<T extends Function, F extends Function>(
47+
String symbolName);
48+
}

sdk/lib/ffi/ffi.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,6 @@ class Pointer<T extends NativeType> extends NativeType {
6868
/// Cast Pointer<T> to a Pointer<V>.
6969
external Pointer<U> cast<U extends NativeType>();
7070

71-
/// Convert to Dart function, automatically marshalling the arguments
72-
/// and return value.
73-
///
74-
/// Can only be called on [Pointer]<[NativeFunction]>. Does not accept dynamic
75-
/// invocations -- where the type of the receiver is [dynamic].
76-
external R asFunction<@DartRepresentationOf("T") R extends Function>();
77-
7871
/// Equality for Pointers only depends on their address.
7972
bool operator ==(other) {
8073
if (other == null) return false;
@@ -87,6 +80,14 @@ class Pointer<T extends NativeType> extends NativeType {
8780
}
8881
}
8982

83+
/// Extension on [Pointer] specialized for the type argument [NativeFunction].
84+
extension NativeFunctionPointer<NF extends Function>
85+
on Pointer<NativeFunction<NF>> {
86+
/// Convert to Dart function, automatically marshalling the arguments
87+
/// and return value.
88+
external DF asFunction<@DartRepresentationOf("NF") DF extends Function>();
89+
}
90+
9091
//
9192
// The following code is generated, do not edit by hand.
9293
//

sdk_nnbd/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,6 @@ class DynamicLibrary {
3030
Pointer<T> lookup<T extends NativeType>(String symbolName)
3131
native "Ffi_dl_lookup";
3232

33-
// The real implementation of this function lives in FfiUseSiteTransformer
34-
// for interface calls. Only dynamic calls (which are illegal) reach this
35-
// implementation.
36-
@patch
37-
F lookupFunction<T extends Function, F extends Function>(String symbolName) {
38-
throw UnsupportedError(
39-
"Dynamic invocation of lookupFunction is not supported.");
40-
}
41-
4233
// TODO(dacoharkes): Expose this to users, or extend Pointer?
4334
// https://github.com/dart-lang/sdk/issues/35881
4435
int getHandle() native "Ffi_dl_getHandle";
@@ -58,3 +49,10 @@ class DynamicLibrary {
5849
@patch
5950
Pointer<Void> get handle => Pointer.fromAddress(getHandle());
6051
}
52+
53+
extension LibraryExtension on DynamicLibrary {
54+
@patch
55+
DS lookupFunction<NS extends Function, DS extends Function>(
56+
String symbolName) =>
57+
throw UnsupportedError("The body is inlined in the frontend.");
58+
}

0 commit comments

Comments
 (0)