[vm/ffi] Support @Native fields

Allow annotating top-level or static fields with `@Native` to create
fields backed by native memory.
By using the `_addressOf` operator implemented in the VM, these fields
can be implemented in the CFE by replacing them with accessors looking
up the pointer and then using existing methods to load and store the
value.

Closes https://github.com/dart-lang/sdk/issues/50551

TEST=tests/ffi/native_assets/asset_*_test.dart
TEST=pkg/analyzer/test/src/diagnostics/ffi_native_test.dart

CoreLibraryReviewExempt: VM & dart2wasm only feature
Change-Id: I61dccc88076723d6a6ba02d7fd848b18e4caf780
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/338020
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Simon Binder 2023-12-15 11:20:23 +00:00 committed by Daco Harkes
parent 39e48045de
commit 150e61a662
43 changed files with 2203 additions and 581 deletions

View file

@ -51,6 +51,12 @@
- `String.fromCharCodes` now allow `start` and `end` to be after the end of
the `Iterable` argument, just like `skip` and `take` does on an `Iterable`.
#### `dart:ffi`
- In addition to functions, `@Native` can now be used on fields.
- Allow taking the address of native functions and fields via
`Native.addressOf`.
#### `dart:nativewrappers`
- **Breaking Change** [#51896][]: The NativeWrapperClasses are marked `base` so

View file

@ -5275,7 +5275,38 @@ const MessageCode messageFfiNativeDuplicateAnnotations = const MessageCode(
"FfiNativeDuplicateAnnotations",
analyzerCodes: <String>["FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS"],
problemMessage:
r"""Native functions must not have more than @Native annotation.""");
r"""Native functions and fields must not have more than @Native annotation.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiNativeFieldMissingType =
messageFfiNativeFieldMissingType;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiNativeFieldMissingType = const MessageCode(
"FfiNativeFieldMissingType",
analyzerCodes: <String>["NATIVE_FIELD_MISSING_TYPE"],
problemMessage:
r"""The native type of this field could not be inferred and must be specified in the annotation.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiNativeFieldMustBeStatic =
messageFfiNativeFieldMustBeStatic;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiNativeFieldMustBeStatic = const MessageCode(
"FfiNativeFieldMustBeStatic",
analyzerCodes: <String>["NATIVE_FIELD_NOT_STATIC"],
problemMessage: r"""Native fields must be static.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiNativeFieldType = messageFfiNativeFieldType;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiNativeFieldType = const MessageCode(
"FfiNativeFieldType",
analyzerCodes: <String>["NATIVE_FIELD_INVALID_TYPE"],
problemMessage:
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> codeFfiNativeMustBeExternal = messageFfiNativeMustBeExternal;
@ -5283,7 +5314,8 @@ const Code<Null> codeFfiNativeMustBeExternal = messageFfiNativeMustBeExternal;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiNativeMustBeExternal = const MessageCode(
"FfiNativeMustBeExternal",
problemMessage: r"""Native functions must be marked external.""");
problemMessage:
r"""Native functions and fields must be marked external.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiNativeOnlyNativeFieldWrapperClassCanBePointer =

View file

@ -1798,6 +1798,12 @@ FfiCode.MUST_BE_A_SUBTYPE:
status: noFix
FfiCode.MUST_RETURN_VOID:
status: noFix
FfiCode.NATIVE_FIELD_INVALID_TYPE:
status: needsEvaluation
FfiCode.NATIVE_FIELD_MISSING_TYPE:
status: needsEvaluation
FfiCode.NATIVE_FIELD_NOT_STATIC:
status: needsEvaluation
FfiCode.NON_CONSTANT_TYPE_ARGUMENT:
status: noFix
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER:

View file

@ -87,7 +87,8 @@ class FfiCode extends AnalyzerErrorCode {
static const FfiCode ARGUMENT_MUST_BE_NATIVE = FfiCode(
'ARGUMENT_MUST_BE_NATIVE',
"Argument to 'Native.addressOf' must be annotated with @Native",
correctionMessage: "Try passing a static function annotated with '@Native'",
correctionMessage:
"Try passing a static function or field annotated with '@Native'",
);
/// No parameters.
@ -163,7 +164,7 @@ class FfiCode extends AnalyzerErrorCode {
/// No parameters
static const FfiCode FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS = FfiCode(
'FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS',
"Native functions must have exactly one `@Native` annotation.",
"Native functions and fields must have exactly one `@Native` annotation.",
correctionMessage: "Try removing the extra annotation.",
);
@ -368,6 +369,37 @@ class FfiCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
/// Parameters:
/// 0: The invalid type.
static const FfiCode NATIVE_FIELD_INVALID_TYPE = FfiCode(
'NATIVE_FIELD_INVALID_TYPE',
"'{0}' is an unsupported type for native fields. Native fields only "
"support pointers or numeric and compound types.",
correctionMessage:
"Try changing the type in the `@Native` annotation to a numeric FFI "
"type, a pointer, or a compound class.",
hasPublishedDocs: true,
);
/// No parameters
static const FfiCode NATIVE_FIELD_MISSING_TYPE = FfiCode(
'NATIVE_FIELD_MISSING_TYPE',
"The native type of this field could not be inferred and must be specified "
"in the annotation.",
correctionMessage:
"Try adding a type parameter extending `NativeType` to the `@Native` "
"annotation.",
hasPublishedDocs: true,
);
/// No parameters
static const FfiCode NATIVE_FIELD_NOT_STATIC = FfiCode(
'NATIVE_FIELD_NOT_STATIC',
"Native fields must be static.",
correctionMessage: "Try adding the modifier 'static' to this field.",
hasPublishedDocs: true,
);
/// Parameters:
/// 0: the name of the function, method, or constructor having type arguments
static const FfiCode NON_CONSTANT_TYPE_ARGUMENT = FfiCode(

View file

@ -611,6 +611,9 @@ const List<ErrorCode> errorCodeValues = [
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
FfiCode.MUST_BE_A_SUBTYPE,
FfiCode.MUST_RETURN_VOID,
FfiCode.NATIVE_FIELD_INVALID_TYPE,
FfiCode.NATIVE_FIELD_MISSING_TYPE,
FfiCode.NATIVE_FIELD_NOT_STATIC,
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
FfiCode.NON_LEAF_CALL_MUST_NOT_TAKE_TYPED_DATA,
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,

View file

@ -181,6 +181,19 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (inCompound) {
_validateFieldsInCompound(node);
}
for (var declared in node.fields.variables) {
var declaredElement = declared.declaredElement;
if (declaredElement != null) {
_checkFfiNative(
errorNode: declared,
declarationElement: declaredElement,
formalParameterList: null,
isExternal: node.externalKeyword != null,
);
}
}
super.visitFieldDeclaration(node);
}
@ -188,9 +201,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
void visitFunctionDeclaration(FunctionDeclaration node) {
_checkFfiNative(
errorNode: node,
annotations: node.declaredElement!.metadata,
declarationElement: node.declaredElement!,
formalParameterList: node.functionExpression.parameters,
isExternal: node.externalKeyword != null,
);
super.visitFunctionDeclaration(node);
}
@ -264,10 +277,11 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
@override
void visitMethodDeclaration(MethodDeclaration node) {
_checkFfiNative(
errorNode: node,
annotations: node.declaredElement!.metadata,
declarationElement: node.declaredElement!,
formalParameterList: node.parameters);
errorNode: node,
declarationElement: node.declaredElement!,
formalParameterList: node.parameters,
isExternal: node.externalKeyword != null,
);
super.visitMethodDeclaration(node);
}
@ -336,17 +350,41 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
super.visitPropertyAccess(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
for (var declared in node.variables.variables) {
var declaredElement = declared.declaredElement;
if (declaredElement != null) {
_checkFfiNative(
errorNode: declared,
declarationElement: declaredElement,
formalParameterList: null,
isExternal: node.externalKeyword != null,
);
}
}
super.visitTopLevelVariableDeclaration(node);
}
DartType? _canonicalFfiTypeForDartType(DartType dartType) {
if (dartType.isPointer || dartType.isCompoundSubtype || dartType.isArray) {
return dartType;
} else {
return null;
}
}
void _checkFfiNative({
required Declaration errorNode,
required List<ElementAnnotation> annotations,
required ExecutableElement declarationElement,
required Element declarationElement,
required FormalParameterList? formalParameterList,
required bool isExternal,
}) {
final formalParameters =
formalParameterList?.parameters ?? <FormalParameter>[];
var hadNativeAnnotation = false;
for (var annotation in annotations) {
for (var annotation in declarationElement.metadata) {
var annotationValue = annotation.computeConstantValue();
var annotationType = annotationValue?.type; // Native<T>
@ -360,124 +398,200 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
var name = (annotation as ElementAnnotationImpl).annotationAst.name;
_errorReporter.reportErrorForNode(
FfiCode.FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS, name, []);
break;
}
hadNativeAnnotation = true;
var ffiSignature = annotationType.typeArguments[0]; // The T in @Native<T>
if (ffiSignature is! FunctionType) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, errorNode, ['T', 'Native']);
return;
}
// Leaf call FFI Natives can't use Handles.
var isLeaf =
annotationValue.getField(_isLeafParamName)?.toBoolValue() ?? false;
if (isLeaf) {
_validateFfiLeafCallUsesNoHandles(ffiSignature, errorNode);
}
if (!declarationElement.isExternal) {
if (!isExternal) {
_errorReporter.reportErrorForNode(
FfiCode.FFI_NATIVE_MUST_BE_EXTERNAL, errorNode);
}
var ffiParameterTypes =
ffiSignature.normalParameterTypes.flattenVarArgs();
var ffiParameters = ffiSignature.parameters;
var ffiSignature = annotationType.typeArguments[0]; // The T in @Native<T>
if ((declarationElement is MethodElement ||
declarationElement is PropertyAccessorElementImpl) &&
!declarationElement.isStatic) {
// Instance methods must have the receiver as an extra parameter in the
// Native annotation.
if (formalParameters.length + 1 != ffiParameterTypes.length) {
_errorReporter.reportErrorForNode(
FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER,
errorNode,
[formalParameters.length + 1, ffiParameterTypes.length]);
return;
}
// Receiver can only be Pointer if the class extends
// NativeFieldWrapperClass1.
if (ffiSignature.normalParameterTypes[0].isPointer) {
final cls = declarationElement.enclosingElement as InterfaceElement;
if (!_extendsNativeFieldWrapperClass1(cls.thisType)) {
_errorReporter.reportErrorForNode(
FfiCode
.FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
errorNode);
}
}
ffiParameterTypes = ffiParameterTypes.sublist(1);
ffiParameters = ffiParameters.sublist(1);
} else {
// Number of parameters in the Native annotation must match the
// annotated declaration.
if (formalParameters.length != ffiParameterTypes.length) {
_errorReporter.reportErrorForNode(
FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS,
errorNode,
[ffiParameterTypes.length, formalParameters.length]);
return;
}
}
// Arguments can only be Pointer if the class extends
// Pointer or NativeFieldWrapperClass1.
for (var i = 0; i < formalParameters.length; i++) {
if (ffiParameterTypes[i].isPointer) {
final type = formalParameters[i].declaredElement!.type;
if (type is! InterfaceType ||
(!type.isPointer &&
!_extendsNativeFieldWrapperClass1(type) &&
!type.isTypedData)) {
_errorReporter.reportErrorForNode(
FfiCode
.FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
errorNode);
}
}
}
final dartType = declarationElement.type;
final nativeType = FunctionTypeImpl(
typeFormals: ffiSignature.typeFormals,
parameters: ffiParameters,
returnType: ffiSignature.returnType,
nullabilitySuffix: ffiSignature.nullabilitySuffix,
);
if (!_isValidFfiNativeFunctionType(nativeType)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
if (ffiSignature is FunctionType) {
if (declarationElement is ExecutableElement) {
_checkFfiNativeFunction(
errorNode,
[nativeType, 'Native']);
return;
declarationElement,
ffiSignature,
annotationValue,
formalParameters,
);
} else {
// Field annotated with a function type, that can't work.
_errorReporter.reportErrorForNode(
FfiCode.NATIVE_FIELD_INVALID_TYPE, errorNode, [ffiSignature]);
}
} else {
if (declarationElement is MethodElement ||
declarationElement is FunctionElement) {
// Function annotated with something that isn't a function type.
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
errorNode,
['T', 'Native']);
} else {
_checkFfiNativeField(
errorNode, declarationElement, ffiSignature, annotationValue);
}
}
if (!_validateCompatibleFunctionTypes(
dartType,
nativeType,
nativeFieldWrappersAsPointer: true,
allowStricterReturn: true,
)) {
_errorReporter.reportErrorForNode(FfiCode.MUST_BE_A_SUBTYPE, errorNode,
[nativeType, dartType, 'Native']);
return;
}
_validateFfiTypedDataUnwrapping(
dartType,
nativeType,
errorNode,
isLeaf: isLeaf,
isCall: true,
);
if (ffiSignature is FunctionType &&
declarationElement is ExecutableElement) {}
}
}
void _checkFfiNativeField(
Declaration errorNode,
Element declarationElement,
DartType ffiSignature,
DartObject annotationValue,
) {
DartType type;
if (declarationElement is FieldElement) {
if (!declarationElement.isStatic) {
_errorReporter.reportErrorForNode(
FfiCode.NATIVE_FIELD_NOT_STATIC, errorNode);
}
type = declarationElement.type;
} else if (declarationElement is TopLevelVariableElement) {
type = declarationElement.type;
} else if (declarationElement is PropertyAccessorElement) {
type = declarationElement.variable.type;
} else {
_errorReporter.reportErrorForNode(
FfiCode.NATIVE_FIELD_NOT_STATIC, errorNode);
return;
}
if (ffiSignature is DynamicType) {
// Attempt to infer the native type from the Dart type.
final canonical = _canonicalFfiTypeForDartType(type);
if (canonical == null) {
_errorReporter.reportErrorForNode(
FfiCode.NATIVE_FIELD_MISSING_TYPE, errorNode);
return;
} else {
ffiSignature = canonical;
}
}
if (!_validateCompatibleNativeType(type, ffiSignature)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE, errorNode, [type, ffiSignature, 'Native']);
} else if (ffiSignature.isArray ||
ffiSignature.isHandle ||
ffiSignature.isNativeFunction) {
_errorReporter.reportErrorForNode(
FfiCode.NATIVE_FIELD_INVALID_TYPE, errorNode, [ffiSignature]);
}
}
void _checkFfiNativeFunction(
Declaration errorNode,
ExecutableElement declarationElement,
FunctionType ffiSignature,
DartObject annotationValue,
List<FormalParameter> formalParameters,
) {
// Leaf call FFI Natives can't use Handles.
var isLeaf =
annotationValue.getField(_isLeafParamName)?.toBoolValue() ?? false;
if (isLeaf) {
_validateFfiLeafCallUsesNoHandles(ffiSignature, errorNode);
}
var ffiParameterTypes = ffiSignature.normalParameterTypes.flattenVarArgs();
var ffiParameters = ffiSignature.parameters;
if ((declarationElement is MethodElement ||
declarationElement is PropertyAccessorElementImpl) &&
!declarationElement.isStatic) {
// Instance methods must have the receiver as an extra parameter in the
// Native annotation.
if (formalParameters.length + 1 != ffiParameterTypes.length) {
_errorReporter.reportErrorForNode(
FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER,
errorNode,
[formalParameters.length + 1, ffiParameterTypes.length]);
return;
}
// Receiver can only be Pointer if the class extends
// NativeFieldWrapperClass1.
if (ffiSignature.normalParameterTypes[0].isPointer) {
final cls = declarationElement.enclosingElement as InterfaceElement;
if (!_extendsNativeFieldWrapperClass1(cls.thisType)) {
_errorReporter.reportErrorForNode(
FfiCode
.FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
errorNode);
}
}
ffiParameterTypes = ffiParameterTypes.sublist(1);
ffiParameters = ffiParameters.sublist(1);
} else {
// Number of parameters in the Native annotation must match the
// annotated declaration.
if (formalParameters.length != ffiParameterTypes.length) {
_errorReporter.reportErrorForNode(
FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS,
errorNode,
[ffiParameterTypes.length, formalParameters.length]);
return;
}
}
// Arguments can only be Pointer if the class extends
// Pointer or NativeFieldWrapperClass1.
for (var i = 0; i < formalParameters.length; i++) {
if (ffiParameterTypes[i].isPointer) {
final type = formalParameters[i].declaredElement!.type;
if (type is! InterfaceType ||
(!type.isPointer &&
!_extendsNativeFieldWrapperClass1(type) &&
!type.isTypedData)) {
_errorReporter.reportErrorForNode(
FfiCode
.FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
errorNode);
}
}
}
final dartType = declarationElement.type;
final nativeType = FunctionTypeImpl(
typeFormals: ffiSignature.typeFormals,
parameters: ffiParameters,
returnType: ffiSignature.returnType,
nullabilitySuffix: ffiSignature.nullabilitySuffix,
);
if (!_isValidFfiNativeFunctionType(nativeType)) {
_errorReporter.reportErrorForNode(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
errorNode, [nativeType, 'Native']);
return;
}
if (!_validateCompatibleFunctionTypes(dartType, nativeType,
nativeFieldWrappersAsPointer: true, allowStricterReturn: true)) {
_errorReporter.reportErrorForNode(FfiCode.MUST_BE_A_SUBTYPE, errorNode,
[nativeType, dartType, 'Native']);
return;
}
_validateFfiTypedDataUnwrapping(
dartType,
nativeType,
errorNode,
isLeaf: isLeaf,
isCall: true,
);
}
bool _extendsNativeFieldWrapperClass1(InterfaceType? type) {
while (type != null) {
if (type.getDisplayString(withNullability: false) ==
@ -1312,7 +1426,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
var validTarget = false;
var referencedElement = switch (argument) {
Identifier() => argument.staticElement,
Identifier() => argument.staticElement?.nonSynthetic,
_ => null,
};
@ -1323,25 +1437,50 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (annotationType is InterfaceType &&
annotationType.element.isNative) {
var functionType = annotationType.typeArguments[0];
var nativeType = annotationType.typeArguments[0];
// 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.reportErrorForNode(
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
node,
[targetType, _nativeAddressOf],
);
if (nativeType is FunctionType) {
// 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.reportErrorForNode(
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
node,
[targetType, _nativeAddressOf],
);
} else {
var targetFunctionType =
(targetType as InterfaceType).typeArguments[0];
if (!typeSystem.isAssignableTo(nativeType, targetFunctionType)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE,
node,
[nativeType, targetFunctionType, _nativeAddressOf],
);
}
}
} else {
var targetFunctionType =
(targetType as InterfaceType).typeArguments[0];
if (!typeSystem.isAssignableTo(functionType, targetFunctionType)) {
// 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) {
final staticType = argument.staticType;
if (staticType != null) {
final canonical = _canonicalFfiTypeForDartType(staticType);
if (canonical != null) {
nativeType = canonical;
}
}
}
if (!typeSystem.isAssignableTo(nativeType, targetType)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE,
node,
[functionType, targetFunctionType, _nativeAddressOf],
[nativeType, targetType, _nativeAddressOf],
);
}
}

View file

@ -18762,8 +18762,164 @@ FfiCode:
```
ARGUMENT_MUST_BE_NATIVE:
problemMessage: "Argument to 'Native.addressOf' must be annotated with @Native"
correctionMessage: "Try passing a static function annotated with '@Native'"
correctionMessage: "Try passing a static function or field annotated with '@Native'"
comment: No parameters
NATIVE_FIELD_INVALID_TYPE:
problemMessage: "'{0}' is an unsupported type for native fields. Native fields only support pointers or numeric and compound types."
correctionMessage: "Try changing the type in the `@Native` annotation to a numeric FFI type, a pointer, or a compound class."
comment: |-
Parameters:
0: The invalid type.
hasPublishedDocs: true
documentation: |-
#### Description
The analyzer produces this diagnostic when an `@Native`-annotated field
has a type not supported for native fields.
Array fields are unsupported because there currently is no size
annotation for native fields. It is possible to represent global array
variables as pointers though, as they have an identical representation in
memory.
Handles are unsupported because there is no way to transparently load and
store Dart object into pointers.
For more information about FFI, see [C interop using dart:ffi][ffi].
#### Example
The following code produces this diagnostic because the field `f` uses an
unsupported type, `Array`:
```dart
import 'dart:ffi';
@Native()
Array<Int> f;
```
#### Common fixes
For array fields, use a pointer instead:
```dart
import 'dart:ffi';
@Native()
Pointer<Int> f;
```
NATIVE_FIELD_MISSING_TYPE:
problemMessage: "The native type of this field could not be inferred and must be specified in the annotation."
correctionMessage: "Try adding a type parameter extending `NativeType` to the `@Native` annotation."
comment: "No parameters"
hasPublishedDocs: true
documentation: |-
#### Description
The analyzer produces this diagnostic when an `@Native`-annotated field
requires a type hint on the annotation to infer the native 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 the correct load and stores when accessing the field, an
explicit type must be given.
#### Example
The following code produces this diagnostic because the field `f` has
the type `int` (for which multiple native representations exist), but no
explicit type parameter on the `Native` annotation:
```dart
import 'dart:ffi';
@Native()
int f;
```
#### Common fixes
To fix this diagnostic, find out the correct native representation from
the native declaration of the field. Then, add the corresponding type to
the annotation. For instance, if `f` was declared as an `uint8_t` in C,
the Dart field should be declared as:
```dart
import 'dart:ffi';
@Native<Uint8>()
int f;
```
For more information about FFI, see [C interop using dart:ffi][ffi].
NATIVE_FIELD_NOT_STATIC:
problemMessage: "Native fields must be static."
correctionMessage: "Try adding the modifier 'static' to this field."
comment: "No parameters"
hasPublishedDocs: true
documentation: |-
#### Description
The analyzer produces this diagnostic when an instance field in a class
has been annotated with `@Native`.
Native fields refer to global variables in C, C++ or other native
languages, whereas instance fields in Dart are specific to an instance of
that class. Hence, native fields must be static.
For more information about FFI, see [C interop using dart:ffi][ffi].
#### Example
The following code produces this diagnostic because the field `f` in the
class `C` is `@Native`, but not `static`:
```dart
import 'dart:ffi';
class C {
@Native<Int>()
external int f;
}
```
#### Common fixes
Either make the field static:
```dart
import 'dart:ffi';
class C {
@Native<Int>()
external static int f;
}
```
Or move it out of a class, in which case no explicit `static` modifier is
required:
```dart
import 'dart:ffi';
class C {
}
@Native<Int>()
external int f;
```
If you meant to annotate an instance field that should be part of a
struct, omit the `@Native` annotation:
```dart
import 'dart:ffi';
class C extends Struct {
@Int()
external int f;
}
```
COMPOUND_IMPLEMENTS_FINALIZABLE:
problemMessage: "The class '{0}' can't implement Finalizable."
correctionMessage: "Try removing the implements clause from '{0}'."
@ -19019,7 +19175,7 @@ FfiCode:
correctionMessage: "Try removing the extra annotation."
comment: No parameters
FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS:
problemMessage: "Native functions must have exactly one `@Native` annotation."
problemMessage: "Native functions and fields must have exactly one `@Native` annotation."
correctionMessage: "Try removing the extra annotation."
comment: No parameters
FIELD_INITIALIZER_IN_STRUCT:

View file

@ -13,6 +13,7 @@ main() {
defineReflectiveTests(AddressOfTest);
defineReflectiveTests(DefaultAssetTest);
defineReflectiveTests(FfiNativeTest);
defineReflectiveTests(NativeFieldTest);
defineReflectiveTests(NativeTest);
});
}
@ -29,6 +30,19 @@ void main() => print(Native.addressOf(() => 3));
]);
}
test_invalid_MismatchedInferredType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native()
external Pointer<IntPtr> global;
void main() => print(Native.addressOf<Pointer<Double>>(global));
''', [
error(FfiCode.MUST_BE_A_SUBTYPE, 85, 41),
]);
}
test_invalid_MismatchingType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@ -93,8 +107,12 @@ import 'dart:ffi';
@Native<Void Function()>()
external void foo();
@Native()
external Pointer<IntPtr> global;
void main() {
print(Native.addressOf<NativeFunction<Void Function()>>(foo));
print(Native.addressOf<Pointer<IntPtr>>(global));
}
''');
}
@ -323,8 +341,143 @@ external double wrongFfiReturnType(int v);
}
}
@reflectiveTest
class NativeFieldTest extends PubPackageResolutionTest {
test_Accessors() async {
await assertNoErrorsInCode(r'''
import 'dart:ffi';
@Native<IntPtr>()
external int get foo;
@Native<IntPtr>()
external set foo(int value);
''');
}
test_Infer() async {
await assertNoErrorsInCode(r'''
import 'dart:ffi';
final class MyStruct extends Struct {
external Pointer<MyStruct> next;
}
@Native()
external MyStruct first;
@Native()
external Pointer<MyStruct> last;
''');
}
test_InvalidFunctionType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native<IntPtr Function(IntPtr)>()
external int field;
''', [
error(FfiCode.NATIVE_FIELD_INVALID_TYPE, 67, 5),
]);
}
test_InvalidInstanceMember() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
class Foo {
@Native<IntPtr>()
external int field;
}
''', [
error(FfiCode.NATIVE_FIELD_NOT_STATIC, 67, 5),
]);
}
test_InvalidNotExternal() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native<IntPtr>()
int field;
''', [
error(CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_VARIABLE, 42, 5),
error(FfiCode.FFI_NATIVE_MUST_BE_EXTERNAL, 42, 5),
]);
}
test_MismatchingType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native<Double>()
external int field;
''', [
error(FfiCode.MUST_BE_A_SUBTYPE, 51, 5),
]);
}
test_MissingType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native()
external int invalid;
@Native()
external Pointer<IntPtr> valid;
''', [
error(FfiCode.NATIVE_FIELD_MISSING_TYPE, 43, 7),
]);
}
test_Unsupported_Array() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native()
external Array<IntPtr> field;
''', [
error(FfiCode.NATIVE_FIELD_INVALID_TYPE, 53, 5),
]);
}
test_Unsupported_Function() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native<NativeFunction<Void Function()>>()
external void Function() field;
''', [
error(FfiCode.MUST_BE_A_SUBTYPE, 88, 5),
]);
}
test_Unsupported_Handle() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native<Handle>()
external Object field;
''', [
error(FfiCode.NATIVE_FIELD_INVALID_TYPE, 54, 5),
]);
}
}
@reflectiveTest
class NativeTest extends PubPackageResolutionTest {
test_annotation_InvalidFieldType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@Native<IntPtr>()
external int foo();
''', [
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 20, 37),
]);
}
test_annotation_MissingType() async {
await assertErrorsInCode(r'''
import 'dart:ffi';

View file

@ -70,6 +70,9 @@ export '../fasta/fasta_codes.dart'
messageFfiLeafCallMustNotReturnHandle,
messageFfiLeafCallMustNotTakeHandle,
messageFfiNativeDuplicateAnnotations,
messageFfiNativeFieldMissingType,
messageFfiNativeFieldMustBeStatic,
messageFfiNativeFieldType,
messageFfiNativeMustBeExternal,
messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
messageFfiNonLeafCallMustNotTakeTypedData,

View file

@ -5239,15 +5239,33 @@ FfiNativeOnlyNativeFieldWrapperClassCanBePointer:
FfiNativeMustBeExternal:
# Used by dart:ffi
problemMessage: "Native functions must be marked external."
problemMessage: "Native functions and fields must be marked external."
external: test/ffi_test.dart
FfiNativeDuplicateAnnotations:
# Used by dart:ffi
problemMessage: "Native functions must not have more than @Native annotation."
problemMessage: "Native functions and fields must not have more than @Native annotation."
external: test/ffi_test.dart
analyzerCode: FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS
FfiNativeFieldMustBeStatic:
# Used by dart:ffi
problemMessage: "Native fields must be static."
analyzerCode: NATIVE_FIELD_NOT_STATIC
external: test/ffi_test.dart
FfiNativeFieldMissingType:
# Used by dart:ffi
problemMessage: "The native type of this field could not be inferred and must be specified in the annotation."
analyzerCode: NATIVE_FIELD_MISSING_TYPE
external: test/ffi_test.dart
FfiNativeFieldType:
# Used by dart:ffi
problemMessage: "Unsupported type for native fields. Native fields only support pointers, compounds and numeric types."
analyzerCode: NATIVE_FIELD_INVALID_TYPE
external: test/ffi_test.dart
FfiAddressOfMustBeNative:
# Used by dart:ffi
problemMessage: "Argument to 'Native.addressOf' must be annotated with @Native."

View file

@ -29,6 +29,7 @@ cast
collide
compilercontext.runincontext
compilesdk
compounds
conformance
constructor(s)
core

View file

@ -47,9 +47,9 @@ class Coordinate extends ffi::Struct {
set y(synthesized core::double* #v) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #v);
get next() → ffi::Pointer<self::Coordinate*>*
return ffi::_fromAddress<self::Coordinate*>(ffi::_loadAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}));
return ffi::_loadPointer<self::Coordinate*>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
set next(synthesized ffi::Pointer<self::Coordinate*>* #v) → void
return ffi::_storeAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #v.{ffi::Pointer::address}{core::int});
return ffi::_storePointer<self::Coordinate*>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #v);
@#C16
static get /*isNonNullableByDefault*/ #sizeOf() → core::int*
return #C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};

View file

@ -40,9 +40,9 @@ class Coordinate extends ffi::Struct {
set y(synthesized core::double* #v) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #v);
get next() → ffi::Pointer<self::Coordinate*>*
return ffi::_fromAddress<self::Coordinate*>(ffi::_loadAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}));
return ffi::_loadPointer<self::Coordinate*>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
set next(synthesized ffi::Pointer<self::Coordinate*>* #v) → void
return ffi::_storeAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #v.{ffi::Pointer::address}{core::int});
return ffi::_storePointer<self::Coordinate*>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #v);
@#C16
static get /*isNonNullableByDefault*/ #sizeOf() → core::int*
return #C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};

View file

@ -21,9 +21,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
set y(synthesized dart.core::double #externalFieldValue) → void
return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get next() → dart.ffi::Pointer<lib::Coordinate>
return dart.ffi::_fromAddress<lib::Coordinate>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set next(synthesized dart.ffi::Pointer<lib::Coordinate> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
static factory allocate(dart.core::double x, dart.core::double y, dart.ffi::Pointer<lib::Coordinate>? next) → lib::Coordinate {
throw "";
}

View file

@ -21,9 +21,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
set y(synthesized dart.core::double #externalFieldValue) → void
return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get next() → dart.ffi::Pointer<lib::Coordinate>
return dart.ffi::_fromAddress<lib::Coordinate>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set next(synthesized dart.ffi::Pointer<lib::Coordinate> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
static factory allocate(dart.core::double x, dart.core::double y, dart.ffi::Pointer<lib::Coordinate>? next) → lib::Coordinate {
throw "";
}

View file

@ -21,9 +21,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
set y(synthesized dart.core::double #externalFieldValue) → void
return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get next() → dart.ffi::Pointer<lib::Coordinate>
return dart.ffi::_fromAddress<lib::Coordinate>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set next(synthesized dart.ffi::Pointer<lib::Coordinate> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
static factory allocate(dart.core::double x, dart.core::double y, dart.ffi::Pointer<lib::Coordinate>? next) → lib::Coordinate {
throw "";
}

View file

@ -12,17 +12,17 @@ library from "org-dartlang-test:///a.dart" as a {
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
get a1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get a2() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a2(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get a3() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a3(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get blah() → a::NestedStruct
return new a::NestedStruct::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};
@ -43,17 +43,17 @@ library from "org-dartlang-test:///a.dart" as a {
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
get n1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get n2() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n2(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get n3() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n3(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
@#C19
static get #sizeOf() → dart.core::int*
return #C17.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*};

View file

@ -12,17 +12,17 @@ library from "org-dartlang-test:///a.dart" as a {
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
get a1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get a2() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a2(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get a3() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a3(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get blah() → a::NestedStruct
return new a::NestedStruct::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};
@ -43,17 +43,17 @@ library from "org-dartlang-test:///a.dart" as a {
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
get n1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get n2() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n2(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get n3() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_fromAddress<dart.ffi::Void>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n3(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
@#C19
static get #sizeOf() → dart.core::int*
return #C17.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*};

View file

@ -21,9 +21,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
set y(synthesized dart.core::double #externalFieldValue) → void
return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get next() → dart.ffi::Pointer<lib::Coordinate>
return dart.ffi::_fromAddress<lib::Coordinate>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set next(synthesized dart.ffi::Pointer<lib::Coordinate> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
static factory allocate(dart.core::double x, dart.core::double y, dart.ffi::Pointer<lib::Coordinate>? next) → lib::Coordinate {
throw "";
}

View file

@ -21,9 +21,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
set y(synthesized dart.core::double #externalFieldValue) → void
return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get next() → dart.ffi::Pointer<lib::Coordinate>
return dart.ffi::_fromAddress<lib::Coordinate>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set next(synthesized dart.ffi::Pointer<lib::Coordinate> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
static factory allocate(dart.core::double x, dart.core::double y, dart.ffi::Pointer<lib::Coordinate>? next) → lib::Coordinate {
throw "";
}

View file

@ -21,9 +21,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
set y(synthesized dart.core::double #externalFieldValue) → void
return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C12.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get next() → dart.ffi::Pointer<lib::Coordinate>
return dart.ffi::_fromAddress<lib::Coordinate>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set next(synthesized dart.ffi::Pointer<lib::Coordinate> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<lib::Coordinate>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C14.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
static factory allocate(dart.core::double x, dart.core::double y, dart.ffi::Pointer<lib::Coordinate>? next) → lib::Coordinate {
dart.core::print("hello");
throw "";

View file

@ -12,9 +12,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
get lpVtbl() → dart.ffi::Pointer<dart.ffi::IntPtr>
return dart.ffi::_fromAddress<dart.ffi::IntPtr>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set lpVtbl(synthesized dart.ffi::Pointer<dart.ffi::IntPtr> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get vtable() → dart.ffi::Pointer<dart.ffi::IntPtr>
return dart.ffi::Pointer::fromAddress<dart.ffi::IntPtr>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{lib::COMObject::lpVtbl}{dart.ffi::Pointer<dart.ffi::IntPtr>}, #C7));
@#C10

View file

@ -12,9 +12,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
get lpVtbl() → dart.ffi::Pointer<dart.ffi::IntPtr>
return dart.ffi::_fromAddress<dart.ffi::IntPtr>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}));
return dart.ffi::_loadPointer<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set lpVtbl(synthesized dart.ffi::Pointer<dart.ffi::IntPtr> #externalFieldValue) → void
return dart.ffi::_storeAbiSpecificInt<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue.{dart.ffi::Pointer::address}{dart.core::int});
return dart.ffi::_storePointer<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*}, #externalFieldValue);
get vtable() → dart.ffi::Pointer<dart.ffi::IntPtr>
return dart.ffi::Pointer::fromAddress<dart.ffi::IntPtr>(dart.ffi::_loadAbiSpecificInt<dart.ffi::IntPtr>(this.{lib::COMObject::lpVtbl}{dart.ffi::Pointer<dart.ffi::IntPtr>}, #C7));
@#C10

View file

@ -24,9 +24,9 @@ final class Coordinate extends ffi::Struct {
set y(synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
get next() → ffi::Pointer<self::Coordinate>
return ffi::_fromAddress<self::Coordinate>(ffi::_loadAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}));
return ffi::_loadPointer<self::Coordinate>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
set next(synthesized ffi::Pointer<self::Coordinate> #externalFieldValue) → void
return ffi::_storeAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::Pointer::address}{core::int});
return ffi::_storePointer<self::Coordinate>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
static factory allocate(ffi::Allocator allocator, core::double x, core::double y, ffi::Pointer<self::Coordinate> next) → self::Coordinate {
return let final self::Coordinate #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}<self::Coordinate>(self::Coordinate::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Coordinate>}!) in block {
#t1.{self::Coordinate::x} = x;

View file

@ -24,9 +24,9 @@ final class Coordinate extends ffi::Struct {
set y(synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
get next() → ffi::Pointer<self::Coordinate>
return ffi::_fromAddress<self::Coordinate>(ffi::_loadAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}));
return ffi::_loadPointer<self::Coordinate>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
set next(synthesized ffi::Pointer<self::Coordinate> #externalFieldValue) → void
return ffi::_storeAbiSpecificInt<ffi::IntPtr>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::Pointer::address}{core::int});
return ffi::_storePointer<self::Coordinate>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
static factory allocate(ffi::Allocator allocator, core::double x, core::double y, ffi::Pointer<self::Coordinate> next) → self::Coordinate {
return let final self::Coordinate #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}<self::Coordinate>(self::Coordinate::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Coordinate>}!) in block {
#t1.{self::Coordinate::x} = x;

View file

@ -642,6 +642,10 @@ class FfiTransformer extends Transformer {
/// If [dartType] is provided, it can be used to guide what Dart type is
/// expected. Currently, this is only used if [allowTypedData] is provided,
/// because `Pointer` is a one to many mapping in this case.
///
/// Some Dart types only have one possible native type (and vice-versa). For
/// those types, [convertNativeTypeToDartType] is the inverse of
/// [convertDartTypeToNativeType].
DartType? convertNativeTypeToDartType(
DartType nativeType, {
DartType? dartType,
@ -761,6 +765,25 @@ class FfiTransformer extends Transformer {
return FunctionType(argumentTypes, returnType, Nullability.legacy);
}
/// Finds a native type for the given [dartType] if there is only one possible
/// native type.
///
/// This is impossible for some types (like [int] which needs a specific ffi
/// type to denote the width in C). This method returns `null` for those
/// types.
///
/// For types where this returns a non-null value, this is the inverse of
/// [convertNativeTypeToDartType].
DartType? convertDartTypeToNativeType(DartType dartType) {
if (isPointerType(dartType) ||
isCompoundSubtype(dartType) ||
isArrayType(dartType)) {
return dartType;
} else {
return null;
}
}
/// Removes the VarArgs from a DartType list.
///
/// ```
@ -933,6 +956,14 @@ class FfiTransformer extends Transformer {
/// ```
Expression typedDataBaseOffset(Expression typedDataBase, Expression offset,
Expression length, DartType dartType, int fileOffset) {
// Avoid generating the branch on the kind of typed data and the offset
// calculation if the end result is a no-op. This offset-generating method
// is used to load compound subtypes, which in many cases are not using any
// offset from their base.
if (offset case ConstantExpression(constant: IntConstant(value: 0))) {
return typedDataBase;
}
final typedDataBaseVar = VariableDeclaration("#typedDataBase",
initializer: typedDataBase,
type: coreTypes.objectNonNullableRawType,
@ -1129,6 +1160,27 @@ class FfiTransformer extends Transformer {
return null;
}
Expression? inlineSizeOf(InterfaceType nativeType) {
final Class nativeClass = nativeType.classNode;
final NativeType? nt = getType(nativeClass);
if (nt == null) {
// User-defined compounds.
final Procedure sizeOfGetter = nativeClass.procedures
.firstWhere((function) => function.name == Name('#sizeOf'));
return StaticGet(sizeOfGetter);
}
final int size = nativeTypeSizes[nt]!;
if (size == WORD_SIZE) {
return runtimeBranchOnLayout(wordSize);
}
if (size != UNKNOWN) {
return ConstantExpression(IntConstant(size),
InterfaceType(intClass, currentLibrary.nonNullable));
}
// Size unknown.
return null;
}
/// Generates an expression performing an Abi specific integer load or store.
///
/// If [value] is provided, it is a store, otherwise a load.
@ -1400,12 +1452,14 @@ class FfiTransformer extends Transformer {
bool allowHandle = false,
bool allowVoid = false,
bool allowTypedData = false,
bool allowArray = false,
}) {
final DartType correspondingDartType = convertNativeTypeToDartType(
nativeType,
dartType: dartType,
allowCompounds: true,
allowHandle: allowHandle,
allowInlineArray: allowArray,
allowVoid: allowVoid,
allowTypedData: allowTypedData,
)!;

View file

@ -429,9 +429,9 @@ class _FfiDefinitionTransformer extends FfiTransformer {
f.fileUri);
// This class is invalid, but continue reporting other errors on it.
success = false;
} else if (isPointerType(type) ||
isCompoundSubtype(type) ||
isArrayType(type)) {
} else if (convertDartTypeToNativeType(type) != null) {
// If the native type is obvious from the Dart type alone, don't allow
// a native type annotation.
if (nativeTypeAnnos.isNotEmpty) {
diagnosticReporter.report(
templateFfiFieldNoAnnotation.withArguments(f.name.text),

View file

@ -6,12 +6,16 @@ import 'package:front_end/src/api_unstable/vm.dart'
show
messageFfiDefaultAssetDuplicate,
messageFfiNativeDuplicateAnnotations,
messageFfiNativeFieldMissingType,
messageFfiNativeFieldMustBeStatic,
messageFfiNativeFieldType,
messageFfiNativeMustBeExternal,
messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
templateCantHaveNamedParameters,
templateCantHaveOptionalParameters,
templateFfiNativeUnexpectedNumberOfParameters,
templateFfiNativeUnexpectedNumberOfParametersWithReceiver;
templateFfiNativeUnexpectedNumberOfParametersWithReceiver,
templateFfiTypeInvalid;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
@ -21,7 +25,8 @@ import 'package:kernel/reference_from_index.dart' show ReferenceFromIndex;
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
import 'package:kernel/type_environment.dart';
import 'common.dart' show FfiStaticTypeError, FfiTransformer;
import 'common.dart' show FfiStaticTypeError, FfiTransformer, NativeType;
import 'native_type_cfe.dart';
/// Transform @Native annotated functions into FFI native function pointer
/// functions.
@ -163,6 +168,26 @@ class FfiNativeTransformer extends FfiTransformer {
return false;
}
StringConstant _resolveNativeSymbolName(
Member member, InstanceConstant native) {
final nativeFunctionConst =
native.fieldValues[nativeSymbolField.fieldReference];
return nativeFunctionConst is StringConstant
? nativeFunctionConst
: StringConstant(member.name.text);
}
StringConstant? _assetNameFromAnnotation(InstanceConstant native) {
final assetConstant = native.fieldValues[nativeAssetField.fieldReference];
return assetConstant is StringConstant ? assetConstant : null;
}
bool _isLeaf(InstanceConstant native) {
return (native.fieldValues[nativeIsLeafField.fieldReference]
as BoolConstant)
.value;
}
// Replaces parameters with Pointer if:
// 1) they extend NativeFieldWrapperClass1, and
// 2) the corresponding FFI parameter is Pointer.
@ -444,7 +469,7 @@ class FfiNativeTransformer extends FfiTransformer {
Procedure _transformProcedure(
Procedure node,
StringConstant nativeFunctionName,
StringConstant? assetName,
StringConstant? overriddenAssetName,
bool isLeaf,
int annotationOffset,
FunctionType dartFunctionType,
@ -462,15 +487,11 @@ class FfiNativeTransformer extends FfiTransformer {
return node;
}
final resolvedNative = InstanceConstant(
nativeClass.reference,
[ffiFunctionType],
{
nativeSymbolField.fieldReference: nativeFunctionName,
nativeAssetField.fieldReference:
assetName ?? StringConstant(currentLibrary.importUri.toString()),
nativeIsLeafField.fieldReference: BoolConstant(isLeaf),
},
final resolvedNative = _generateResolvedNativeConstant(
nativeType: ffiFunctionType,
nativeName: nativeFunctionName,
isLeaf: isLeaf,
overriddenAssetName: overriddenAssetName,
);
final pragmaConstant = ConstantExpression(
InstanceConstant(pragmaClass.reference, [], {
@ -584,6 +605,29 @@ class FfiNativeTransformer extends FfiTransformer {
return node;
}
/// Creates a `Native` constant with all fields resolved.
///
/// Re-using the constant from the original `@Native` annotation doesn't work
/// because the asset field may be inferred from the library.
InstanceConstant _generateResolvedNativeConstant({
required DartType nativeType,
required StringConstant nativeName,
required bool isLeaf,
StringConstant? overriddenAssetName,
}) {
return InstanceConstant(
nativeClass.reference,
[nativeType],
{
nativeSymbolField.fieldReference: nativeName,
nativeAssetField.fieldReference: overriddenAssetName ??
currentAsset ??
StringConstant(currentLibrary.importUri.toString()),
nativeIsLeafField.fieldReference: BoolConstant(isLeaf),
},
);
}
// Transform Native instance methods.
//
// Example:
@ -665,7 +709,7 @@ class FfiNativeTransformer extends FfiTransformer {
Procedure node,
FunctionType ffiFunctionType,
StringConstant nativeFunctionName,
StringConstant? assetName,
StringConstant? overriddenAssetName,
bool isLeaf,
int annotationOffset) {
final dartFunctionType =
@ -679,7 +723,7 @@ class FfiNativeTransformer extends FfiTransformer {
return _transformProcedure(
node,
nativeFunctionName,
assetName,
overriddenAssetName,
isLeaf,
annotationOffset,
dartFunctionType,
@ -689,6 +733,75 @@ class FfiNativeTransformer extends FfiTransformer {
);
}
Expression _generateAddressOfField(
Member node, InterfaceType ffiType, InstanceConstant native) {
return StaticInvocation(
nativePrivateAddressOf,
Arguments([ConstantExpression(native)], types: [ffiType]),
)..fileOffset = node.fileOffset;
}
DartType _validateOrInferNativeFieldType(
Member node, DartType ffiType, DartType dartType) {
if (ffiType is DynamicType) {
// If no type argument is given on the @Native annotation, try to infer
// it.
final inferred = convertDartTypeToNativeType(dartType);
if (inferred != null) {
ffiType = inferred;
} else {
diagnosticReporter.report(messageFfiNativeFieldMissingType,
node.fileOffset, 1, node.location?.file);
throw FfiStaticTypeError();
}
}
ensureNativeTypeValid(
ffiType,
node,
allowCompounds: true,
// Handles and arrays are not currently supported, but checking them
// separately and allowing them here yields a more specific error message.
allowHandle: true,
allowInlineArray: true,
);
ensureNativeTypeToDartType(ffiType, dartType, node,
allowHandle: true, allowArray: true);
// Only allow compound, pointer and numeric types.
if (isCompoundSubtype(ffiType) || isAbiSpecificIntegerSubtype(ffiType)) {
return ffiType;
}
final type = switch (ffiType) {
InterfaceType(:var classNode) => getType(classNode),
_ => null,
};
if (type == null ||
type == NativeType.kNativeFunction ||
type == NativeType.kHandle ||
isArrayType(ffiType)) {
diagnosticReporter.report(
messageFfiNativeFieldType, node.fileOffset, 1, node.location?.file);
throw FfiStaticTypeError();
}
return ffiType;
}
@override
TreeNode visitField(Field node) {
final nativeAnnotation = tryGetNativeAnnotationOrWarnOnDuplicates(node);
if (nativeAnnotation == null) {
return node;
}
// @Native annotations on fields are valid if the field is external. But
// external fields are represented as a getter/setter pair in Kernel, so
// only visit fields to verify that no native annotation is present.
assert(!node.isExternal);
diagnosticReporter.report(messageFfiNativeMustBeExternal, node.fileOffset,
1, node.location?.file);
return node;
}
@override
visitProcedure(Procedure node) {
// Only transform functions that are external and have Native annotation:
@ -708,37 +821,102 @@ class FfiNativeTransformer extends FfiTransformer {
node.annotations.remove(ffiNativeAnnotation);
final ffiConstant = ffiNativeAnnotation.constant as InstanceConstant;
final nativeType = ffiConstant.typeArguments[0];
try {
final nativeFunctionType = InterfaceType(
nativeFunctionClass, Nullability.nonNullable, [nativeType]);
ensureNativeTypeValid(nativeFunctionType, ffiNativeAnnotation,
allowCompounds: true, allowHandle: true);
} on FfiStaticTypeError {
// We've already reported an error.
return node;
}
final ffiFunctionType = ffiConstant.typeArguments[0] as FunctionType;
final nativeFunctionConst =
ffiConstant.fieldValues[nativeSymbolField.fieldReference];
final nativeFunctionName = nativeFunctionConst is StringConstant
? nativeFunctionConst
: StringConstant(node.name.text);
final assetConstant =
ffiConstant.fieldValues[nativeAssetField.fieldReference];
final assetName =
assetConstant is StringConstant ? assetConstant : currentAsset;
final isLeaf = (ffiConstant.fieldValues[nativeIsLeafField.fieldReference]
as BoolConstant)
.value;
var nativeType = ffiConstant.typeArguments[0];
if (!node.isStatic) {
return _transformInstanceMethod(node, ffiFunctionType, nativeFunctionName,
assetName, isLeaf, ffiNativeAnnotation.fileOffset);
final nativeName = _resolveNativeSymbolName(node, ffiConstant);
final overriddenAssetName = _assetNameFromAnnotation(ffiConstant);
final isLeaf = _isLeaf(ffiConstant);
if (nativeType is FunctionType) {
try {
final nativeFunctionType = InterfaceType(
nativeFunctionClass, Nullability.nonNullable, [nativeType]);
ensureNativeTypeValid(nativeFunctionType, ffiNativeAnnotation,
allowCompounds: true, allowHandle: true);
} on FfiStaticTypeError {
// We've already reported an error.
return node;
}
final ffiFunctionType = ffiConstant.typeArguments[0] as FunctionType;
if (!node.isStatic) {
return _transformInstanceMethod(node, ffiFunctionType, nativeName,
overriddenAssetName, isLeaf, ffiNativeAnnotation.fileOffset);
}
return _transformStaticFunction(node, ffiFunctionType, nativeName,
overriddenAssetName, isLeaf, ffiNativeAnnotation.fileOffset);
} else if (node.kind == ProcedureKind.Getter ||
node.kind == ProcedureKind.Setter) {
if (!node.isStatic) {
diagnosticReporter.report(messageFfiNativeFieldMustBeStatic,
node.fileOffset, 1, node.location?.file);
}
DartType dartType;
try {
dartType = node.kind == ProcedureKind.Getter
? node.function.returnType
: node.function.positionalParameters[0].type;
nativeType =
_validateOrInferNativeFieldType(node, nativeType, dartType);
} on FfiStaticTypeError {
return node;
}
final resolved = _generateResolvedNativeConstant(
nativeType: nativeType,
nativeName: nativeName,
isLeaf: isLeaf,
overriddenAssetName: overriddenAssetName,
);
node.isExternal = false;
final nativeTypeCfe = NativeTypeCfe.withoutLayout(this, nativeType);
final zeroOffset = ConstantExpression(IntConstant(0));
if (node.kind == ProcedureKind.Getter) {
node.function.body = ReturnStatement(nativeTypeCfe.generateLoad(
dartType: dartType,
fileOffset: node.fileOffset,
typedDataBase: _generateAddressOfField(
node, nativeType as InterfaceType, resolved),
transformer: this,
offsetInBytes: zeroOffset,
));
node.annotations.add(ConstantExpression(
InstanceConstant(pragmaClass.reference, [], {
pragmaName.fieldReference: StringConstant(nativeMarker),
pragmaOptions.fieldReference: resolved,
}),
InterfaceType(
pragmaClass,
Nullability.nonNullable,
[],
),
));
} else {
node.function.body = ExpressionStatement(nativeTypeCfe.generateStore(
VariableGet(node.function.positionalParameters[0]),
dartType: dartType,
fileOffset: node.fileOffset,
typedDataBase: _generateAddressOfField(
node, nativeType as InterfaceType, resolved),
transformer: this,
offsetInBytes: zeroOffset,
));
}
} else {
// This function is not annotated with a native function type, which is
// invalid.
diagnosticReporter.report(
templateFfiTypeInvalid.withArguments(
nativeType, currentLibrary.isNonNullableByDefault),
node.fileOffset,
1,
node.location?.file);
}
return _transformStaticFunction(node, ffiFunctionType, nativeFunctionName,
assetName, isLeaf, ffiNativeAnnotation.fileOffset);
return node;
}
/// Checks whether the FFI function type is valid and reports any errors.

View file

@ -13,7 +13,21 @@ import 'common.dart';
///
/// This algebraic data structure does not stand on its own but refers
/// intimately to AST nodes such as [Class].
abstract class NativeTypeCfe {
sealed class NativeTypeCfe {
NativeTypeCfe._();
/// Constructs a [NativeTypeCfe] for transformers that can refer to types
/// without having to know their internal layout or size.
factory NativeTypeCfe.withoutLayout(
FfiTransformer transformer, DartType dartType) {
if (transformer.isCompoundSubtype(dartType)) {
return ReferencedCompoundSubtypeCfe(
(dartType as InterfaceType).classNode);
} else {
return NativeTypeCfe(transformer, dartType);
}
}
factory NativeTypeCfe(FfiTransformer transformer, DartType dartType,
{List<int>? arrayDimensions,
Map<Class, NativeTypeCfe> compoundCache = const {},
@ -107,11 +121,72 @@ abstract class NativeTypeCfe {
/// See runtime/vm/compiler/ffi/native_type.cc:NativeType::FromAbstractType.
Constant generateConstant(FfiTransformer transformer);
/// Generates an expression evaluating to an instance of [dartType], which is
/// assumed to be a Dart type compatible to this native type, by loading this
/// instance from memory.
///
/// [typedDataBase] is an expression evaluating to a `Pointer` or `TypedData`,
/// the type will be loaded from that buffer, starting at [offsetInBytes].
///
/// For example, loading a `Pointer` from memory (via [PointerNativeTypeCfe])
/// would build an expression like `ffi._loadPointer<T>
/// (#typedDataBase, #offsetInBytes)`, where `Pointer<T> == dartType`.
///
/// For struct fields, [generateGetterStatement] fills in values for
/// [offsetInBytes] and [unaligned] based on the ABI of the struct. It also
/// wraps the expression in a return statement.
Expression generateLoad({
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
});
/// Generates an expression storing [value], which must evaluate to a
/// [dartType] compatible with this native type, in native memory.
///
/// [typedDataBase] is an expression evaluating to a `Pointer` or `TypedData`,
/// the [value] will be stored in that buffer from [offsetInBytes].
///
/// For example, storing a `Pointer` (via [PointerNativeTypeCfe]) would
/// generate a call to `ffi._storePointer`.
///
/// For struct fields, [generateSetterStatement] fills in values for
/// [offsetInBytes] and [unaligned] based on the ABI of the struct. It also
/// wraps the expression in a return statement.
Expression generateStore(
Expression value, {
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
});
/// Generates the return statement for a compound field getter with this type.
///
/// Takes [transformer] to be able to lookup classes and methods.
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
Map<Abi, int?> offsets, bool unalignedAccess, FfiTransformer transformer);
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) {
return ReturnStatement(
generateLoad(
dartType: dartType,
fileOffset: fileOffset,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer: transformer,
unaligned: unalignedAccess,
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
),
);
}
/// Generates the return statement for a compound field setter with this type.
///
@ -122,13 +197,24 @@ abstract class NativeTypeCfe {
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer);
FfiTransformer transformer) {
return ReturnStatement(generateStore(
VariableGet(argument)..fileOffset = fileOffset,
dartType: dartType,
fileOffset: fileOffset,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer: transformer,
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
unaligned: unalignedAccess,
));
}
}
class InvalidNativeTypeCfe implements NativeTypeCfe {
final class InvalidNativeTypeCfe extends NativeTypeCfe {
final String reason;
InvalidNativeTypeCfe(this.reason);
InvalidNativeTypeCfe(this.reason) : super._();
@override
Map<Abi, int?> get alignment => throw reason;
@ -140,23 +226,29 @@ class InvalidNativeTypeCfe implements NativeTypeCfe {
Constant generateConstant(FfiTransformer transformer) => throw reason;
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) =>
throw reason;
Expression generateLoad({
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
throw reason;
}
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
throw reason;
Expression generateStore(
Expression value, {
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
throw reason;
}
@override
Map<Abi, int?> get size => throw reason;
@ -165,12 +257,12 @@ class InvalidNativeTypeCfe implements NativeTypeCfe {
int? getSizeFor(Abi abi) => throw reason;
}
class PrimitiveNativeTypeCfe implements NativeTypeCfe {
class PrimitiveNativeTypeCfe extends NativeTypeCfe {
final NativeType nativeType;
final Class clazz;
PrimitiveNativeTypeCfe(this.nativeType, this.clazz);
PrimitiveNativeTypeCfe(this.nativeType, this.clazz) : super._();
@override
Map<Abi, int?> get size {
@ -219,56 +311,59 @@ class PrimitiveNativeTypeCfe implements NativeTypeCfe {
return false;
}
/// Sample output for `int get x =>`:
/// Sample output for [nativeType] being [NativeType.kInt8]:
///
/// ```
/// _loadInt8(_typedDataBase, offset);
/// _loadInt8(#typedDataBase, #offsetInBytes)
/// ```
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
(unalignedAccess && isFloat
? transformer.loadUnalignedMethods
: transformer.loadMethods)[nativeType]!,
Arguments([
transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer.runtimeBranchOnLayout(offsets)
]))
..fileOffset = fileOffset);
Expression generateLoad({
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
return StaticInvocation(
(unaligned && isFloat
? transformer.loadUnalignedMethods
: transformer.loadMethods)[nativeType]!,
Arguments([typedDataBase, offsetInBytes]))
..fileOffset = fileOffset;
}
/// Sample output for `set x(int #v) =>`:
/// Sample output for [nativeType] being [NativeType.kInt8]:
///
/// ```
/// _storeInt8(_typedDataBase, offset, #v);
/// _storeInt8(#typedDataBase, #offsetInBytes, #value)
/// ```
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
(unalignedAccess && isFloat
? transformer.storeUnalignedMethods
: transformer.storeMethods)[nativeType]!,
Arguments([
transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer.runtimeBranchOnLayout(offsets),
VariableGet(argument)
]))
..fileOffset = fileOffset);
Expression generateStore(
Expression value, {
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
return StaticInvocation(
(unaligned && isFloat
? transformer.storeUnalignedMethods
: transformer.storeMethods)[nativeType]!,
Arguments([
typedDataBase,
offsetInBytes,
value,
]))
..fileOffset = fileOffset;
}
}
class PointerNativeTypeCfe implements NativeTypeCfe {
class PointerNativeTypeCfe extends NativeTypeCfe {
PointerNativeTypeCfe() : super._();
@override
Map<Abi, int?> get size => wordSize;
@ -288,66 +383,61 @@ class PointerNativeTypeCfe implements NativeTypeCfe {
transformer.pointerClass.superclass!, Nullability.nonNullable)
]));
/// Sample output for `Pointer<Int8> get x =>`:
/// Sample output:
///
/// ```
/// _fromAddress<Int8>(_loadAbiSpecificInt<IntPtr>(_typedDataBase, offset));
/// _loadPointer<#dartType>(#typedDataBase, #offsetInBytes);
/// ```
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
transformer.fromAddressInternal,
Arguments([
transformer.abiSpecificLoadOrStoreExpression(
transformer.intptrNativeTypeCfe,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
fileOffset: fileOffset,
),
], types: [
(dartType as InterfaceType).typeArguments.single
]))
..fileOffset = fileOffset);
Expression generateLoad({
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
return StaticInvocation(
transformer.loadMethods[NativeType.kPointer]!,
Arguments([
typedDataBase,
offsetInBytes,
], types: [
(dartType as InterfaceType).typeArguments.single
]),
)..fileOffset = fileOffset;
}
/// Sample output for `set x(Pointer<Int8> #v) =>`:
/// Sample output:
///
/// ```
/// _storeAbiSpecificInt<IntPtr>(
/// _typedDataBase,
/// offset,
/// (#v as Pointer<Int8>).address,
/// _storePointer<#dartType>(
/// #typedDataBase,
/// #offsetInBytes,
/// (#value as Pointer<#dartType>),
/// );
/// ```
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(
transformer.abiSpecificLoadOrStoreExpression(
transformer.intptrNativeTypeCfe,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
value: InstanceGet(
InstanceAccessKind.Instance,
VariableGet(argument),
transformer.addressGetter.name,
interfaceTarget: transformer.addressGetter,
resultType: transformer.addressGetter.getterType,
)..fileOffset = fileOffset,
fileOffset: fileOffset,
),
);
Expression generateStore(
Expression value, {
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
return StaticInvocation(
transformer.storeMethods[NativeType.kPointer]!,
Arguments([
typedDataBase,
offsetInBytes,
value,
], types: [
(dartType as InterfaceType).typeArguments.single
]),
)..fileOffset = fileOffset;
}
}
/// The layout of a `Struct` or `Union` in one [Abi].
@ -366,14 +456,94 @@ class CompoundLayout {
CompoundLayout(this.size, this.alignment, this.offsets);
}
abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
abstract mixin class _CompoundLoadAndStoreMixin implements NativeTypeCfe {
Class get clazz;
bool get knowsLayout;
/// Generates an expression evaluating to the size of this compound subtype in
/// bytes.
///
/// If we know the size, we can construct a constant or a runtime lookup based
/// on the ABI. Otherwise, we'll look it up from the `#size` field generated
/// by the definitions transformer.
Expression _generateSize(FfiTransformer transformer) {
if (knowsLayout) {
return transformer.runtimeBranchOnLayout(size);
} else {
return transformer.inlineSizeOf(
clazz.getThisType(transformer.coreTypes, Nullability.nonNullable))!;
}
}
/// Sample output for `MyStruct`:
///
/// ```
/// MyStruct.#fromTypedDataBase(
/// typedDataBaseOffset(#typedDataBase, #offsetInBytes, size)
/// );
/// ```
@override
Expression generateLoad({
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
final constructor = clazz.constructors
.firstWhere((c) => c.name == Name("#fromTypedDataBase"));
return ConstructorInvocation(
constructor,
Arguments([
transformer.typedDataBaseOffset(typedDataBase, offsetInBytes,
_generateSize(transformer), dartType, fileOffset)
]))
..fileOffset = fileOffset;
}
/// Sample output for `set x(MyStruct #v) =>`:
///
/// ```
/// _memCopy(#typedDataBase, #offsetInBytes, #v._typedDataBase, 0, size);
/// ```
@override
Expression generateStore(
Expression value, {
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
return StaticInvocation(
transformer.memCopy,
Arguments([
typedDataBase,
offsetInBytes,
transformer.getCompoundTypedDataBaseField(value, fileOffset),
ConstantExpression(IntConstant(0)),
_generateSize(transformer),
]))
..fileOffset = fileOffset;
}
}
abstract class CompoundNativeTypeCfe extends NativeTypeCfe
with _CompoundLoadAndStoreMixin {
@override
final Class clazz;
final List<NativeTypeCfe> members;
final Map<Abi, CompoundLayout> layout;
CompoundNativeTypeCfe._(this.clazz, this.members, this.layout);
@override
bool get knowsLayout => true;
CompoundNativeTypeCfe._(this.clazz, this.members, this.layout) : super._();
@override
Map<Abi, int?> get size =>
@ -392,63 +562,6 @@ abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
@override
Constant generateConstant(FfiTransformer transformer) =>
TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
/// Sample output for `MyStruct get x =>`:
///
/// ```
/// MyStruct.#fromTypedDataBase(
/// typedDataBaseOffset(_typedDataBase, offset, size, dartType)
/// );
/// ```
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) {
final constructor = clazz.constructors
.firstWhere((c) => c.name == Name("#fromTypedDataBase"));
return ReturnStatement(ConstructorInvocation(
constructor,
Arguments([
transformer.typedDataBaseOffset(
transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer.runtimeBranchOnLayout(offsets),
transformer.runtimeBranchOnLayout(size),
dartType,
fileOffset)
]))
..fileOffset = fileOffset);
}
/// Sample output for `set x(MyStruct #v) =>`:
///
/// ```
/// _memCopy(_typedDataBase, offset, #v._typedDataBase, 0, size);
/// ```
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
transformer.memCopy,
Arguments([
transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer.runtimeBranchOnLayout(offsets),
transformer.getCompoundTypedDataBaseField(
VariableGet(argument), fileOffset),
ConstantExpression(IntConstant(0)),
transformer.runtimeBranchOnLayout(size),
]))
..fileOffset = fileOffset);
}
class StructNativeTypeCfe extends CompoundNativeTypeCfe {
@ -520,11 +633,51 @@ class UnionNativeTypeCfe extends CompoundNativeTypeCfe {
}
}
class ArrayNativeTypeCfe implements NativeTypeCfe {
/// A compound type only being referenced (instead of being fully resolved like
/// in [CompoundNativeTypeCfe]).
///
/// This type can't report the underlying size, alignment or inner fields of
/// the struct or union.
///
/// Since the definitions transformer generates static size fields on compounds,
/// other transformers not needing access to individual fields can use this type
/// to generate loads and stores to compounds when only having their class.
class ReferencedCompoundSubtypeCfe extends NativeTypeCfe
with _CompoundLoadAndStoreMixin {
@override
final Class clazz;
ReferencedCompoundSubtypeCfe(this.clazz) : super._();
Never _informationUnavailable() {
throw UnsupportedError('Reference to struct');
}
@override
bool get knowsLayout => false;
@override
Map<Abi, int?> get alignment => _informationUnavailable();
@override
Constant generateConstant(FfiTransformer transformer) =>
_informationUnavailable();
@override
int? getAlignmentFor(Abi abi) => _informationUnavailable();
@override
int? getSizeFor(Abi abi) => _informationUnavailable();
@override
Map<Abi, int?> get size => _informationUnavailable();
}
class ArrayNativeTypeCfe extends NativeTypeCfe {
final NativeTypeCfe elementType;
final int length;
ArrayNativeTypeCfe(this.elementType, this.length);
ArrayNativeTypeCfe(this.elementType, this.length) : super._();
factory ArrayNativeTypeCfe.multi(
NativeTypeCfe elementType, List<int> dimensions) {
@ -580,29 +733,31 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
IntConstant(dimensionsFlattened)
});
/// Sample output for `Array<Int8> get x =>`:
/// Sample output for `Array<Int8>`:
///
/// ```
/// Array<Int8>._(
/// typedDataBaseOffset(_typedDataBase, offset, size, typeArgument)
/// typedDataBaseOffset(#typedDataBase, #offsetInBytes, size, typeArgument)
/// );
/// ```
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer) {
Expression generateLoad({
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
InterfaceType typeArgument =
(dartType as InterfaceType).typeArguments.single as InterfaceType;
return ReturnStatement(ConstructorInvocation(
return ConstructorInvocation(
transformer.arrayConstructor,
Arguments([
transformer.typedDataBaseOffset(
transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer.runtimeBranchOnLayout(offsets),
typedDataBase,
offsetInBytes,
transformer.runtimeBranchOnLayout(size),
typeArgument,
fileOffset),
@ -611,42 +766,43 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
], types: [
typeArgument
]))
..fileOffset = fileOffset);
..fileOffset = fileOffset;
}
/// Sample output for `set x(Array #v) =>`:
///
/// ```
/// _memCopy(_typedDataBase, offset, #v._typedDataBase, 0, size);
/// _memCopy(#typedDataBase, #offsetInBytes, #v._typedDataBase, 0, size);
/// ```
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
transformer.memCopy,
Arguments([
transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
transformer.runtimeBranchOnLayout(offsets),
transformer.getArrayTypedDataBaseField(
VariableGet(argument), fileOffset),
ConstantExpression(IntConstant(0)),
transformer.runtimeBranchOnLayout(size),
]))
..fileOffset = fileOffset);
Expression generateStore(
Expression value, {
required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false,
}) {
return StaticInvocation(
transformer.memCopy,
Arguments([
typedDataBase,
offsetInBytes,
transformer.getArrayTypedDataBaseField(value, fileOffset),
ConstantExpression(IntConstant(0)),
transformer.runtimeBranchOnLayout(size),
]))
..fileOffset = fileOffset;
}
}
class AbiSpecificNativeTypeCfe implements NativeTypeCfe {
class AbiSpecificNativeTypeCfe extends NativeTypeCfe {
final Map<Abi, NativeTypeCfe> abiSpecificTypes;
final Class clazz;
AbiSpecificNativeTypeCfe(this.abiSpecificTypes, this.clazz);
AbiSpecificNativeTypeCfe(this.abiSpecificTypes, this.clazz) : super._();
@override
Map<Abi, int?> get size => abiSpecificTypes.map(
@ -667,42 +823,35 @@ class AbiSpecificNativeTypeCfe implements NativeTypeCfe {
TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer,
) {
return ReturnStatement(
transformer.abiSpecificLoadOrStoreExpression(
this,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
fileOffset: fileOffset,
),
Expression generateLoad(
{required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false}) {
return transformer.abiSpecificLoadOrStoreExpression(
this,
typedDataBase: typedDataBase,
offsetInBytes: offsetInBytes,
fileOffset: fileOffset,
);
}
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer,
) {
return ReturnStatement(
transformer.abiSpecificLoadOrStoreExpression(
this,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
value: VariableGet(argument),
fileOffset: fileOffset,
),
Expression generateStore(Expression value,
{required DartType dartType,
required int fileOffset,
required Expression typedDataBase,
required FfiTransformer transformer,
required Expression offsetInBytes,
bool unaligned = false}) {
return transformer.abiSpecificLoadOrStoreExpression(
this,
typedDataBase: typedDataBase,
offsetInBytes: offsetInBytes,
value: value,
fileOffset: fileOffset,
);
}
}

View file

@ -29,18 +29,10 @@ import 'package:kernel/type_algebra.dart'
show FunctionTypeInstantiator, Substitution;
import 'package:kernel/type_environment.dart';
import 'abi.dart' show wordSize;
import 'definitions.dart' as definitions;
import 'native_type_cfe.dart';
import 'native.dart' as native;
import 'common.dart'
show
FfiStaticTypeError,
FfiTransformer,
NativeType,
nativeTypeSizes,
UNKNOWN,
WORD_SIZE;
import 'common.dart' show FfiStaticTypeError, FfiTransformer, NativeType;
import 'finalizable.dart';
/// Checks and replaces calls to dart:ffi compound fields and methods.
@ -268,7 +260,8 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
ensureNativeTypeValid(nativeType, node, allowCompounds: true);
Expression? inlineSizeOf = _inlineSizeOf(nativeType as InterfaceType);
Expression? inlineSizeOf =
this.inlineSizeOf(nativeType as InterfaceType);
if (inlineSizeOf != null) {
// Generates `receiver.offsetBy(inlineSizeOfExpression)`.
return InstanceInvocation(InstanceAccessKind.Instance, pointer,
@ -304,7 +297,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
allowCompounds: true, allowVoid: true);
if (nativeType is InterfaceType) {
Expression? inlineSizeOf = _inlineSizeOf(nativeType);
Expression? inlineSizeOf = this.inlineSizeOf(nativeType);
if (inlineSizeOf != null) {
return inlineSizeOf;
}
@ -418,7 +411,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
// Inline the body to get rid of a generic invocation of sizeOf.
// TODO(http://dartbug.com/39964): Add `alignmentOf<T>()` call.
Expression? sizeInBytes = _inlineSizeOf(nativeType as InterfaceType);
Expression? sizeInBytes = inlineSizeOf(nativeType as InterfaceType);
if (sizeInBytes != null) {
if (node.arguments.positional.length == 2) {
sizeInBytes = multiply(node.arguments.positional[1], sizeInBytes);
@ -452,27 +445,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
.distinct()
.fold(nestedExpression, invokeCompoundConstructor);
Expression? _inlineSizeOf(InterfaceType nativeType) {
final Class nativeClass = nativeType.classNode;
final NativeType? nt = getType(nativeClass);
if (nt == null) {
// User-defined compounds.
final Procedure sizeOfGetter = nativeClass.procedures
.firstWhere((function) => function.name == Name('#sizeOf'));
return StaticGet(sizeOfGetter);
}
final int size = nativeTypeSizes[nt]!;
if (size == WORD_SIZE) {
return runtimeBranchOnLayout(wordSize);
}
if (size != UNKNOWN) {
return ConstantExpression(IntConstant(size),
InterfaceType(intClass, currentLibrary.nonNullable));
}
// Size unknown.
return null;
}
// We need to replace calls to 'DynamicLibrary.lookupFunction' with explicit
// Kernel, because we cannot have a generic call to 'asFunction' in its body.
//
@ -825,8 +797,8 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
Expression _replaceGetRef(StaticInvocation node) {
final dartType = node.arguments.types[0];
final clazz = (dartType as InterfaceType).classNode;
final constructor = clazz.constructors
.firstWhere((c) => c.name == Name("#fromTypedDataBase"));
final referencedStruct = ReferencedCompoundSubtypeCfe(clazz);
Expression pointer = NullCheck(node.arguments.positional[0]);
if (node.arguments.positional.length == 2) {
pointer = InstanceInvocation(
@ -834,45 +806,50 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
pointer,
offsetByMethod.name,
Arguments([
multiply(node.arguments.positional[1], _inlineSizeOf(dartType)!)
multiply(node.arguments.positional[1], inlineSizeOf(dartType)!)
]),
interfaceTarget: offsetByMethod,
functionType:
Substitution.fromPairs(pointerClass.typeParameters, [dartType])
.substituteType(offsetByMethod.getterType) as FunctionType);
}
return ConstructorInvocation(constructor, Arguments([pointer]));
return referencedStruct.generateLoad(
dartType: dartType,
transformer: this,
typedDataBase: pointer,
offsetInBytes: ConstantExpression(IntConstant(0)),
fileOffset: node.fileOffset,
);
}
/// Replaces a `.ref=` or `[]=` on a compound pointer extension with a mem
/// copy call.
Expression _replaceSetRef(StaticInvocation node) {
final target = node.arguments.positional[0]; // Receiver of extension
final referencedStruct = ReferencedCompoundSubtypeCfe(
(node.arguments.types[0] as InterfaceType).classNode);
final Expression source, targetOffset;
final Expression sourceStruct, targetOffset;
if (node.arguments.positional.length == 3) {
// []= call, args are (receiver, index, source)
source = getCompoundTypedDataBaseField(
node.arguments.positional[2], node.fileOffset);
sourceStruct = node.arguments.positional[2];
targetOffset = multiply(node.arguments.positional[1],
_inlineSizeOf(node.arguments.types[0] as InterfaceType)!);
inlineSizeOf(node.arguments.types[0] as InterfaceType)!);
} else {
// .ref= call, args are (receiver, source)
source = getCompoundTypedDataBaseField(
node.arguments.positional[1], node.fileOffset);
sourceStruct = node.arguments.positional[1];
targetOffset = ConstantExpression(IntConstant(0));
}
return StaticInvocation(
memCopy,
Arguments([
target,
targetOffset,
source,
ConstantExpression(IntConstant(0)),
_inlineSizeOf(node.arguments.types[0] as InterfaceType)!,
]),
return referencedStruct.generateStore(
sourceStruct,
dartType: node.arguments.types[0],
offsetInBytes: targetOffset,
typedDataBase: target,
transformer: this,
fileOffset: node.fileOffset,
);
}
@ -884,8 +861,8 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
final typedDataBasePrime = typedDataBaseOffset(
getArrayTypedDataBaseField(NullCheck(node.arguments.positional[0])),
multiply(node.arguments.positional[1], _inlineSizeOf(dartType)!),
_inlineSizeOf(dartType)!,
multiply(node.arguments.positional[1], inlineSizeOf(dartType)!),
inlineSizeOf(dartType)!,
dartType,
node.fileOffset);
@ -905,7 +882,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
/// Array #array = this!;
/// int #index = index!;
/// #array._checkIndex(#index);
/// int #singleElementSize = _inlineSizeOf<innermost(T)>();
/// int #singleElementSize = inlineSizeOf<innermost(T)>();
/// int #elementSize = #array.nestedDimensionsFlattened * #singleElementSize;
/// int #offset = #elementSize * #index;
///
@ -927,7 +904,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
/// Array #array = this!;
/// int #index = index!;
/// #array._checkIndex(#index);
/// int #singleElementSize = _inlineSizeOf<innermost(T)>();
/// int #singleElementSize = inlineSizeOf<innermost(T)>();
/// int #elementSize = #array.nestedDimensionsFlattened * #singleElementSize;
/// int #offset = #elementSize * #index;
///
@ -950,7 +927,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
isSynthesized: true)
..fileOffset = node.fileOffset;
final singleElementSizeVar = VariableDeclaration("#singleElementSize",
initializer: _inlineSizeOf(elementType as InterfaceType),
initializer: inlineSizeOf(elementType as InterfaceType),
type: coreTypes.intNonNullableRawType,
isSynthesized: true)
..fileOffset = node.fileOffset;
@ -1121,13 +1098,16 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
final arg = node.arguments.positional.single;
final nativeType = node.arguments.types.single;
// `x` must be a method annotated with `@Native`, so referencing it makes
// it a tear-off.
if (arg case ConstantExpression(constant: StaticTearOffConstant method)) {
// The method must have the `vm:ffi:native` pragma added by the native
// transformer.
Constant? nativeAnnotation;
for (final annotation in method.target.annotations) {
final potentiallyNativeTarget = switch (arg) {
ConstantExpression(constant: StaticTearOffConstant method) =>
method.target,
StaticGet(:var targetReference) => targetReference.asMember,
_ => null,
};
Constant? nativeAnnotation;
if (potentiallyNativeTarget != null) {
for (final annotation in potentiallyNativeTarget.annotations) {
if (annotation
case ConstantExpression(constant: final InstanceConstant c)) {
if (c.classNode == coreTypes.pragmaClass) {
@ -1141,25 +1121,22 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
}
}
}
}
if (nativeAnnotation == null) {
diagnosticReporter.report(messageFfiAddressOfMustBeNative,
node.arguments.fileOffset, 1, node.location?.file);
return node;
}
ensureNativeTypeValid(nativeType, node);
ensureNativeTypeToDartType(nativeType, arg.type, node);
return StaticInvocation(
nativePrivateAddressOf,
Arguments([ConstantExpression(nativeAnnotation)], types: [nativeType]),
)..fileOffset = arg.fileOffset;
} else {
if (nativeAnnotation == null) {
diagnosticReporter.report(messageFfiAddressOfMustBeNative, arg.fileOffset,
1, node.location?.file);
return node;
}
ensureNativeTypeValid(nativeType, node, allowCompounds: true);
ensureNativeTypeToDartType(
nativeType, arg.getStaticType(staticTypeContext!), node);
return StaticInvocation(
nativePrivateAddressOf,
Arguments([ConstantExpression(nativeAnnotation)], types: [nativeType]),
)..fileOffset = arg.fileOffset;
}
}

View file

@ -0,0 +1,22 @@
@DefaultAsset('someAssetId')
library;
import 'dart:ffi';
@Native<Pointer<Void> Function()>()
external Pointer<Void> malloc();
@Native<Pointer<Void> Function()>(assetId: 'anotherAsset')
external Pointer<Void> mallocInAsset();
@Native()
external final Pointer<Void> ptr;
@Native(assetId: 'anotherAsset')
external final Pointer<Void> ptrInAsset;
void main() {
print(malloc());
print(mallocInAsset());
print(ptr);
}

View file

@ -0,0 +1,40 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:ffi";
@#C6
@#C8
external static method malloc() → ffi::Pointer<ffi::Void>;
@#C12
@#C13
external static method mallocInAsset() → ffi::Pointer<ffi::Void>;
@#C16
static get ptr() → ffi::Pointer<ffi::Void>
return [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_loadPointer<ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<ffi::Pointer<ffi::Void>>(#C15), #C17);
static method main() → void {
core::print([@vm.inferred-type.metadata=dart.ffi::Pointer] self::malloc());
core::print([@vm.inferred-type.metadata=dart.ffi::Pointer] self::mallocInAsset());
core::print([@vm.inferred-type.metadata=dart.ffi::Pointer] self::ptr);
}
constants {
#C1 = "cfe:ffi:native-marker"
#C2 = "malloc"
#C3 = "someAssetId"
#C4 = false
#C5 = ffi::Native<() → ffi::Pointer<ffi::Void>> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:ffi:native"
#C8 = core::pragma {name:#C7, options:#C5}
#C9 = "mallocInAsset"
#C10 = "anotherAsset"
#C11 = ffi::Native<() → ffi::Pointer<ffi::Void>> {symbol:#C9, assetId:#C10, isLeaf:#C4}
#C12 = core::pragma {name:#C1, options:#C11}
#C13 = core::pragma {name:#C7, options:#C11}
#C14 = "ptr"
#C15 = ffi::Native<ffi::Pointer<ffi::Void>> {symbol:#C14, assetId:#C3, isLeaf:#C4}
#C16 = core::pragma {name:#C1, options:#C15}
#C17 = 0
}

View file

@ -0,0 +1,48 @@
@#C2
library #lib;
import self as self;
import "dart:ffi" as ffi;
import "dart:core" as core;
import "dart:ffi";
@#C7
@#C9
external static method malloc() → ffi::Pointer<ffi::Void>;
@#C13
@#C14
external static method mallocInAsset() → ffi::Pointer<ffi::Void>;
@#C17
static get ptr() → ffi::Pointer<ffi::Void>
return ffi::_loadPointer<ffi::Void>(ffi::Native::_addressOf<ffi::Pointer<ffi::Void>>(#C16), #C18);
@#C21
static get ptrInAsset() → ffi::Pointer<ffi::Void>
return ffi::_loadPointer<ffi::Void>(ffi::Native::_addressOf<ffi::Pointer<ffi::Void>>(#C20), #C18);
static method main() → void {
core::print(self::malloc());
core::print(self::mallocInAsset());
core::print(self::ptr);
}
constants {
#C1 = "someAssetId"
#C2 = ffi::DefaultAsset {id:#C1}
#C3 = "cfe:ffi:native-marker"
#C4 = "malloc"
#C5 = false
#C6 = ffi::Native<() → ffi::Pointer<ffi::Void>> {symbol:#C4, assetId:#C1, isLeaf:#C5}
#C7 = core::pragma {name:#C3, options:#C6}
#C8 = "vm:ffi:native"
#C9 = core::pragma {name:#C8, options:#C6}
#C10 = "mallocInAsset"
#C11 = "anotherAsset"
#C12 = ffi::Native<() → ffi::Pointer<ffi::Void>> {symbol:#C10, assetId:#C11, isLeaf:#C5}
#C13 = core::pragma {name:#C3, options:#C12}
#C14 = core::pragma {name:#C8, options:#C12}
#C15 = "ptr"
#C16 = ffi::Native<ffi::Pointer<ffi::Void>> {symbol:#C15, assetId:#C1, isLeaf:#C5}
#C17 = core::pragma {name:#C3, options:#C16}
#C18 = 0
#C19 = "ptrInAsset"
#C20 = ffi::Native<ffi::Pointer<ffi::Void>> {symbol:#C19, assetId:#C11, isLeaf:#C5}
#C21 = core::pragma {name:#C3, options:#C20}
}

View file

@ -0,0 +1,45 @@
import 'dart:ffi';
@Native()
external Pointer<Char> aString;
@Native<Int32>()
external int anInt;
@Native<Int>()
external int anotherInt;
final class Vec2d extends Struct {
@Double()
external double x;
@Double()
external double y;
}
final class MyUnion extends Union {
external Vec2d vector;
external Pointer<Vec2d> indirectVector;
}
@Native()
external final Vec2d vector;
@Native()
external MyUnion union;
void main() {
print('first char of string: ${aString.value}');
print('global int: {$anInt}');
aString = nullptr;
anInt++;
final vec = vector;
print('(${vec.x}, ${vec.y})');
union.indirectVector = Native.addressOf(vector);
print(Native.addressOf<Int>(anotherInt));
print(Native.addressOf<Vec2d>(vector));
print(Native.addressOf<MyUnion>(union));
}

View file

@ -0,0 +1,87 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:ffi";
@#C6
final class Vec2d extends ffi::Struct {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] synthesized core::Object #typedDataBase) → self::Vec2d
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] [@vm.unboxing-info.metadata=()->d] get x() → core::double
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] [@vm.unboxing-info.metadata=()->d] get y() → core::double
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
}
@#C15
final class MyUnion extends ffi::Union {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] synthesized core::Object #typedDataBase) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] set indirectVector([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<self::Vec2d> #externalFieldValue) → void
return ffi::_storePointer<self::Vec2d>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
}
@#C21
static get aString() → ffi::Pointer<ffi::Char>
return [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_loadPointer<ffi::Char>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<ffi::Pointer<ffi::Char>>(#C20), #C7);
static set aString([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::Char> #externalFieldValue) → void
ffi::_storePointer<ffi::Char>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<ffi::Pointer<ffi::Char>>(#C20), #C7, #externalFieldValue);
[@vm.unboxing-info.metadata=()->i]@#C24
static get anInt() → core::int
return [@vm.inferred-type.metadata=int] ffi::_loadInt32([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<ffi::Int32>(#C23), #C7);
[@vm.unboxing-info.metadata=(i)->b]static set anInt([@vm.inferred-arg-type.metadata=int] synthesized core::int #externalFieldValue) → void
ffi::_storeInt32([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<ffi::Int32>(#C23), #C7, #externalFieldValue);
@#C27
static get vector() → self::Vec2d
return new self::Vec2d::#fromTypedDataBase([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<self::Vec2d>(#C26));
@#C30
static get union() → self::MyUnion
return new self::MyUnion::#fromTypedDataBase([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<self::MyUnion>(#C29));
static method main() → void {
core::print("first char of string: ${ffi::_loadAbiSpecificInt<ffi::Char>([@vm.inferred-type.metadata=dart.ffi::Pointer] self::aString, #C7)}");
core::print("global int: {${self::anInt}}");
self::aString = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::nullptr;
self::anInt = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=int] self::anInt.{core::num::+}(1){(core::num) → core::int};
final self::Vec2d vec = [@vm.inferred-type.metadata=#lib::Vec2d] self::vector;
core::print("(${[@vm.direct-call.metadata=#lib::Vec2d.x] vec.{self::Vec2d::x}{core::double}}, ${[@vm.direct-call.metadata=#lib::Vec2d.y] vec.{self::Vec2d::y}{core::double}})");
[@vm.direct-call.metadata=#lib::MyUnion.indirectVector] [@vm.inferred-type.metadata=!? (skip check)] [@vm.inferred-type.metadata=#lib::MyUnion] self::union.{self::MyUnion::indirectVector} = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<self::Vec2d>(#C26);
core::print([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<ffi::Int>(#C32));
core::print([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<self::Vec2d>(#C26));
core::print([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Native::_addressOf<self::MyUnion>(#C29));
}
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = <core::Type>[#C2, #C2]
#C4 = null
#C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = 0
#C8 = <core::int*>[#C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7]
#C9 = 8
#C10 = <core::int*>[#C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9]
#C11 = TypeLiteralConstant(self::Vec2d)
#C12 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
#C13 = <core::Type>[#C11, #C12]
#C14 = ffi::_FfiStructLayout {fieldTypes:#C13, packing:#C4}
#C15 = core::pragma {name:#C1, options:#C14}
#C16 = "cfe:ffi:native-marker"
#C17 = "aString"
#C18 = "#lib"
#C19 = false
#C20 = ffi::Native<ffi::Pointer<ffi::Char>> {symbol:#C17, assetId:#C18, isLeaf:#C19}
#C21 = core::pragma {name:#C16, options:#C20}
#C22 = "anInt"
#C23 = ffi::Native<ffi::Int32> {symbol:#C22, assetId:#C18, isLeaf:#C19}
#C24 = core::pragma {name:#C16, options:#C23}
#C25 = "vector"
#C26 = ffi::Native<self::Vec2d> {symbol:#C25, assetId:#C18, isLeaf:#C19}
#C27 = core::pragma {name:#C16, options:#C26}
#C28 = "union"
#C29 = ffi::Native<self::MyUnion> {symbol:#C28, assetId:#C18, isLeaf:#C19}
#C30 = core::pragma {name:#C16, options:#C29}
#C31 = "anotherInt"
#C32 = ffi::Native<ffi::Int> {symbol:#C31, assetId:#C18, isLeaf:#C19}
}

View file

@ -0,0 +1,131 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:_internal" as _in;
import "dart:ffi";
@#C6
final class Vec2d extends ffi::Struct {
synthetic constructor •() → self::Vec2d
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Vec2d
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
@#C7
get x() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C7
set x(synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C7
get y() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C7
set y(synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C13
static get #sizeOf() → core::int*
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C20
final class MyUnion extends ffi::Union {
synthetic constructor •() → self::MyUnion
: super ffi::Union::•()
;
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
get vector() → self::Vec2d
return new self::Vec2d::#fromTypedDataBase( block {
synthesized core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::int #offset = #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
} =>#typedDataBase is{ForLegacy} ffi::Pointer<ffi::NativeType> ?{core::Object} ffi::_fromAddress<self::Vec2d>(#typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let synthesized typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}{typ::ByteBuffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}){([core::int, core::int?]) → typ::Uint8List});
set vector(synthesized self::Vec2d #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
get indirectVector() → ffi::Pointer<self::Vec2d>
return ffi::_loadPointer<self::Vec2d>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
set indirectVector(synthesized ffi::Pointer<self::Vec2d> #externalFieldValue) → void
return ffi::_storePointer<self::Vec2d>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C13
static get #sizeOf() → core::int*
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C26
static get aString() → ffi::Pointer<ffi::Char>
return ffi::_loadPointer<ffi::Char>(ffi::Native::_addressOf<ffi::Pointer<ffi::Char>>(#C25), #C8);
static set aString(synthesized ffi::Pointer<ffi::Char> #externalFieldValue) → void
ffi::_storePointer<ffi::Char>(ffi::Native::_addressOf<ffi::Pointer<ffi::Char>>(#C25), #C8, #externalFieldValue);
@#C29
static get anInt() → core::int
return ffi::_loadInt32(ffi::Native::_addressOf<ffi::Int32>(#C28), #C8);
static set anInt(synthesized core::int #externalFieldValue) → void
ffi::_storeInt32(ffi::Native::_addressOf<ffi::Int32>(#C28), #C8, #externalFieldValue);
@#C32
static get anotherInt() → core::int
return ffi::_loadAbiSpecificInt<ffi::Int>(ffi::Native::_addressOf<ffi::Int>(#C31), #C8);
static set anotherInt(synthesized core::int #externalFieldValue) → void
ffi::_storeAbiSpecificInt<ffi::Int>(ffi::Native::_addressOf<ffi::Int>(#C31), #C8, #externalFieldValue);
@#C35
static get vector() → self::Vec2d
return new self::Vec2d::#fromTypedDataBase(ffi::Native::_addressOf<self::Vec2d>(#C34));
@#C38
static get union() → self::MyUnion
return new self::MyUnion::#fromTypedDataBase(ffi::Native::_addressOf<self::MyUnion>(#C37));
static set union(synthesized self::MyUnion #externalFieldValue) → void
ffi::_memCopy(ffi::Native::_addressOf<self::MyUnion>(#C37), #C8, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, self::MyUnion::#sizeOf);
static method main() → void {
core::print("first char of string: ${ffi::_loadAbiSpecificInt<ffi::Char>(self::aString, #C8)}");
core::print("global int: {${self::anInt}}");
self::aString = ffi::nullptr;
self::anInt = self::anInt.{core::num::+}(1){(core::num) → core::int};
final self::Vec2d vec = self::vector;
core::print("(${vec.{self::Vec2d::x}{core::double}}, ${vec.{self::Vec2d::y}{core::double}})");
self::union.{self::MyUnion::indirectVector} = ffi::Native::_addressOf<self::Vec2d>(#C34);
core::print(ffi::Native::_addressOf<ffi::Int>(#C31));
core::print(ffi::Native::_addressOf<self::Vec2d>(#C34));
core::print(ffi::Native::_addressOf<self::MyUnion>(#C37));
}
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = <core::Type>[#C2, #C2]
#C4 = null
#C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = ffi::Double {}
#C8 = 0
#C9 = <core::int*>[#C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8]
#C10 = 8
#C11 = <core::int*>[#C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10]
#C12 = "vm:prefer-inline"
#C13 = core::pragma {name:#C12, options:#C4}
#C14 = 16
#C15 = <core::int*>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
#C16 = TypeLiteralConstant(self::Vec2d)
#C17 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
#C18 = <core::Type>[#C16, #C17]
#C19 = ffi::_FfiStructLayout {fieldTypes:#C18, packing:#C4}
#C20 = core::pragma {name:#C1, options:#C19}
#C21 = "cfe:ffi:native-marker"
#C22 = "aString"
#C23 = "#lib"
#C24 = false
#C25 = ffi::Native<ffi::Pointer<ffi::Char>> {symbol:#C22, assetId:#C23, isLeaf:#C24}
#C26 = core::pragma {name:#C21, options:#C25}
#C27 = "anInt"
#C28 = ffi::Native<ffi::Int32> {symbol:#C27, assetId:#C23, isLeaf:#C24}
#C29 = core::pragma {name:#C21, options:#C28}
#C30 = "anotherInt"
#C31 = ffi::Native<ffi::Int> {symbol:#C30, assetId:#C23, isLeaf:#C24}
#C32 = core::pragma {name:#C21, options:#C31}
#C33 = "vector"
#C34 = ffi::Native<self::Vec2d> {symbol:#C33, assetId:#C23, isLeaf:#C24}
#C35 = core::pragma {name:#C21, options:#C34}
#C36 = "union"
#C37 = ffi::Native<self::MyUnion> {symbol:#C36, assetId:#C23, isLeaf:#C24}
#C38 = core::pragma {name:#C21, options:#C37}
}

View file

@ -25,9 +25,11 @@
#if defined(_WIN32)
#define DART_EXPORT extern "C" __declspec(dllexport)
#define DART_EXPORT_FIELD DART_EXPORT
#else
#define DART_EXPORT \
extern "C" __attribute__((visibility("default"))) __attribute((used))
#define DART_EXPORT_FIELD __attribute__((visibility("default")))
#endif
namespace dart {
@ -40,24 +42,40 @@ namespace dart {
#define CHECK_EQ(X, Y) CHECK((X) == (Y))
////////////////////////////////////////////////////////////////////////////////
// Tests for Dart -> native fields.
struct Coord {
double x;
double y;
Coord* next;
};
extern "C" {
DART_EXPORT_FIELD int32_t globalInt;
DART_EXPORT_FIELD Coord globalStruct;
DART_EXPORT_FIELD const char* globalString = "Hello Dart!";
}
////////////////////////////////////////////////////////////////////////////////
// Tests for Dart -> native calls.
//
// Note: If this interface is changed please also update
// sdk/runtime/tools/dartfuzz/ffiapi.dart
int32_t globalVar;
// sdk/runtime/tools/dartfuzz/dartfuzz_ffi_api.dart
DART_EXPORT void InduceACrash() {
*reinterpret_cast<int*>(InduceACrash) = 123;
}
DART_EXPORT void SetGlobalVar(int32_t v) {
globalVar = v;
globalInt = v;
}
DART_EXPORT int32_t GetGlobalVar() {
return globalVar;
return globalInt;
}
DART_EXPORT Coord GetGlobalStruct() {
return globalStruct;
}
// Sums two ints and adds 42.
@ -542,12 +560,6 @@ DART_EXPORT int64_t* Assign1337Index1(int64_t* a) {
return retval;
}
struct Coord {
double x;
double y;
Coord* next;
};
// Transposes Coordinate by (10, 10) and returns next Coordinate.
// Used for testing struct pointer parameter, struct pointer return value,
// struct field access, and struct pointer field dereference.

View file

@ -5062,10 +5062,16 @@ Fragment FlowGraphBuilder::FfiNativeLookupAddress(
String::ZoneHandle(Z, String::RawCast(native.GetField(asset_id_field)));
const auto& type_args = TypeArguments::Handle(Z, native.GetTypeArguments());
ASSERT(type_args.Length() == 1);
const auto& native_type =
FunctionType::Cast(AbstractType::ZoneHandle(Z, type_args.TypeAt(0)));
const intptr_t arg_n =
native_type.NumParameters() - native_type.num_implicit_parameters();
const auto& native_type = AbstractType::ZoneHandle(Z, type_args.TypeAt(0));
intptr_t arg_n;
if (native_type.IsFunctionType()) {
const auto& native_function_type = FunctionType::Cast(native_type);
arg_n = native_function_type.NumParameters() -
native_function_type.num_implicit_parameters();
} else {
// We're looking up the address of a native field.
arg_n = 0;
}
const auto& ffi_resolver =
Function::ZoneHandle(Z, IG->object_store()->ffi_resolver_function());

View file

@ -1495,42 +1495,63 @@ abstract final class NativeApi {
external static Pointer<Void> get initializeApiDLData;
}
/// Annotation specifying how to bind an external function to native code.
/// Annotation binding an external declaration to its native implementation.
///
/// The annotation applies only to `external` function declarations.
/// Can only be applied to `external` declarations of static and top-level
/// functions and variables.
///
/// A [Native]-annotated `external` function is implemented by native code.
/// The implementation is found in the native library denoted by [assetId].
/// Similarly, a [Native]-annotated `external` variable is implemented by
/// reading from or writing to native memory.
///
/// The compiler and/or runtime provides a binding from [assetId] to native
/// library, which depends on the target platform.
/// The compiler/runtime can then resolve/lookup symbols (identifiers)
/// against the native library, to find a native function,
/// and bind an `external` Dart function declaration to that native function.
/// against the native library, to find a native function or a native global
/// variable, and bind an `external` Dart function or variable declaration to
/// that native declaration.
/// By default, the runtime expects a native symbol with the same name as the
/// annotated function or variable in Dart. This can be overridden with the
/// [symbol] parameter on the annotation.
///
/// Use this annotation on `external` functions to specify that they
/// are resolved against an asset, and to, optionally, provide overrides
/// of the default symbol and asset IDs.
/// 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].
///
/// 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].
/// 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.
///
/// Example:
///
/// ```dart template:top
/// @Native<Int64 Function(Int64, Int64)>()
/// external int sum(int a, int b);
///
/// @Native<Int64>()
/// external int aGlobalInt;
///
/// @Native()
/// external final Pointer<Char> aGlobalString;
/// ```
///
/// Calling such function will try to resolve the [symbol] in (in that order)
/// Calling a `@Native` function, as well as reading or writing to a `@Native`
/// variable, will try to resolve the [symbol] in (in the order):
/// 1. the provided or default [assetId],
/// 2. the native resolver set with `Dart_SetFfiNativeResolver` in
/// `dart_api.h`, and
/// 3. the current process.
///
/// At least one of those three *must* provide a binding for the symbol,
/// otherwise the method call fails.
/// otherwise the method call or the variable access fails.
///
/// NOTE: This is an experimental feature and may change in the future.
@Since('2.19')
@ -1614,6 +1635,8 @@ final class Native<T> {
/// in a group is trying to perform a GC and a second isolate is blocked in a
/// leaf call, then the first isolate will have to pause and wait until this
/// leaf call returns.
///
/// This value has no meaning for native fields.
final bool isLeaf;
const Native({
@ -1622,12 +1645,26 @@ final class Native<T> {
this.symbol,
});
/// The native address of [native].
/// The native address of the implementation of [native].
///
/// [native] must be a reference to a method annotated with `@Native` and [T]
/// must be a [NativeFunction] compatible to the signature of [native].
/// When calling this function, the argument for [native] must be an
/// expression denoting a variable or function declaration which is annotated
/// with [Native].
/// For a variable declaration, the type [T] must be the same native type
/// as the type argument to that `@Native` annotation.
/// For a function declaration, the type [T] must be `NativeFunction<F>`
/// where `F` was the type argument to that `@Native` annotation.
///
/// For example, for a native C library exposing a function:
///
/// ```C
/// #include <stdint.h>
/// int64_t sum(int64_t a, int64_t b) { return a + b; }
/// ```
///
/// The following code binds `sum` to a Dart function declaration, and
/// extracts the address of the native `sum` implementation:
///
/// Example:
/// ```dart
/// import 'dart:ffi';
///
@ -1637,7 +1674,30 @@ final class Native<T> {
/// external int sum(int a, int b);
///
/// void main() {
/// final address = Native.addressOf<NativeFunction<NativeAdd>>(sum);
/// Pointer<NativeFunction<NativeAdd>> addressSum = Native.addressOf(sum);
/// }
/// ```
///
/// Similarly, for a native C library exposing a global variable:
///
/// ```C
/// const char* myString;
/// ```
///
/// The following code binds `myString` to a top-level variable in Dart, and
/// extracts the address of the underlying native field:
///
/// ```dart
/// import 'dart:ffi';
///
/// @Native()
/// external Pointer<Char> myString;
///
/// void main() {
/// // This pointer points to the memory location where the loader has
/// // placed the `myString` global itself. To get the string value, read
/// // the myString field directly.
/// Pointer<Pointer<Char>> addressMyString = Native.addressOf(myString);
/// }
/// ```
@Since('3.3')

View file

@ -16,6 +16,7 @@
// SharedObjects=ffi_test_functions
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
@ -65,6 +66,7 @@ Future<void> selfInvokes() async {
Future<void> runTests() async {
testFfiTestfunctionsDll();
testFfiTestFieldsDll();
testNonExistingFunction();
}
@ -80,3 +82,68 @@ void testFfiTestfunctionsDll() {
.asFunction<int Function(int, int)>();
Expect.equals(2 + 3 + 42, viaAddressOf(2, 3));
}
@Native<Int32>()
external int globalInt;
@Native<Int32>(symbol: 'globalInt')
external int get globalIntProcedure;
@Native<Int32>(symbol: 'globalInt')
external set globalIntProcedure(int value);
@Native<Void Function(Int32)>()
external void SetGlobalVar(int value);
@Native<Int32 Function()>()
external int GetGlobalVar();
@Native()
external final Pointer<Char> globalString;
final class Coord extends Struct {
@Double()
external double x;
@Double()
external double y;
external Pointer<Coord> next;
}
@Native()
external Coord globalStruct;
@Native<Coord Function()>()
external Coord GetGlobalStruct();
void testFfiTestFieldsDll() {
SetGlobalVar(42);
Expect.equals(globalInt, 42);
Expect.equals(globalIntProcedure, 42);
globalInt = 13;
Expect.equals(GetGlobalVar(), 13);
globalIntProcedure = 26;
Expect.equals(GetGlobalVar(), 26);
var readString = utf8.decode(globalString.cast<Uint8>().asTypedList(11));
Expect.equals(readString, 'Hello Dart!');
globalStruct
..x = 1
..y = 2
..next = nullptr;
final viaFunction = GetGlobalStruct();
Expect.equals(viaFunction.x, 1.0);
Expect.equals(viaFunction.y, 2.0);
Expect.equals(viaFunction.next, nullptr);
viaFunction.x *= 2;
viaFunction.y *= 2;
viaFunction.next = Pointer.fromAddress(0xdeadbeef);
globalStruct = viaFunction;
Expect.equals(globalStruct.x, 2.0);
Expect.equals(globalStruct.y, 4.0);
Expect.equals(globalStruct.next.address, 0xdeadbeef);
}

View file

@ -13,6 +13,7 @@
library asset_test;
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
@ -63,6 +64,7 @@ Future<void> selfInvokes() async {
Future<void> runTests() async {
testFfiTestfunctionsDll();
testFfiTestFieldsDll();
}
@Native<Int32 Function(Int32, Int32)>()
@ -77,3 +79,68 @@ void testFfiTestfunctionsDll() {
final function = ptr.asFunction<int Function(int, int)>();
Expect.equals(2 + 3 + 42, function(2, 3));
}
@Native<Int32>()
external int globalInt;
@Native<Int32>(symbol: 'globalInt')
external int get globalIntProcedure;
@Native<Int32>(symbol: 'globalInt')
external set globalIntProcedure(int value);
@Native<Void Function(Int32)>()
external void SetGlobalVar(int value);
@Native<Int32 Function()>()
external int GetGlobalVar();
@Native()
external final Pointer<Char> globalString;
final class Coord extends Struct {
@Double()
external double x;
@Double()
external double y;
external Pointer<Coord> next;
}
@Native()
external Coord globalStruct;
@Native<Coord Function()>()
external Coord GetGlobalStruct();
void testFfiTestFieldsDll() {
SetGlobalVar(42);
Expect.equals(globalInt, 42);
Expect.equals(globalIntProcedure, 42);
globalInt = 13;
Expect.equals(GetGlobalVar(), 13);
globalIntProcedure = 26;
Expect.equals(GetGlobalVar(), 26);
var readString = utf8.decode(globalString.cast<Uint8>().asTypedList(11));
Expect.equals(readString, 'Hello Dart!');
globalStruct
..x = 1
..y = 2
..next = nullptr;
final viaFunction = GetGlobalStruct();
Expect.equals(viaFunction.x, 1.0);
Expect.equals(viaFunction.y, 2.0);
Expect.equals(viaFunction.next, nullptr);
viaFunction.x *= 2;
viaFunction.y *= 2;
viaFunction.next = Pointer.fromAddress(0xdeadbeef);
globalStruct = viaFunction;
Expect.equals(globalStruct.x, 2.0);
Expect.equals(globalStruct.y, 4.0);
Expect.equals(globalStruct.next.address, 0xdeadbeef);
}

View file

@ -17,6 +17,7 @@
// SharedObjects=ffi_test_functions
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
@ -120,6 +121,7 @@ Future<void> invokeSelf({
Future<void> runTests() async {
testFfiTestfunctionsDll();
testNonExistingFunction();
testFfiTestFieldsDll();
}
@Native<Int32 Function(Int32, Int32)>()
@ -135,3 +137,68 @@ void testFfiTestfunctionsDll() {
Expect.equals(2 + 3 + 42, viaAddressOf(2, 3));
}
@Native<Int32>()
external int globalInt;
@Native<Int32>(symbol: 'globalInt')
external int get globalIntProcedure;
@Native<Int32>(symbol: 'globalInt')
external set globalIntProcedure(int value);
@Native<Void Function(Int32)>()
external void SetGlobalVar(int value);
@Native<Int32 Function()>()
external int GetGlobalVar();
@Native()
external final Pointer<Char> globalString;
final class Coord extends Struct {
@Double()
external double x;
@Double()
external double y;
external Pointer<Coord> next;
}
@Native()
external Coord globalStruct;
@Native<Coord Function()>()
external Coord GetGlobalStruct();
void testFfiTestFieldsDll() {
SetGlobalVar(42);
Expect.equals(globalInt, 42);
Expect.equals(globalIntProcedure, 42);
globalInt = 13;
Expect.equals(GetGlobalVar(), 13);
globalIntProcedure = 26;
Expect.equals(GetGlobalVar(), 26);
var readString = utf8.decode(globalString.cast<Uint8>().asTypedList(11));
Expect.equals(readString, 'Hello Dart!');
globalStruct
..x = 1
..y = 2
..next = nullptr;
final viaFunction = GetGlobalStruct();
Expect.equals(viaFunction.x, 1.0);
Expect.equals(viaFunction.y, 2.0);
Expect.equals(viaFunction.next, nullptr);
viaFunction.x *= 2;
viaFunction.y *= 2;
viaFunction.next = Pointer.fromAddress(0xdeadbeef);
globalStruct = viaFunction;
Expect.equals(globalStruct.x, 2.0);
Expect.equals(globalStruct.y, 4.0);
Expect.equals(globalStruct.next.address, 0xdeadbeef);
}

View file

@ -41,14 +41,66 @@ external void notNative();
// [analyzer] COMPILE_TIME_ERROR.FFI_NATIVE_INVALID_MULTIPLE_ANNOTATIONS
external int foo(int v);
// ^
// [cfe] Native functions must not have more than @Native annotation.
// [cfe] Native functions and fields must not have more than @Native annotation.
@Native()
external final MyStruct myStruct0;
@Native<MyStruct>()
external MyStruct myStruct1;
@Native<Pointer<MyStruct>>()
external MyStruct myStructInvalid;
// ^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
// [cfe] Expected type 'MyStruct' to be 'Pointer<MyStruct>', which is the Dart type corresponding to 'Pointer<MyStruct>'.
@Native()
external Pointer<MyStruct> myStructPtr0;
@Native<Pointer<MyStruct>>()
external final Pointer<MyStruct> myStructPtr1;
@Native<MyStruct>()
external Pointer<MyStruct> myStructPtrInvalid;
// ^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
// [cfe] Expected type 'Pointer<MyStruct>' to be 'MyStruct', which is the Dart type corresponding to 'MyStruct'.
@Native()
external int invalidNoInferrence;
// ^^^^^^^^^^^^^^^^^^^
// [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.
@Native<Handle>()
external Object invalidUnsupportedHandle;
// ^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NATIVE_FIELD_INVALID_TYPE
// [cfe] Unsupported type for native fields. Native fields only support pointers, compounds and numeric types.
@Native()
external Array<IntPtr> invalidUnsupportedArray;
// ^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NATIVE_FIELD_INVALID_TYPE
// [cfe] Unsupported type for native fields. Native fields only support pointers, compounds and numeric types.
class MyClass {
@Native<Double>()
external double invalidInstanceField;
// ^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NATIVE_FIELD_NOT_STATIC
// [cfe] Native fields must be static.
@Native<Double>()
external static double validField;
}
void addressOf() {
Native.addressOf<NativeFunction<Void Function()>>(notNative);
// ^
// [cfe] Argument to 'Native.addressOf' must be annotated with @Native.
// ^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_MUST_BE_NATIVE
// [cfe] Argument to 'Native.addressOf' must be annotated with @Native.
var boolean = 1 == 2;
Native.addressOf<NativeFunction<Void Function()>>(boolean ? _valid : _valid2);
@ -79,4 +131,15 @@ void addressOf() {
// [cfe] Expected type 'void Function()' to be 'void Function(int)', which is the Dart type corresponding to 'NativeFunction<Void Function(Int)>'.
Native.addressOf<NativeFunction<ComplexNativeFunction>>(validNative);
Native.addressOf<MyStruct>(myStruct0);
Native.addressOf<MyStruct>(myStruct1);
Native.addressOf<Pointer<MyStruct>>(myStructPtr0);
Native.addressOf<Pointer<MyStruct>>(myStructPtr1);
Native.addressOf<Int>(myStruct0);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
// ^
// [cfe] Expected type 'MyStruct' to be 'int', which is the Dart type corresponding to 'Int'.
}