From c1467ab5d32705a08d79af79724382c13abb6426 Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Fri, 14 Feb 2020 15:46:37 +0000 Subject: [PATCH] [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: https://github.com/dart-lang/sdk/issues/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 Reviewed-by: Martin Kustermann --- .../lib/src/generated/ffi_verifier.dart | 25 ++++--- .../lib/src/test_utilities/mock_sdk.dart | 12 +++- ...unction_type_argument_to_pointer_test.dart | 5 +- .../subtype_of_ffi_class_test.dart | 9 --- pkg/vm/lib/transformations/ffi.dart | 5 +- pkg/vm/lib/transformations/ffi_use_sites.dart | 71 +++++++++---------- .../frontend/base_flow_graph_builder.cc | 3 +- .../vm/lib/ffi_dynamic_library_patch.dart | 16 ++--- sdk/lib/_internal/vm/lib/ffi_patch.dart | 12 ++-- sdk/lib/ffi/dynamic_library.dart | 11 +-- sdk/lib/ffi/ffi.dart | 15 ++-- .../vm/lib/ffi_dynamic_library_patch.dart | 16 ++--- sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart | 12 ++-- sdk_nnbd/lib/ffi/dynamic_library.dart | 11 +-- sdk_nnbd/lib/ffi/ffi.dart | 15 ++-- tests/ffi/negative_function_test.dart | 4 +- tests/ffi_2/negative_function_test.dart | 4 +- 17 files changed, 129 insertions(+), 117 deletions(-) diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart index 70b6e5af355..86bf650a2e4 100644 --- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart +++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart @@ -126,12 +126,17 @@ class FfiVerifier extends RecursiveAstVisitor { Element enclosingElement = element.enclosingElement; if (enclosingElement is ClassElement) { if (_isPointer(enclosingElement)) { - if (element.name == 'asFunction') { - _validateAsFunction(node, element); - } else if (element.name == 'fromFunction') { + if (element.name == 'fromFunction') { _validateFromFunction(node, element); } - } else if (_isDynamicLibrary(enclosingElement) && + } + } + if (enclosingElement is ExtensionElement) { + if (_isNativeFunctionPointerExtension(enclosingElement)) { + if (element.name == 'asFunction') { + _validateAsFunction(node, element); + } + } else if (_isDynamicLibraryExtension(enclosingElement) && element.name == 'lookupFunction') { _validateLookupFunction(node); } @@ -152,10 +157,10 @@ class FfiVerifier extends RecursiveAstVisitor { return element is ClassElement && element.library.name == 'dart.ffi'; } - /// Return `true` if the given [element] represents the class - /// `DynamicLibrary`. - bool _isDynamicLibrary(ClassElement element) => - element.name == 'DynamicLibrary' && element.library.name == 'dart.ffi'; + /// Return `true` if the given [element] represents the extension + /// `LibraryExtension`. + bool _isDynamicLibraryExtension(Element element) => + element.name == 'LibraryExtension' && element.library.name == 'dart.ffi'; /// Returns `true` iff [nativeType] is a `ffi.NativeFunction` type. bool _isNativeFunctionInterfaceType(DartType nativeType) { @@ -184,6 +189,10 @@ class FfiVerifier extends RecursiveAstVisitor { bool _isPointer(Element element) => element.name == 'Pointer' && element.library.name == 'dart.ffi'; + bool _isNativeFunctionPointerExtension(Element element) => + element.name == 'NativeFunctionPointer' && + element.library.name == 'dart.ffi'; + /// Returns `true` iff [nativeType] is a `ffi.Pointer` type. bool _isPointerInterfaceType(DartType nativeType) { if (nativeType is InterfaceType) { diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart index 39af3207366..4367530df28 100644 --- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart +++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart @@ -532,13 +532,19 @@ class Pointer extends NativeType { static Pointer> fromFunction( @DartRepresentationOf("T") Function f, [Object exceptionalReturn]); - R asFunction<@DartRepresentationOf("T") R extends Function>(); +} +extension NativeFunctionPointer + on Pointer> { + external DF asFunction(); } class Struct extends NativeType {} -abstract class DynamicLibrary { - F lookupFunction(String symbolName); +abstract class DynamicLibrary {} +extension LibraryExtension on DynamicLibrary { + external F lookupFunction( + String symbolName); } + abstract class NativeFunction extends NativeType {} class DartRepresentationOf { diff --git a/pkg/analyzer/test/src/diagnostics/non_native_function_type_argument_to_pointer_test.dart b/pkg/analyzer/test/src/diagnostics/non_native_function_type_argument_to_pointer_test.dart index 092885091a7..576cbdbdccf 100644 --- a/pkg/analyzer/test/src/diagnostics/non_native_function_type_argument_to_pointer_test.dart +++ b/pkg/analyzer/test/src/diagnostics/non_native_function_type_argument_to_pointer_test.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analyzer/src/dart/error/ffi_code.dart'; +import 'package:analyzer/src/error/codes.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import '../dart/resolution/driver_resolution.dart'; @@ -25,7 +26,9 @@ class C { } } ''', [ - error(FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER, 109, 1), + // This changed from a method to a extension method, uses Dart semantics + // instead of manual check now. + error(StaticTypeWarningCode.UNDEFINED_METHOD, 98, 10), ]); } diff --git a/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart b/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart index e13d2249c41..504a6f18623 100644 --- a/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart +++ b/pkg/analyzer/test/src/diagnostics/subtype_of_ffi_class_test.dart @@ -77,9 +77,6 @@ class C extends Int8 {} import 'dart:ffi'; class C extends Pointer {} ''', [ - // TODO(brianwilkerson) The following diagnostic should not be generated. - error(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE, - 25, 1), error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS, 35, 7), ]); } @@ -198,9 +195,6 @@ class C implements Int8 {} import 'dart:ffi'; class C implements Pointer {} ''', [ - // TODO(brianwilkerson) The following diagnostic should not be generated. - error(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE, - 25, 1), error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS, 38, 7), ]); } @@ -333,9 +327,6 @@ class C with Int8 {} import 'dart:ffi'; class C with Pointer {} ''', [ - // TODO(brianwilkerson) The following diagnostic should not be generated. - error(StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE, - 25, 1), error(CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, 32, 7), error(FfiCode.SUBTYPE_OF_FFI_CLASS_IN_WITH, 32, 7), ]); diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart index ba1fe7889ec..dcc9c5fa71b 100644 --- a/pkg/vm/lib/transformations/ffi.dart +++ b/pkg/vm/lib/transformations/ffi.dart @@ -234,11 +234,12 @@ class FfiTransformer extends Transformer { addressOfField = index.getMember('dart:ffi', 'Struct', '_addressOf'), structFromPointer = index.getMember('dart:ffi', 'Struct', '_fromPointer'), - asFunctionMethod = index.getMember('dart:ffi', 'Pointer', 'asFunction'), + asFunctionMethod = + index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'), asFunctionInternal = index.getTopLevelMember('dart:ffi', '_asFunctionInternal'), lookupFunctionMethod = - index.getMember('dart:ffi', 'DynamicLibrary', 'lookupFunction'), + index.getMember('dart:ffi', 'LibraryExtension', 'lookupFunction'), fromFunctionMethod = index.getMember('dart:ffi', 'Pointer', 'fromFunction'), libraryLookupMethod = diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart index 17f7f699cd8..9ac20c8e7f8 100644 --- a/pkg/vm/lib/transformations/ffi_use_sites.dart +++ b/pkg/vm/lib/transformations/ffi_use_sites.dart @@ -153,7 +153,30 @@ class _FfiUseSiteTransformer extends FfiTransformer { final Member target = node.target; try { - if (target == fromFunctionMethod) { + if (target == lookupFunctionMethod && !isFfiLibrary) { + 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) { + final DartType dartType = node.arguments.types[1]; + final DartType nativeType = InterfaceType( + nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]); + + _ensureNativeTypeValid(nativeType, node); + _ensureNativeTypeToDartType(nativeType, dartType, node); + + final DartType nativeSignature = + (nativeType as InterfaceType).typeArguments[0]; + // Inline function body to make all type arguments instatiated. + return StaticInvocation( + asFunctionInternal, + Arguments([node.arguments.positional[0]], + types: [dartType, nativeSignature])); + } else if (target == fromFunctionMethod) { final DartType nativeType = InterfaceType( nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]); final Expression func = node.arguments.positional[0]; @@ -246,15 +269,10 @@ class _FfiUseSiteTransformer extends FfiTransformer { // We need to replace calls to 'DynamicLibrary.lookupFunction' with explicit // Kernel, because we cannot have a generic call to 'asFunction' in its body. // - // Below, in 'visitMethodInvocation', we ensure that the type arguments to + // Above, in 'visitStaticInvocation', we ensure that the type arguments to // 'lookupFunction' are constants, so by inlining the call to 'asFunction' at // the call-site, we ensure that there are no generic calls to 'asFunction'. - // - // We will not detect dynamic invocations of 'asFunction' and - // 'lookupFunction': these are handled by the stubs in 'ffi_patch.dart' and - // 'dynamic_library_patch.dart'. Dynamic invocations of 'lookupFunction' (and - // 'asFunction') are not legal and throw a runtime exception. - Expression _replaceLookupFunction(MethodInvocation node) { + Expression _replaceLookupFunction(StaticInvocation node) { // The generated code looks like: // // _asFunctionInternal(lookup>(symbolName)) @@ -263,13 +281,16 @@ class _FfiUseSiteTransformer extends FfiTransformer { final DartType dartSignature = node.arguments.types[1]; final Arguments args = Arguments([ - node.arguments.positional.single + node.arguments.positional[1] ], types: [ InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeSignature]) ]); final Expression lookupResult = MethodInvocation( - node.receiver, Name("lookup"), args, libraryLookupMethod); + node.arguments.positional[0], + Name("lookup"), + args, + libraryLookupMethod); return StaticInvocation(asFunctionInternal, Arguments([lookupResult], types: [dartSignature, nativeSignature])); @@ -318,35 +339,7 @@ class _FfiUseSiteTransformer extends FfiTransformer { final Member target = node.interfaceTarget; try { - // We will not detect dynamic invocations of 'asFunction' and - // 'lookupFunction' -- these are handled by the 'asFunctionInternal' stub - // in 'dynamic_library_patch.dart'. Dynamic invocations of 'asFunction' - // and 'lookupFunction' are not legal and throw a runtime exception. - 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) { - final DartType dartType = node.arguments.types[0]; - final DartType pointerType = - node.receiver.getStaticType(_staticTypeContext); - final DartType nativeType = _pointerTypeGetTypeArg(pointerType); - - _ensureNativeTypeValid(pointerType, node); - _ensureNativeTypeValid(nativeType, node); - _ensureNativeTypeToDartType(nativeType, dartType, node); - - final DartType nativeSignature = - (nativeType as InterfaceType).typeArguments[0]; - return StaticInvocation(asFunctionInternal, - Arguments([node.receiver], types: [dartType, nativeSignature])); - } else if (target == elementAtMethod) { - // TODO(37773): When moving to extension methods we can get rid of - // this rewiring. + if (target == elementAtMethod) { final DartType pointerType = node.receiver.getStaticType(_staticTypeContext); final DartType nativeType = _pointerTypeGetTypeArg(pointerType); diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc index 958e62680d7..494b96dc2c7 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc @@ -884,7 +884,8 @@ Fragment BaseFlowGraphBuilder::AllocateObject(TokenPosition position, Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall( const TypeArguments& signatures) { - ASSERT(signatures.IsInstantiated() && signatures.Length() == 2); + ASSERT(signatures.IsInstantiated()); + ASSERT(signatures.Length() == 2); const AbstractType& dart_type = AbstractType::Handle(signatures.TypeAt(0)); const AbstractType& native_type = AbstractType::Handle(signatures.TypeAt(1)); diff --git a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart index b616ffc0a76..f6af77194bf 100644 --- a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart @@ -32,15 +32,6 @@ class DynamicLibrary { Pointer lookup(String symbolName) native "Ffi_dl_lookup"; - // The real implementation of this function lives in FfiUseSiteTransformer - // for interface calls. Only dynamic calls (which are illegal) reach this - // implementation. - @patch - F lookupFunction(String symbolName) { - throw UnsupportedError( - "Dynamic invocation of lookupFunction is not supported."); - } - // TODO(dacoharkes): Expose this to users, or extend Pointer? // https://github.com/dart-lang/sdk/issues/35881 int getHandle() native "Ffi_dl_getHandle"; @@ -59,3 +50,10 @@ class DynamicLibrary { @patch Pointer get handle => Pointer.fromAddress(getHandle()); } + +extension LibraryExtension on DynamicLibrary { + @patch + DS lookupFunction( + String symbolName) => + throw UnsupportedError("The body is inlined in the frontend."); +} diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart index 33baa8fa966..ecce8080fc5 100644 --- a/sdk/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart @@ -106,11 +106,6 @@ class Pointer { @patch Pointer cast() => Pointer.fromAddress(address); - - @patch - R asFunction() { - throw UnsupportedError("Pointer.asFunction cannot be called dynamically."); - } } /// Returns an integer encoding the ABI used for size and alignment @@ -229,6 +224,13 @@ Pointer> _elementAtPointer( Pointer> pointer, int index) => Pointer.fromAddress(pointer.address + _intPtrSize * index); +extension NativeFunctionPointer + on Pointer> { + @patch + DF asFunction() => + throw UnsupportedError("The body is inlined in the frontend."); +} + // // The following code is generated, do not edit by hand. // diff --git a/sdk/lib/ffi/dynamic_library.dart b/sdk/lib/ffi/dynamic_library.dart index 4640857bd69..bb513adb153 100644 --- a/sdk/lib/ffi/dynamic_library.dart +++ b/sdk/lib/ffi/dynamic_library.dart @@ -30,10 +30,6 @@ class DynamicLibrary { /// Throws an [ArgumentError] if it fails to lookup the symbol. external Pointer lookup(String symbolName); - /// Helper that combines lookup and cast to a Dart function. - external F lookupFunction( - String symbolName); - /// Dynamic libraries are equal if they load the same library. external bool operator ==(other); @@ -43,3 +39,10 @@ class DynamicLibrary { /// The handle to the dynamic library. external Pointer get handle; } + +/// Methods which cannot be invoked dynamically. +extension LibraryExtension on DynamicLibrary { + /// Helper that combines lookup and cast to a Dart function. + external F lookupFunction( + String symbolName); +} diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart index d00b4e715d6..e8bae228502 100644 --- a/sdk/lib/ffi/ffi.dart +++ b/sdk/lib/ffi/ffi.dart @@ -68,13 +68,6 @@ class Pointer extends NativeType { /// Cast Pointer to a Pointer. external Pointer cast(); - /// Convert to Dart function, automatically marshalling the arguments - /// and return value. - /// - /// Can only be called on [Pointer]<[NativeFunction]>. Does not accept dynamic - /// invocations -- where the type of the receiver is [dynamic]. - external R asFunction<@DartRepresentationOf("T") R extends Function>(); - /// Equality for Pointers only depends on their address. bool operator ==(other) { if (other == null) return false; @@ -87,6 +80,14 @@ class Pointer extends NativeType { } } +/// Extension on [Pointer] specialized for the type argument [NativeFunction]. +extension NativeFunctionPointer + on Pointer> { + /// Convert to Dart function, automatically marshalling the arguments + /// and return value. + external DF asFunction<@DartRepresentationOf("NF") DF extends Function>(); +} + // // The following code is generated, do not edit by hand. // diff --git a/sdk_nnbd/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart index 15df7d4053b..c3543b8f7d2 100644 --- a/sdk_nnbd/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart +++ b/sdk_nnbd/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart @@ -30,15 +30,6 @@ class DynamicLibrary { Pointer lookup(String symbolName) native "Ffi_dl_lookup"; - // The real implementation of this function lives in FfiUseSiteTransformer - // for interface calls. Only dynamic calls (which are illegal) reach this - // implementation. - @patch - F lookupFunction(String symbolName) { - throw UnsupportedError( - "Dynamic invocation of lookupFunction is not supported."); - } - // TODO(dacoharkes): Expose this to users, or extend Pointer? // https://github.com/dart-lang/sdk/issues/35881 int getHandle() native "Ffi_dl_getHandle"; @@ -58,3 +49,10 @@ class DynamicLibrary { @patch Pointer get handle => Pointer.fromAddress(getHandle()); } + +extension LibraryExtension on DynamicLibrary { + @patch + DS lookupFunction( + String symbolName) => + throw UnsupportedError("The body is inlined in the frontend."); +} diff --git a/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart index 71706ef6b25..f2942e1000d 100644 --- a/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart @@ -104,11 +104,6 @@ class Pointer { @patch Pointer cast() => Pointer.fromAddress(address); - - @patch - R asFunction() { - throw UnsupportedError("Pointer.asFunction cannot be called dynamically."); - } } /// Returns an integer encoding the ABI used for size and alignment @@ -227,6 +222,13 @@ Pointer> _elementAtPointer( Pointer> pointer, int index) => Pointer.fromAddress(pointer.address + _intPtrSize * index); +extension NativeFunctionPointer + on Pointer> { + @patch + DF asFunction() => + throw UnsupportedError("The body is inlined in the frontend."); +} + // // The following code is generated, do not edit by hand. // diff --git a/sdk_nnbd/lib/ffi/dynamic_library.dart b/sdk_nnbd/lib/ffi/dynamic_library.dart index 76c8f2dedc0..4dbd47faa64 100644 --- a/sdk_nnbd/lib/ffi/dynamic_library.dart +++ b/sdk_nnbd/lib/ffi/dynamic_library.dart @@ -28,10 +28,6 @@ class DynamicLibrary { /// Throws an [ArgumentError] if it fails to lookup the symbol. external Pointer lookup(String symbolName); - /// Helper that combines lookup and cast to a Dart function. - external F lookupFunction( - String symbolName); - /// Dynamic libraries are equal if they load the same library. external bool operator ==(Object other); @@ -41,3 +37,10 @@ class DynamicLibrary { /// The handle to the dynamic library. external Pointer get handle; } + +/// Methods which cannot be invoked dynamically. +extension LibraryExtension on DynamicLibrary { + /// Helper that combines lookup and cast to a Dart function. + external F lookupFunction( + String symbolName); +} diff --git a/sdk_nnbd/lib/ffi/ffi.dart b/sdk_nnbd/lib/ffi/ffi.dart index bf7f6e4e066..0e2d1cb0c8b 100644 --- a/sdk_nnbd/lib/ffi/ffi.dart +++ b/sdk_nnbd/lib/ffi/ffi.dart @@ -66,13 +66,6 @@ class Pointer extends NativeType { /// Cast Pointer to a Pointer. external Pointer cast(); - /// Convert to Dart function, automatically marshalling the arguments - /// and return value. - /// - /// Can only be called on [Pointer]<[NativeFunction]>. Does not accept dynamic - /// invocations -- where the type of the receiver is [dynamic]. - external R asFunction<@DartRepresentationOf("T") R extends Function>(); - /// Equality for Pointers only depends on their address. bool operator ==(Object other) { if (other is! Pointer) return false; @@ -86,6 +79,14 @@ class Pointer extends NativeType { } } +/// Extension on [Pointer] specialized for the type argument [NativeFunction]. +extension NativeFunctionPointer + on Pointer> { + /// Convert to Dart function, automatically marshalling the arguments + /// and return value. + external DF asFunction<@DartRepresentationOf("NF") DF extends Function>(); +} + // // The following code is generated, do not edit by hand. // diff --git a/tests/ffi/negative_function_test.dart b/tests/ffi/negative_function_test.dart index d96c17cb065..7eac37cc47f 100644 --- a/tests/ffi/negative_function_test.dart +++ b/tests/ffi/negative_function_test.dart @@ -68,7 +68,7 @@ void testWrongTypes() { // an exception. void testDynamicAsFunction() { dynamic x = ffi.nullptr.cast>(); - Expect.throwsUnsupportedError(() { + Expect.throwsNoSuchMethodError(() { x.asFunction(); }); } @@ -77,7 +77,7 @@ void testDynamicAsFunction() { // type throws an exception. void testDynamicLookupFunction() { dynamic lib = ffiTestFunctions; - Expect.throwsUnsupportedError(() { + Expect.throwsNoSuchMethodError(() { lib.lookupFunction("_"); }); } diff --git a/tests/ffi_2/negative_function_test.dart b/tests/ffi_2/negative_function_test.dart index d96c17cb065..7eac37cc47f 100644 --- a/tests/ffi_2/negative_function_test.dart +++ b/tests/ffi_2/negative_function_test.dart @@ -68,7 +68,7 @@ void testWrongTypes() { // an exception. void testDynamicAsFunction() { dynamic x = ffi.nullptr.cast>(); - Expect.throwsUnsupportedError(() { + Expect.throwsNoSuchMethodError(() { x.asFunction(); }); } @@ -77,7 +77,7 @@ void testDynamicAsFunction() { // type throws an exception. void testDynamicLookupFunction() { dynamic lib = ffiTestFunctions; - Expect.throwsUnsupportedError(() { + Expect.throwsNoSuchMethodError(() { lib.lookupFunction("_"); }); }