[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>
This commit is contained in:
committed by
Commit Queue
parent
31f97eaec1
commit
1a8afa6bbf
@@ -6647,6 +6647,18 @@ const MessageCode messageFfiNativeFieldType = const MessageCode(
|
||||
r"""Unsupported type for native fields. Native fields only support pointers, compounds and numeric types.""",
|
||||
);
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
@@ -1792,6 +1792,8 @@ FfiCode.NATIVE_FIELD_MISSING_TYPE:
|
||||
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:
|
||||
|
||||
@@ -387,6 +387,16 @@ class FfiCode extends ErrorCode {
|
||||
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',
|
||||
|
||||
@@ -632,6 +632,7 @@ const List<ErrorCode> errorCodeValues = [
|
||||
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,
|
||||
|
||||
@@ -110,6 +110,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
||||
/// 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 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
||||
);
|
||||
}
|
||||
} 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 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (ffiSignature is FunctionType &&
|
||||
declarationElement is ExecutableElement2) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,11 +711,20 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
||||
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 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
||||
// 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 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
||||
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;
|
||||
|
||||
@@ -19212,6 +19212,49 @@ FfiCode:
|
||||
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}'."
|
||||
|
||||
@@ -77,6 +77,21 @@ void main() {
|
||||
await assertErrorsInCode(r'''
|
||||
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;
|
||||
|
||||
@@ -116,6 +131,19 @@ void main() => print(Native.addressOf<NativeFunction>(foo));
|
||||
]);
|
||||
}
|
||||
|
||||
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';
|
||||
@@ -133,11 +161,15 @@ import 'dart:ffi';
|
||||
@Native<Void Function()>()
|
||||
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 @@ import 'dart:ffi';
|
||||
@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 @@ import 'dart:ffi';
|
||||
@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 @@ const a = Native();
|
||||
@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 @@ external int foo();
|
||||
]);
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
@@ -14424,6 +14424,49 @@ final class C extends Struct {
|
||||
}
|
||||
```
|
||||
|
||||
### 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._
|
||||
|
||||
@@ -5276,6 +5276,12 @@ FfiNativeFieldType:
|
||||
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."
|
||||
|
||||
@@ -843,14 +843,42 @@ class FfiTransformer extends Transformer {
|
||||
///
|
||||
/// 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 @@ class FfiTransformer extends Transformer {
|
||||
bool allowInlineArray = false,
|
||||
bool allowVoid = false,
|
||||
}) {
|
||||
if (!_nativeTypeValid(nativeType,
|
||||
if (!isNativeTypeValid(nativeType,
|
||||
allowStructAndUnion: allowStructAndUnion,
|
||||
allowHandle: allowHandle,
|
||||
allowInlineArray: allowInlineArray,
|
||||
@@ -1484,7 +1512,7 @@ class FfiTransformer extends Transformer {
|
||||
|
||||
/// 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,
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:front_end/src/codes/cfe_codes.dart'
|
||||
messageFfiNativeFieldMissingType,
|
||||
messageFfiNativeFieldMustBeStatic,
|
||||
messageFfiNativeFieldType,
|
||||
messageFfiNativeFunctionMissingType,
|
||||
messageFfiNativeMustBeExternal,
|
||||
messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
|
||||
templateCantHaveNamedParameters,
|
||||
@@ -793,6 +794,43 @@ class FfiNativeTransformer extends FfiTransformer {
|
||||
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 @@ class FfiNativeTransformer extends FfiTransformer {
|
||||
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 @@ class FfiNativeTransformer extends FfiTransformer {
|
||||
// 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) {
|
||||
|
||||
@@ -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 @@ void main() {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -199,6 +199,8 @@ static method main() → void {
|
||||
[@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 @@ constants {
|
||||
#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}
|
||||
}
|
||||
|
||||
@@ -144,14 +144,20 @@ class NativeClassy extends nat::NativeFieldWrapperClass1 {
|
||||
@#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 @@ static method main() → void {
|
||||
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 @@ constants {
|
||||
#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}
|
||||
}
|
||||
|
||||
+26
-13
@@ -2374,20 +2374,30 @@ abstract final class NativeApi {
|
||||
/// 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 @@ abstract final class NativeApi {
|
||||
/// @Native<Int64 Function(Int64, Int64)>()
|
||||
/// external int sum(int a, int b);
|
||||
///
|
||||
/// @Native()
|
||||
/// external void free(Pointer p);
|
||||
///
|
||||
/// @Native<Int64>()
|
||||
/// external int aGlobalInt;
|
||||
///
|
||||
|
||||
@@ -113,7 +113,7 @@ final class Coord extends Struct {
|
||||
@Native()
|
||||
external Coord globalStruct;
|
||||
|
||||
@Native<Coord Function()>()
|
||||
@Native()
|
||||
external Coord GetGlobalStruct();
|
||||
|
||||
@Native()
|
||||
|
||||
@@ -111,7 +111,7 @@ final class Coord extends Struct {
|
||||
@Native()
|
||||
external Coord globalStruct;
|
||||
|
||||
@Native<Coord Function()>()
|
||||
@Native()
|
||||
external Coord GetGlobalStruct();
|
||||
|
||||
@Native()
|
||||
|
||||
@@ -79,25 +79,25 @@ Future<void> runTests() async {
|
||||
@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() {
|
||||
|
||||
@@ -193,7 +193,7 @@ final class Coord extends Struct {
|
||||
@Native()
|
||||
external Coord globalStruct;
|
||||
|
||||
@Native<Coord Function()>()
|
||||
@Native()
|
||||
external Coord GetGlobalStruct();
|
||||
|
||||
@Native()
|
||||
|
||||
@@ -81,13 +81,13 @@ Future<void> runTests() async {
|
||||
@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() {
|
||||
|
||||
@@ -28,13 +28,13 @@ external Pointer posixMalloc(int size);
|
||||
@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 @@ void testFailure() {
|
||||
}
|
||||
}
|
||||
|
||||
@Native<Void Function()>(symbol: 'symbol_is_not_defined_29903211')
|
||||
@Native(symbol: 'symbol_is_not_defined_29903211')
|
||||
external void symbolIsNotDefined();
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
@@ -27,6 +27,89 @@ final class MyStruct extends Struct {
|
||||
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 @@ external Pointer<MyStruct> myStructPtrInvalid;
|
||||
// [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 @@ void addressOf() {
|
||||
// ^
|
||||
// [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);
|
||||
|
||||
+14
-13
@@ -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 @@ Future<void> main(List<String> args) async {
|
||||
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 @@ Future<List<String>> _testGetConfigurations(String testName) async {
|
||||
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 @@ Stream<Map<String, dynamic>> _configurationDocuments() async* {
|
||||
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 @@ Stream<Map<String, dynamic>> _configurationDocuments() async* {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user