[vm/ffi] Allow omitting native types for `@Native` functions
This change simplifies working with `@Native`-annotated functions by allowing the native type to be omitted when it can be inferred from the Dart function's signature. While this was previously supported for `@Native` fields, it now applies to functions as well.
Before this change, you needed to specify the native type explicitly:
```
@Native<Void Function(Pointer)>()
external void free(Pointer p);
```
After this change, the native type can now be omitted if it's clear from the Dart signature:
```
@Native()
external void free(Pointer p);
```
TEST=tests/ffi/native_assets/*
CoreLibraryReviewExempt: VM only
Closes: https://github.com/dart-lang/sdk/issues/54810
Change-Id: Ied5407fcd2f49d85284cb7817f0c8cad2a73626b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400840
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Moritz Sümmermann <mosum@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index b0cbf2f..5557089 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -6648,6 +6648,18 @@
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeFfiNativeFunctionMissingType =
+ messageFfiNativeFunctionMissingType;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageFfiNativeFunctionMissingType = const MessageCode(
+ "FfiNativeFunctionMissingType",
+ analyzerCodes: <String>["NATIVE_FUNCTION_MISSING_TYPE"],
+ problemMessage:
+ r"""The native type of this function couldn't be inferred so it must be specified in the annotation.""",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiNativeMustBeExternal = messageFfiNativeMustBeExternal;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 9460639..7f7b4a1 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1792,6 +1792,8 @@
status: needsEvaluation
FfiCode.NATIVE_FIELD_NOT_STATIC:
status: needsEvaluation
+FfiCode.NATIVE_FUNCTION_MISSING_TYPE:
+ status: needsEvaluation
FfiCode.NEGATIVE_VARIABLE_DIMENSION:
status: noFix
FfiCode.NON_CONSTANT_TYPE_ARGUMENT:
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
index 255d1f5..fce374c 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
@@ -387,6 +387,16 @@
hasPublishedDocs: true,
);
+ /// No parameters
+ static const FfiCode NATIVE_FUNCTION_MISSING_TYPE = FfiCode(
+ 'NATIVE_FUNCTION_MISSING_TYPE',
+ "The native type of this function couldn't be inferred so it must be "
+ "specified in the annotation.",
+ correctionMessage:
+ "Try adding a type parameter extending `NativeType` to the `@Native` "
+ "annotation.",
+ );
+
/// No parameters.
static const FfiCode NEGATIVE_VARIABLE_DIMENSION = FfiCode(
'NEGATIVE_VARIABLE_DIMENSION',
diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart
index aea94ad..f55471a 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -632,6 +632,7 @@
FfiCode.NATIVE_FIELD_INVALID_TYPE,
FfiCode.NATIVE_FIELD_MISSING_TYPE,
FfiCode.NATIVE_FIELD_NOT_STATIC,
+ FfiCode.NATIVE_FUNCTION_MISSING_TYPE,
FfiCode.NEGATIVE_VARIABLE_DIMENSION,
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index e4a9a53..88b3aa2 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -110,6 +110,9 @@
/// Subclass of `Struct` or `Union` we are currently visiting, or `null`.
ClassDeclaration? compound;
+ /// The `Void` type from `dart:ffi`, or `null` if unresolved.
+ InterfaceType? ffiVoidType;
+
/// Initialize a newly created verifier.
FfiVerifier(this.typeSystem, this._errorReporter,
{required this.strictCasts});
@@ -493,8 +496,36 @@
);
}
} else {
- if (declarationElement is MethodElement2 ||
- declarationElement is TopLevelFunctionElement) {
+ if (declarationElement
+ case TopLevelFunctionElement() || MethodElement2()) {
+ declarationElement = declarationElement as ExecutableElement2;
+ var dartSignature = declarationElement.type;
+
+ if (declarationElement.isStatic && ffiSignature is DynamicType) {
+ // No type argument was given on the @Native annotation, so we try
+ // to infer the native type from the Dart signature.
+ if (dartSignature.returnType is VoidType) {
+ // The Dart signature has a `void` return type, so we create a new
+ // `FunctionType` with FFI's `Void` as the return type.
+ dartSignature = FunctionTypeImpl.v2(
+ typeParameters: dartSignature.typeParameters,
+ formalParameters: dartSignature.formalParameters,
+ returnType: ffiVoidType ??= annotationType.element3.library2
+ .getClass2('Void')!
+ .thisType,
+ nullabilitySuffix: dartSignature.nullabilitySuffix,
+ );
+ }
+ _checkFfiNativeFunction(
+ errorNode,
+ declarationElement,
+ dartSignature,
+ annotationValue,
+ formalParameters,
+ );
+ return;
+ }
+
// Function annotated with something that isn't a function type.
_errorReporter.atToken(
errorNode,
@@ -512,9 +543,6 @@
);
}
}
-
- if (ffiSignature is FunctionType &&
- declarationElement is ExecutableElement2) {}
}
}
@@ -683,11 +711,20 @@
nullabilitySuffix: ffiSignature.nullabilitySuffix,
);
if (!_isValidFfiNativeFunctionType(nativeType)) {
- _errorReporter.atToken(
- errorToken,
- FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
- arguments: [nativeType, 'Native'],
- );
+ var nativeTypeIsOmitted = (annotationValue.type! as InterfaceType)
+ .typeArguments[0] is DynamicType;
+ if (nativeTypeIsOmitted) {
+ _errorReporter.atToken(
+ errorToken,
+ FfiCode.NATIVE_FUNCTION_MISSING_TYPE,
+ );
+ } else {
+ _errorReporter.atToken(
+ errorToken,
+ FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
+ arguments: [nativeType, 'Native'],
+ );
+ }
return;
}
if (!_validateCompatibleFunctionTypes(
@@ -1707,15 +1744,8 @@
// When referencing a function, the target type must be a
// `NativeFunction<T>` so that `T` matches the type from the
// annotation.
- if (!targetType.isNativeFunction) {
- _errorReporter.atNode(
- node,
- FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
- arguments: [targetType, _nativeAddressOf],
- );
- } else {
- var targetFunctionType =
- (targetType as InterfaceType).typeArguments[0];
+ if (targetType case InterfaceType(isNativeFunction: true)) {
+ var targetFunctionType = targetType.typeArguments[0];
if (!typeSystem.isEqualTo(nativeType, targetFunctionType)) {
_errorReporter.atNode(
node,
@@ -1723,30 +1753,62 @@
arguments: [nativeType, targetFunctionType, _nativeAddressOf],
);
}
- }
- } else {
- // A native field is being referenced, this doesn't require a
- // NativeFunction wrapper. However, we can't read the native type
- // from the annotation directly because it might be inferred if none
- // was given.
- if (nativeType is DynamicType) {
- var staticType = argument.staticType;
- if (staticType != null) {
- var canonical = _canonicalFfiTypeForDartType(staticType);
-
- if (canonical != null) {
- nativeType = canonical;
- }
- }
- }
-
- if (!typeSystem.isEqualTo(nativeType, targetType)) {
+ } else {
_errorReporter.atNode(
node,
- FfiCode.MUST_BE_A_SUBTYPE,
- arguments: [nativeType, targetType, _nativeAddressOf],
+ FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
+ arguments: [targetType, _nativeAddressOf],
);
}
+ } else {
+ if (argument.staticType case var staticType?
+ when nativeType is DynamicType) {
+ // No type argument was given on the @Native annotation, so we try
+ // to infer the native type from the Dart signature.
+ if (staticType is FunctionType) {
+ if (staticType.returnType is VoidType) {
+ // The Dart signature has a `void` return type, so we create a
+ // new `FunctionType` with FFI's `Void` as the return type.
+ staticType = FunctionTypeImpl.v2(
+ typeParameters: staticType.typeParameters,
+ formalParameters: staticType.formalParameters,
+ returnType: ffiVoidType ??= annotationType.element3.library2
+ .getClass2('Void')!
+ .thisType,
+ nullabilitySuffix: staticType.nullabilitySuffix,
+ );
+ }
+
+ if (targetType case InterfaceType(isNativeFunction: true)) {
+ var targetFunctionType = targetType.typeArguments[0];
+ if (!typeSystem.isEqualTo(staticType, targetFunctionType)) {
+ _errorReporter.atNode(
+ node,
+ FfiCode.MUST_BE_A_SUBTYPE,
+ arguments: [
+ staticType,
+ targetFunctionType,
+ _nativeAddressOf
+ ],
+ );
+ }
+ } else {
+ _errorReporter.atNode(
+ node,
+ FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
+ arguments: [targetType, _nativeAddressOf],
+ );
+ }
+ } else {
+ if (!typeSystem.isEqualTo(staticType, targetType)) {
+ _errorReporter.atNode(
+ node,
+ FfiCode.MUST_BE_A_SUBTYPE,
+ arguments: [staticType, targetType, _nativeAddressOf],
+ );
+ }
+ }
+ }
}
validTarget = true;
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 7233f80..66e6a80 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -19212,6 +19212,49 @@
external int f;
}
```
+ NATIVE_FUNCTION_MISSING_TYPE:
+ problemMessage: The native type of this function couldn't be inferred so it must be specified in the annotation.
+ correctionMessage: Try adding a type parameter extending `NativeType` to the `@Native` annotation.
+ hasPublishedDocs: false
+ comment: No parameters
+ documentation: |-
+ #### Description
+
+ The analyzer produces this diagnostic when a `@Native`-annotated function
+ requires a type hint on the annotation to infer the native function type.
+
+ Dart types like `int` and `double` have multiple possible native
+ representations. Since the native type needs to be known at compile time
+ to generate correct bindings and call instructions for the function, an
+ explicit type must be given.
+
+ For more information about FFI, see [C interop using dart:ffi][ffi].
+
+ #### Example
+
+ The following code produces this diagnostic because the function `f()` has
+ the return type `int`, but doesn't have an explicit type parameter on the
+ `Native` annotation:
+
+ ```dart
+ import 'dart:ffi';
+
+ @Native()
+ external int [!f!]();
+ ```
+
+ #### Common fixes
+
+ Add the corresponding type to the annotation. For instance, if `f()` was
+ declared to return an `int32_t` in C, the Dart function should be declared
+ as:
+
+ ```dart
+ import 'dart:ffi';
+
+ @Native<Int32 Function()>()
+ external int f();
+ ```
COMPOUND_IMPLEMENTS_FINALIZABLE:
problemMessage: "The class '{0}' can't implement Finalizable."
correctionMessage: "Try removing the implements clause from '{0}'."
diff --git a/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart b/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
index 2cc05aa..ff0b1f1 100644
--- a/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
@@ -78,6 +78,21 @@
import 'dart:ffi';
@Native()
+external void foo();
+
+void main() {
+ print(Native.addressOf(foo));
+}
+''', [
+ error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 74, 21),
+ ]);
+ }
+
+ test_invalid_MissingType3() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
external Pointer<IntPtr> global;
void main() => print(Native.addressOf(global));
@@ -116,6 +131,19 @@
]);
}
+ test_invalid_NotAPreciseType2() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external void foo();
+
+void main() => print(Native.addressOf<NativeFunction>(foo));
+''', [
+ error(FfiCode.MUST_BE_A_SUBTYPE, 73, 37),
+ ]);
+ }
+
test_invalid_String() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@@ -134,10 +162,14 @@
external void foo();
@Native()
+external void foo2();
+
+@Native()
external Pointer<IntPtr> global;
void main() {
print(Native.addressOf<NativeFunction<Void Function()>>(foo));
+ print(Native.addressOf<NativeFunction<Void Function()>>(foo2));
print(Native.addressOf<Pointer<IntPtr>>(global));
}
''');
@@ -235,7 +267,7 @@
@Native()
external int foo();
''', [
- error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 43, 3),
+ error(FfiCode.NATIVE_FUNCTION_MISSING_TYPE, 43, 3),
]);
}
@@ -570,7 +602,7 @@
@Native()
external int foo();
''', [
- error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 43, 3),
+ error(FfiCode.NATIVE_FUNCTION_MISSING_TYPE, 43, 3),
]);
}
@@ -583,7 +615,7 @@
@a
external int foo();
''', [
- error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 57, 3),
+ error(FfiCode.NATIVE_FUNCTION_MISSING_TYPE, 57, 3),
]);
}
@@ -614,6 +646,234 @@
]);
}
+ test_InferPointerReturnNoParameters() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external Pointer foo();
+''');
+ }
+
+ test_InferPointerReturnPointerParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external Pointer foo(Pointer x);
+''');
+ }
+
+ test_InferPointerReturnStructParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external Pointer foo(MyStruct x);
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+''');
+ }
+
+ test_InferPointerReturnUnionParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external Pointer foo(MyUnion x);
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
+ test_InferStructReturnNoParameters() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyStruct foo();
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+''');
+ }
+
+ test_InferStructReturnPointerParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyStruct foo(Pointer x);
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+''');
+ }
+
+ test_InferStructReturnStructParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyStruct foo(MyStruct x);
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+''');
+ }
+
+ test_InferStructReturnUnionParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyStruct foo(MyUnion x);
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
+ test_InferUnionReturnNoParameters() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyUnion foo();
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
+ test_InferUnionReturnPointerParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyUnion foo(Pointer x);
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
+ test_InferUnionReturnStructParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyUnion foo(MyStruct x);
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
+ test_InferUnionReturnUnionParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external MyUnion foo(MyUnion x);
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
+ test_InferVoidReturnNoParameters() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external void foo();
+''');
+ }
+
+ test_InferVoidReturnPointerParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external void foo(Pointer x);
+''');
+ }
+
+ test_InferVoidReturnStructParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external void foo(MyStruct x);
+
+final class MyStruct extends Struct {
+ @Int8()
+ external int value;
+}
+''');
+ }
+
+ test_InferVoidReturnUnionParameter() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native()
+external void foo(MyUnion x);
+
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+''');
+ }
+
test_NativeCanUseHandles() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 0ce095d..8d206a8 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -14424,6 +14424,49 @@
}
```
+### native_function_missing_type
+
+_The native type of this function couldn't be inferred so it must be specified
+in the annotation._
+
+#### Description
+
+The analyzer produces this diagnostic when a `@Native`-annotated function
+requires a type hint on the annotation to infer the native function type.
+
+Dart types like `int` and `double` have multiple possible native
+representations. Since the native type needs to be known at compile time
+to generate correct bindings and call instructions for the function, an
+explicit type must be given.
+
+For more information about FFI, see [C interop using dart:ffi][ffi].
+
+#### Example
+
+The following code produces this diagnostic because the function `f()` has
+the return type `int`, but doesn't have an explicit type parameter on the
+`Native` annotation:
+
+```dart
+import 'dart:ffi';
+
+@Native()
+external int [!f!]();
+```
+
+#### Common fixes
+
+Add the corresponding type to the annotation. For instance, if `f()` was
+declared to return an `int32_t` in C, the Dart function should be declared
+as:
+
+```dart
+import 'dart:ffi';
+
+@Native<Int32 Function()>()
+external int f();
+```
+
### negative_variable_dimension
_The variable dimension of a variable-length array must be non-negative._
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index bf10cfd..71ddca5 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -5276,6 +5276,12 @@
analyzerCode: NATIVE_FIELD_INVALID_TYPE
external: test/ffi_test.dart
+FfiNativeFunctionMissingType:
+ # Used by dart:ffi
+ problemMessage: "The native type of this function couldn't be inferred so it must be specified in the annotation."
+ analyzerCode: NATIVE_FUNCTION_MISSING_TYPE
+ external: test/ffi_test.dart
+
FfiAddressOfMustBeNative:
# Used by dart:ffi
problemMessage: "Argument to 'Native.addressOf' must be annotated with @Native."
diff --git a/pkg/vm/lib/modular/transformations/ffi/common.dart b/pkg/vm/lib/modular/transformations/ffi/common.dart
index 8165e05..1c8c1ed 100644
--- a/pkg/vm/lib/modular/transformations/ffi/common.dart
+++ b/pkg/vm/lib/modular/transformations/ffi/common.dart
@@ -843,14 +843,42 @@
///
/// For types where this returns a non-null value, this is the inverse of
/// [convertNativeTypeToDartType].
- DartType? convertDartTypeToNativeType(DartType dartType) {
- if (isPointerType(dartType) ||
- isStructOrUnionSubtype(dartType) ||
- isArrayType(dartType)) {
+ DartType? convertDartTypeToNativeType(
+ DartType dartType, {
+ bool allowVoid = false,
+ }) {
+ if (allowVoid && dartType is VoidType) return voidType;
+
+ if (isArrayType(dartType) ||
+ isPointerType(dartType) ||
+ isStructOrUnionSubtype(dartType)) {
return dartType;
- } else {
- return null;
}
+
+ if (dartType is FunctionType) {
+ if (dartType.namedParameters.isNotEmpty) return null;
+ if (dartType.positionalParameters.length !=
+ dartType.requiredParameterCount) {
+ return null;
+ }
+ if (dartType.typeParameters.isNotEmpty) return null;
+
+ final returnType =
+ convertDartTypeToNativeType(dartType.returnType, allowVoid: true);
+ if (returnType == null) return null;
+
+ final argumentTypes = <DartType>[];
+ for (final paramDartType
+ in flattenVarargs(dartType).positionalParameters) {
+ argumentTypes.add(
+ convertDartTypeToNativeType(paramDartType) ?? dummyDartType,
+ );
+ }
+ if (argumentTypes.contains(dummyDartType)) return null;
+ return FunctionType(argumentTypes, returnType, Nullability.nonNullable);
+ }
+
+ return null;
}
/// Removes the VarArgs from a DartType list.
@@ -1468,7 +1496,7 @@
bool allowInlineArray = false,
bool allowVoid = false,
}) {
- if (!_nativeTypeValid(nativeType,
+ if (!isNativeTypeValid(nativeType,
allowStructAndUnion: allowStructAndUnion,
allowHandle: allowHandle,
allowInlineArray: allowInlineArray,
@@ -1484,7 +1512,7 @@
/// The Dart type system does not enforce that NativeFunction return and
/// parameter types are only NativeTypes, so we need to check this.
- bool _nativeTypeValid(
+ bool isNativeTypeValid(
DartType nativeType, {
bool allowStructAndUnion = false,
bool allowHandle = false,
diff --git a/pkg/vm/lib/modular/transformations/ffi/native.dart b/pkg/vm/lib/modular/transformations/ffi/native.dart
index 5dee1ed..e228a12 100644
--- a/pkg/vm/lib/modular/transformations/ffi/native.dart
+++ b/pkg/vm/lib/modular/transformations/ffi/native.dart
@@ -11,6 +11,7 @@
messageFfiNativeFieldMissingType,
messageFfiNativeFieldMustBeStatic,
messageFfiNativeFieldType,
+ messageFfiNativeFunctionMissingType,
messageFfiNativeMustBeExternal,
messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
templateCantHaveNamedParameters,
@@ -793,6 +794,43 @@
return (ffiType, NativeTypeCfe.withoutLayout(this, ffiType));
}
+ DartType _validateOrInferNativeFunctionType(
+ Procedure node,
+ DartType nativeType,
+ FunctionType dartType,
+ ) {
+ if (nativeType is DynamicType) {
+ // No type argument was given on the @Native annotation, so we try to
+ // infer the native type from the Dart signature.
+ final inferred = convertDartTypeToNativeType(dartType, allowVoid: true);
+ if (inferred != null) {
+ nativeType = inferred;
+ }
+
+ final nativeFunctionType = InterfaceType(
+ nativeFunctionClass,
+ Nullability.nonNullable,
+ [nativeType],
+ );
+
+ if (!isNativeTypeValid(
+ nativeFunctionType,
+ allowHandle: true,
+ allowStructAndUnion: true,
+ )) {
+ diagnosticReporter.report(
+ messageFfiNativeFunctionMissingType,
+ node.fileOffset,
+ 1,
+ node.location?.file,
+ );
+ throw FfiStaticTypeError();
+ }
+ }
+
+ return nativeType;
+ }
+
@override
TreeNode visitField(Field node) {
final nativeAnnotation = tryGetNativeAnnotationOrWarnOnDuplicates(node);
@@ -829,6 +867,18 @@
final ffiConstant = ffiNativeAnnotation.constant as InstanceConstant;
var nativeType = ffiConstant.typeArguments[0];
+ if (node.kind == ProcedureKind.Method) {
+ final dartType =
+ node.function.computeFunctionType(Nullability.nonNullable);
+ try {
+ nativeType =
+ _validateOrInferNativeFunctionType(node, nativeType, dartType);
+ } on FfiStaticTypeError {
+ // We've already reported an error.
+ return node;
+ }
+ }
+
final nativeName = _resolveNativeSymbolName(node, ffiConstant);
final overriddenAssetName = _assetNameFromAnnotation(ffiConstant);
final isLeaf = _isLeaf(ffiConstant);
@@ -843,14 +893,13 @@
// We've already reported an error.
return node;
}
- final ffiFunctionType = ffiConstant.typeArguments[0] as FunctionType;
if (!node.isStatic) {
- return _transformInstanceMethod(node, ffiFunctionType, nativeName,
+ return _transformInstanceMethod(node, nativeType, nativeName,
overriddenAssetName, isLeaf, ffiNativeAnnotation.fileOffset);
}
- return _transformStaticFunction(node, ffiFunctionType, nativeName,
+ return _transformStaticFunction(node, nativeType, nativeName,
overriddenAssetName, isLeaf, ffiNativeAnnotation.fileOffset);
} else if (node.kind == ProcedureKind.Getter ||
node.kind == ProcedureKind.Setter) {
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart b/pkg/vm/testcases/transformations/ffi/ffinative.dart
index 313780c..2b04048 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart
@@ -11,6 +11,12 @@
import 'dart:ffi';
import 'dart:nativewrappers';
+@Native()
+external void returnVoid();
+
+@Native()
+external Pointer returnPointer(Pointer x);
+
@Native<IntPtr Function(IntPtr)>(symbol: 'ReturnIntPtr')
external int returnIntPtr(int x);
@@ -69,5 +75,7 @@
final b = NativeClassy().myField;
NativeClassy().myField = !b;
+ Native.addressOf<NativeFunction<Void Function()>>(returnVoid);
+ Native.addressOf<NativeFunction<Pointer Function(Pointer)>>(returnPointer);
Native.addressOf<NativeFunction<IntPtr Function(IntPtr)>>(returnIntPtr);
}
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect
index 8eb300f..b46e9ca 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart.aot.expect
@@ -199,6 +199,8 @@
[@vm.direct-call.metadata=#lib::NativeClassy.blah] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::blah}(){() → core::bool};
final core::bool b = [@vm.direct-call.metadata=#lib::NativeClassy.myField] [@vm.inferred-type.metadata=dart.core::bool] new self::NativeClassy::•().{self::NativeClassy::myField}{core::bool};
[@vm.direct-call.metadata=#lib::NativeClassy.myField] [@vm.inferred-type.metadata=!? (skip check)] new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
+ ffi::Native::_addressOf<ffi::NativeFunction<() → ffi::Void>>(#C48);
+ ffi::Native::_addressOf<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Pointer<ffi::NativeType>>>(#C50);
ffi::Native::_addressOf<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>>(#C5);
}
constants {
@@ -248,4 +250,8 @@
#C44 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C28}
#C45 = core::pragma {name:#C1, options:#C44}
#C46 = core::pragma {name:#C7, options:#C44}
+ #C47 = "returnVoid"
+ #C48 = ffi::Native<() → ffi::Void> {symbol:#C47, assetId:#C3, isLeaf:#C4}
+ #C49 = "returnPointer"
+ #C50 = ffi::Native<(ffi::Pointer<ffi::NativeType>) → ffi::Pointer<ffi::NativeType>> {symbol:#C49, assetId:#C3, isLeaf:#C4}
}
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
index ab86316..5d77cdc 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
@@ -144,14 +144,20 @@
@#C42
external static method _myField$Setter$FfiNative(ffi::Pointer<ffi::Void> #t0, core::bool #t1) → void;
}
+@#C45
+@#C46
+external static method returnVoid() → void;
+@#C49
+@#C50
+external static method returnPointer(ffi::Pointer<ffi::NativeType> x) → ffi::Pointer<ffi::NativeType>;
@#C6
@#C8
external static method returnIntPtr(core::int x) → core::int;
-@#C44
-@#C45
+@#C52
+@#C53
external static method returnIntPtrLeaf(core::int x) → core::int;
-@#C48
-@#C49
+@#C56
+@#C57
external static method returnNativeIntPtrLeaf(core::int x) → core::int;
static method main() → void {
self::returnIntPtr(13);
@@ -166,6 +172,8 @@
new self::NativeClassy::•().{self::NativeClassy::blah}(){() → core::bool};
final core::bool b = new self::NativeClassy::•().{self::NativeClassy::myField}{core::bool};
new self::NativeClassy::•().{self::NativeClassy::myField} = !b;
+ ffi::Native::_addressOf<ffi::NativeFunction<() → ffi::Void>>(#C44);
+ ffi::Native::_addressOf<ffi::NativeFunction<(ffi::Pointer<ffi::NativeType>) → ffi::Pointer<ffi::NativeType>>>(#C48);
ffi::Native::_addressOf<ffi::NativeFunction<(ffi::IntPtr) → ffi::IntPtr>>(#C5);
}
constants {
@@ -211,11 +219,19 @@
#C40 = core::pragma {name:#C7, options:#C27}
#C41 = core::pragma {name:#C7, options:#C30}
#C42 = core::pragma {name:#C7, options:#C32}
- #C43 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C29}
- #C44 = core::pragma {name:#C1, options:#C43}
- #C45 = core::pragma {name:#C7, options:#C43}
- #C46 = "returnNativeIntPtrLeaf"
- #C47 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C46, assetId:#C3, isLeaf:#C29}
- #C48 = core::pragma {name:#C1, options:#C47}
- #C49 = core::pragma {name:#C7, options:#C47}
+ #C43 = "returnVoid"
+ #C44 = ffi::Native<() → ffi::Void> {symbol:#C43, assetId:#C3, isLeaf:#C4}
+ #C45 = core::pragma {name:#C1, options:#C44}
+ #C46 = core::pragma {name:#C7, options:#C44}
+ #C47 = "returnPointer"
+ #C48 = ffi::Native<(ffi::Pointer<ffi::NativeType>) → ffi::Pointer<ffi::NativeType>> {symbol:#C47, assetId:#C3, isLeaf:#C4}
+ #C49 = core::pragma {name:#C1, options:#C48}
+ #C50 = core::pragma {name:#C7, options:#C48}
+ #C51 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C2, assetId:#C3, isLeaf:#C29}
+ #C52 = core::pragma {name:#C1, options:#C51}
+ #C53 = core::pragma {name:#C7, options:#C51}
+ #C54 = "returnNativeIntPtrLeaf"
+ #C55 = ffi::Native<(ffi::IntPtr) → ffi::IntPtr> {symbol:#C54, assetId:#C3, isLeaf:#C29}
+ #C56 = core::pragma {name:#C1, options:#C55}
+ #C57 = core::pragma {name:#C7, options:#C55}
}
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 10bfda2..c0f8d7b 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -2374,20 +2374,30 @@
/// annotated function or variable in Dart. This can be overridden with the
/// [symbol] parameter on the annotation.
///
-/// If this annotation is used on a function, then the type argument [T] to the
-/// [Native] annotation must be a function type representing the native
-/// function's parameter and return types. The parameter and return types must
-/// be subtypes of [NativeType].
+/// When used on a function, [T] must be a function type that represents the
+/// native function's parameter and return types. The parameter and return types
+/// must be subtypes of [NativeType].
///
-/// If this annotation is used on an external variable, then the type argument
-/// [T] must be a compatible native type. For example, an [int] field can be
-/// annotated with [Int32].
-/// If the type argument to `@Native` is omitted, it defaults to the Dart type
-/// of the annotated declaration, which *must* then be a native type too.
-/// This will never work for function declarations, but can apply to variables
-/// whose type is some of the types of this library, such as [Pointer].
-/// For native global variables that cannot be re-assigned, a final variable in
-/// Dart or a getter can be used to prevent assignments to the native field.
+/// When used on a variable, [T] must be a compatible native type. For example,
+/// an [int] field can be annotated with [Int32].
+///
+/// If the type argument [T] is omitted in the `@Native` annotation, it is
+/// inferred from the static type of the declaration, which must meet the
+/// following constraints:
+///
+/// For function or method declarations:
+/// - The return type must be one of the following:
+/// - [Pointer]
+/// - `void`
+/// - Subtype of compound types, such as [Struct] or [Union]
+/// - The parameter types must be subtypes of compound types or [Pointer]
+///
+/// For variable declarations, the type can be any of the following:
+/// - [Pointer]
+/// - Subtype of compound types, such as [Struct] or [Union]
+///
+/// For native global variables that cannot be reassigned, a `final` variable in
+/// Dart or a getter can be used to prevent modifications to the native field.
///
/// Example:
///
@@ -2395,6 +2405,9 @@
/// @Native<Int64 Function(Int64, Int64)>()
/// external int sum(int a, int b);
///
+/// @Native()
+/// external void free(Pointer p);
+///
/// @Native<Int64>()
/// external int aGlobalInt;
///
diff --git a/tests/ffi/native_assets/asset_absolute_test.dart b/tests/ffi/native_assets/asset_absolute_test.dart
index 5c3e7c6..d3669f2 100644
--- a/tests/ffi/native_assets/asset_absolute_test.dart
+++ b/tests/ffi/native_assets/asset_absolute_test.dart
@@ -113,7 +113,7 @@
@Native()
external Coord globalStruct;
-@Native<Coord Function()>()
+@Native()
external Coord GetGlobalStruct();
@Native()
diff --git a/tests/ffi/native_assets/asset_library_annotation_test.dart b/tests/ffi/native_assets/asset_library_annotation_test.dart
index 2a99623..2ffb3f2 100644
--- a/tests/ffi/native_assets/asset_library_annotation_test.dart
+++ b/tests/ffi/native_assets/asset_library_annotation_test.dart
@@ -111,7 +111,7 @@
@Native()
external Coord globalStruct;
-@Native<Coord Function()>()
+@Native()
external Coord GetGlobalStruct();
@Native()
diff --git a/tests/ffi/native_assets/asset_process_test.dart b/tests/ffi/native_assets/asset_process_test.dart
index 9242eef..f6c6850 100644
--- a/tests/ffi/native_assets/asset_process_test.dart
+++ b/tests/ffi/native_assets/asset_process_test.dart
@@ -79,25 +79,25 @@
@Native<Pointer Function(IntPtr)>(symbol: 'malloc')
external Pointer posixMalloc(int size);
-@Native<Void Function(Pointer)>(symbol: 'free')
+@Native(symbol: 'free')
external void posixFree(Pointer pointer);
@Native<Pointer Function(Size)>(symbol: 'CoTaskMemAlloc')
external Pointer winCoTaskMemAlloc(int cb);
-@Native<Void Function(Pointer)>(symbol: 'CoTaskMemFree')
+@Native(symbol: 'CoTaskMemFree')
external void winCoTaskMemFree(Pointer pv);
@Native<Pointer Function(IntPtr)>()
external Pointer malloc(int size);
-@Native<Void Function(Pointer)>(assetId: asset2Name)
+@Native(assetId: asset2Name)
external void free(Pointer pointer);
@Native<Pointer Function(Size)>()
external Pointer CoTaskMemAlloc(int cb);
-@Native<Void Function(Pointer)>(assetId: asset2Name)
+@Native(assetId: asset2Name)
external void CoTaskMemFree(Pointer pv);
void testProcessOrSystem() {
diff --git a/tests/ffi/native_assets/asset_relative_test.dart b/tests/ffi/native_assets/asset_relative_test.dart
index d7586e5..7042791 100644
--- a/tests/ffi/native_assets/asset_relative_test.dart
+++ b/tests/ffi/native_assets/asset_relative_test.dart
@@ -193,7 +193,7 @@
@Native()
external Coord globalStruct;
-@Native<Coord Function()>()
+@Native()
external Coord GetGlobalStruct();
@Native()
diff --git a/tests/ffi/native_assets/asset_system_test.dart b/tests/ffi/native_assets/asset_system_test.dart
index dc9ec63..e421e62 100644
--- a/tests/ffi/native_assets/asset_system_test.dart
+++ b/tests/ffi/native_assets/asset_system_test.dart
@@ -81,13 +81,13 @@
@Native<Pointer Function(IntPtr)>()
external Pointer malloc(int size);
-@Native<Void Function(Pointer)>(assetId: asset2Name)
+@Native(assetId: asset2Name)
external void free(Pointer pointer);
@Native<Pointer Function(Size)>()
external Pointer CoTaskMemAlloc(int cb);
-@Native<Void Function(Pointer)>(assetId: asset2Name)
+@Native(assetId: asset2Name)
external void CoTaskMemFree(Pointer pv);
void testProcessOrSystem() {
diff --git a/tests/ffi/native_assets/process_test.dart b/tests/ffi/native_assets/process_test.dart
index f734146..aadcd0e 100644
--- a/tests/ffi/native_assets/process_test.dart
+++ b/tests/ffi/native_assets/process_test.dart
@@ -28,13 +28,13 @@
@Native<Pointer Function(IntPtr, IntPtr)>(symbol: 'calloc')
external Pointer posixCalloc(int num, int size);
-@Native<Void Function(Pointer)>(symbol: 'free')
+@Native(symbol: 'free')
external void posixFree(Pointer pointer);
@Native<Pointer Function(Size)>(symbol: 'CoTaskMemAlloc')
external Pointer winCoTaskMemAlloc(int cb);
-@Native<Void Function(Pointer)>(symbol: 'CoTaskMemFree')
+@Native(symbol: 'CoTaskMemFree')
external void winCoTaskMemFree(Pointer pv);
class _MallocAllocator implements Allocator {
@@ -90,5 +90,5 @@
}
}
-@Native<Void Function()>(symbol: 'symbol_is_not_defined_29903211')
+@Native(symbol: 'symbol_is_not_defined_29903211')
external void symbolIsNotDefined();
diff --git a/tests/ffi/static_checks/regress_51913_test.dart b/tests/ffi/static_checks/regress_51913_test.dart
deleted file mode 100644
index b2dd94f..0000000
--- a/tests/ffi/static_checks/regress_51913_test.dart
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2021, 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.
-
-// Formatting can break multitests, so don't format them.
-// dart format off
-
-import 'dart:ffi';
-
-// Should have been `@Native<Void Function()>` or something.
-@Native() //# 1: compile-time error
-external void foo(); //# 1: compile-time error
-
-void main() {
- print('something');
-}
diff --git a/tests/ffi/static_checks/vmspecific_static_checks_native_test.dart b/tests/ffi/static_checks/vmspecific_static_checks_native_test.dart
index 438bf0b..9f7517a 100644
--- a/tests/ffi/static_checks/vmspecific_static_checks_native_test.dart
+++ b/tests/ffi/static_checks/vmspecific_static_checks_native_test.dart
@@ -27,6 +27,89 @@
external Pointer<MyStruct> next;
}
+final class MyUnion extends Union {
+ @Int8()
+ external int a;
+ @Int8()
+ external int b;
+}
+
+@Native()
+external void validInference();
+
+@Native()
+external void validInference2(Pointer x);
+
+@Native()
+external void validInference3(MyStruct x);
+
+@Native()
+external void validInference4(MyUnion x);
+
+@Native()
+external MyStruct validInference5();
+
+@Native()
+external MyStruct validInference6(Pointer x);
+
+@Native()
+external MyStruct validInference7(MyStruct x);
+
+@Native()
+external MyStruct validInference8(MyUnion x);
+
+@Native()
+external MyUnion validInference9();
+
+@Native()
+external MyUnion validInference10(Pointer x);
+
+@Native()
+external MyUnion validInference11(MyStruct x);
+
+@Native()
+external MyUnion validInference12(MyUnion x);
+
+@Native()
+external Pointer validInference13();
+
+@Native()
+external Pointer validInference14(Pointer x);
+
+@Native()
+external Pointer validInference15(MyStruct x);
+
+@Native()
+external Pointer validInference16(MyUnion x);
+
+@Native()
+external void invalidNoInference(int x);
+// ^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.NATIVE_FUNCTION_MISSING_TYPE
+// ^
+// [cfe] The native type of this function couldn't be inferred so it must be specified in the annotation.
+
+@Native()
+external MyStruct invalidNoInference2(int x);
+// ^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.NATIVE_FUNCTION_MISSING_TYPE
+// ^
+// [cfe] The native type of this function couldn't be inferred so it must be specified in the annotation.
+
+@Native()
+external MyUnion invalidNoInference3(int x);
+// ^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.NATIVE_FUNCTION_MISSING_TYPE
+// ^
+// [cfe] The native type of this function couldn't be inferred so it must be specified in the annotation.
+
+@Native()
+external Pointer invalidNoInference4(int x);
+// ^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.NATIVE_FUNCTION_MISSING_TYPE
+// ^
+// [cfe] The native type of this function couldn't be inferred so it must be specified in the annotation.
+
typedef ComplexNativeFunction = MyStruct Function(Long, Double, MyStruct);
const native = Native<ComplexNativeFunction>();
@@ -68,7 +151,7 @@
// [cfe] Expected type 'Pointer<MyStruct>' to be 'MyStruct', which is the Dart type corresponding to 'MyStruct'.
@Native()
-external int invalidNoInferrence;
+external int invalidNoInference5;
// ^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NATIVE_FIELD_MISSING_TYPE
// [cfe] The native type of this field could not be inferred and must be specified in the annotation.
@@ -171,18 +254,35 @@
// ^
// [cfe] Expected type 'NativeType' to be a valid and instantiated subtype of 'NativeType'.
+ Native.addressOf(validInference);
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_NATIVE_FUNCTION_TYPE
+ // ^
+ // [cfe] Expected type 'NativeType' to be a valid and instantiated subtype of 'NativeType'.
+
Native.addressOf<NativeFunction>(_valid);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
// ^
// [cfe] Expected type 'NativeFunction<Function>' to be a valid and instantiated subtype of 'NativeType'.
+ Native.addressOf<NativeFunction>(validInference);
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
+ // ^
+ // [cfe] Expected type 'NativeFunction<Function>' to be a valid and instantiated subtype of 'NativeType'.
+
Native.addressOf<NativeFunction<Void Function(Int)>>(_valid);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
// ^
// [cfe] Expected type 'void Function()' to be 'void Function(int)', which is the Dart type corresponding to 'NativeFunction<Void Function(Int)>'.
+ Native.addressOf<NativeFunction<Void Function()>>(validInference);
+ Native.addressOf<NativeFunction<Void Function(Pointer)>>(validInference2);
+ Native.addressOf<NativeFunction<MyStruct Function()>>(validInference5);
+ Native.addressOf<NativeFunction<MyUnion Function()>>(validInference9);
+ Native.addressOf<NativeFunction<Pointer Function()>>(validInference13);
Native.addressOf<NativeFunction<ComplexNativeFunction>>(validNative);
Native.addressOf(myStruct0);
diff --git a/tools/find_builders.dart b/tools/find_builders.dart
index 7ddb178..5d9b53e 100755
--- a/tools/find_builders.dart
+++ b/tools/find_builders.dart
@@ -8,7 +8,7 @@
// Usage:
//
// ```
-// $ tools/find_builders.dart ffi/regress_51504_test ffi/regress_51913_test
+// $ tools/find_builders.dart ffi/regress_51504_test ffi/regress_52298_test
// Cq-Include-Trybots: dart/try:vm-kernel-linux-debug-x64,...
// ```
@@ -27,10 +27,11 @@
for (final testName in testNames) ...await _testGetConfigurations(testName),
});
final configurationBuilders = await _configurationBuilders();
- final builders = _filterBuilders(
- {for (final config in configurations) configurationBuilders[config]!},
- ).toList()
- ..sort();
+ final builders =
+ _filterBuilders({
+ for (final config in configurations) configurationBuilders[config]!,
+ }).toList()
+ ..sort();
final gerritTryList = builders.map((b) => '$b-try').join(',');
print('Cq-Include-Trybots: dart/try:$gerritTryList');
@@ -47,7 +48,7 @@
final object = jsonDecode(response) as Map<String, dynamic>;
return [
for (final result in ((object['results'] as List)).cast<Map>())
- result['configuration']
+ result['configuration'],
];
}
@@ -105,7 +106,8 @@
final response = await _get(requestUrl);
final object = jsonDecode(response) as Map<String, dynamic>;
yield* Stream.fromIterable(
- (object['documents'] as List).cast<Map<String, dynamic>>());
+ (object['documents'] as List).cast<Map<String, dynamic>>(),
+ );
nextPageToken = object['nextPageToken'];
} while (nextPageToken != null);
@@ -114,12 +116,11 @@
Future<Map<String, String>> _configurationBuilders() async {
return {
await for (final document in _configurationDocuments())
- if (document
- case {
- 'name': String fullName,
- 'fields': {'builder': {'stringValue': String builder}}
- })
- fullName.split('/').last: builder
+ if (document case {
+ 'name': String fullName,
+ 'fields': {'builder': {'stringValue': String builder}},
+ })
+ fullName.split('/').last: builder,
};
}