Skip to content

Commit 4b66657

Browse files
dcharkesCommit Queue
authored and
Commit Queue
committed
[vm/ffi] address of operator for FFI leaf calls
During FFI leaf calls, the Dart GC will not run. This means that we can pass pointers into `TypedData` to FFI calls that take `Pointer` arguments. After this CL, we have three types of arguments that can flow into `Pointer` argument in an FFI call: * `Pointer`. * `TypedData`: Any typed data including views. * `_Compound`: A TypedData/Pointer and an offset in bytes. The is only possible for `@Native external` functions, `asFunction` does not support passing in `TypedData`. (See related GitHub issues for discussion. TLDR: FFIgen should generate bindings without config.) `.address` expressions on `TypedData` and `Array` elements do _not_ introduce bounds checks, even though `TypedData` and `Array` have bounds information. E.g. `ffiNative(Uint8List(10)[20].address)` does not throw. Implementation details: The CFE analyzes call-sites to `@Native external` functions. If the arguments are `.address` expressions, it transforms the call site to pass the compound or `TypedData`. If an additional offset needs to be applied, the CFE constructs a new `_Compound` with the correct offset in bytes. The CFE then also creates a new `@Native external` function which have `TypedData`s and `_Compound`s parameters. To avoid name clashes, these functions are postfixed with `#` and `P`, `T`, or `C` for each Pointer parameter. TEST=pkg/vm/testcases/transformations/ffi/address_of_* In the VM, `TypedData` arguments are passed as tagged values, and the address is loaded inside the `FfiCallInstr`. `_Compound` arguments turn into two IL definitions, one for the `TypedDataBase` (tagged), and one for the offset in bytes (unboxed). The address is then loaded inside the `FfiCallInstr` and the offset in bytes is applied. Adding the offset in bytes required an extra temp register for ia32. Also, it uncovered that the temp register in arm32 was conflicting with the argument registers. However, TMP should suffice instead. TEST=tests/ffi/address_of_array_generated_test.dart TEST=tests/ffi/address_of_struct_generated_test.dart TEST=tests/ffi/address_of_typeddata_generated_test.dart Closes: #44589 Closes: #54771 CoreLibraryReviewExempt: VM only, unsupported in dart2wasm Change-Id: I01fb428cfd6f9096a34689c2819c124a8003cb6b Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64c-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/360882 Reviewed-by: Jens Johansen <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Tess Strickland <[email protected]> Reviewed-by: Lasse Nielsen <[email protected]>
1 parent 8c1475c commit 4b66657

File tree

56 files changed

+7487
-412
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+7487
-412
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5900,6 +5900,28 @@ const MessageCode messageFfiAddressOfMustBeNative = const MessageCode(
59005900
r"""Argument to 'Native.addressOf' must be annotated with @Native.""",
59015901
);
59025902

5903+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
5904+
const Code<Null> codeFfiAddressPosition = messageFfiAddressPosition;
5905+
5906+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
5907+
const MessageCode messageFfiAddressPosition = const MessageCode(
5908+
"FfiAddressPosition",
5909+
problemMessage:
5910+
r"""The '.address' expression can only be used as argument to a leaf native external call.""",
5911+
);
5912+
5913+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
5914+
const Code<Null> codeFfiAddressReceiver = messageFfiAddressReceiver;
5915+
5916+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
5917+
const MessageCode messageFfiAddressReceiver = const MessageCode(
5918+
"FfiAddressReceiver",
5919+
problemMessage:
5920+
r"""The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field.""",
5921+
correctionMessage:
5922+
r"""Change the receiver of '.address' to one of the allowed kinds.""",
5923+
);
5924+
59035925
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
59045926
const Template<Message Function(String string, String name)>
59055927
templateFfiCompoundImplementsFinalizable =

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,12 @@ FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING:
17431743
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED:
17441744
status: noFix
17451745
since: ~2.16
1746+
FfiCode.ADDRESS_POSITION:
1747+
status: needsEvaluation
1748+
since: ~3.5
1749+
FfiCode.ADDRESS_RECEIVER:
1750+
status: needsEvaluation
1751+
since: ~3.5
17461752
FfiCode.ANNOTATION_ON_POINTER_FIELD:
17471753
status: needsFix
17481754
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT:

pkg/analyzer/lib/src/dart/error/ffi_code.g.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ class FfiCode extends AnalyzerErrorCode {
6565
hasPublishedDocs: true,
6666
);
6767

68+
/// No parameters.
69+
static const FfiCode ADDRESS_POSITION = FfiCode(
70+
'ADDRESS_POSITION',
71+
"The '.address' expression can only be used as argument to a leaf native "
72+
"external call.",
73+
);
74+
75+
/// No parameters.
76+
static const FfiCode ADDRESS_RECEIVER = FfiCode(
77+
'ADDRESS_RECEIVER',
78+
"The receiver of '.address' must be a concrete 'TypedData', a concrete "
79+
"'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a "
80+
"Union field.",
81+
correctionMessage:
82+
"Change the receiver of '.address' to one of the allowed kinds.",
83+
);
84+
6885
/// No parameters.
6986
static const FfiCode ANNOTATION_ON_POINTER_FIELD = FfiCode(
7087
'ANNOTATION_ON_POINTER_FIELD',

pkg/analyzer/lib/src/error/error_code_values.g.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,8 @@ const List<ErrorCode> errorCodeValues = [
590590
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_EXTRA,
591591
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING,
592592
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED,
593+
FfiCode.ADDRESS_POSITION,
594+
FfiCode.ADDRESS_RECEIVER,
593595
FfiCode.ANNOTATION_ON_POINTER_FIELD,
594596
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT,
595597
FfiCode.ARGUMENT_MUST_BE_NATIVE,

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

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,37 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
3535
static const _nativeCallable = 'NativeCallable';
3636
static const _opaqueClassName = 'Opaque';
3737

38+
static const _addressOfExtensionNames = {
39+
..._addressOfCompoundExtensionNames,
40+
..._addressOfPrimitiveExtensionNames,
41+
..._addressOfTypedDataExtensionNames,
42+
};
43+
44+
static const _addressOfCompoundExtensionNames = {
45+
'ArrayAddress',
46+
'StructAddress',
47+
'UnionAddress',
48+
};
49+
50+
static const _addressOfPrimitiveExtensionNames = {
51+
'BoolAddress',
52+
'DoubleAddress',
53+
'IntAddress',
54+
};
55+
56+
static const _addressOfTypedDataExtensionNames = {
57+
'Float32ListAddress',
58+
'Float64ListAddress',
59+
'Int16ListAddress',
60+
'Int32ListAddress',
61+
'Int64ListAddress',
62+
'Int8ListAddress',
63+
'Uint16ListAddress',
64+
'Uint32ListAddress',
65+
'Uint64ListAddress',
66+
'Uint8ListAddress',
67+
};
68+
3869
static const Set<String> _primitiveIntegerNativeTypesFixedSize = {
3970
'Int8',
4071
'Int16',
@@ -346,6 +377,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
346377
if (element.name == 'ref') {
347378
_validateRefPrefixedIdentifier(node);
348379
}
380+
} else if (enclosingElement.isAddressOfExtension) {
381+
if (element.name == 'address') {
382+
_validateAddressPrefixedIdentifier(node);
383+
}
349384
}
350385
}
351386
super.visitPrefixedIdentifier(node);
@@ -361,6 +396,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
361396
if (element.name == 'ref') {
362397
_validateRefPropertyAccess(node);
363398
}
399+
} else if (enclosingElement.isAddressOfExtension) {
400+
if (element.name == 'address') {
401+
_validateAddressPropertyAccess(node);
402+
}
364403
}
365404
}
366405
super.visitPropertyAccess(node);
@@ -986,6 +1025,84 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
9861025
}
9871026
}
9881027

1028+
/// Check that .address is only used in argument lists passed to native leaf
1029+
/// calls.
1030+
void _validateAddressPosition(Expression node, AstNode errorNode) {
1031+
final parent = node.parent;
1032+
final grandParent = parent?.parent;
1033+
if (parent is! ArgumentList ||
1034+
grandParent is! MethodInvocation ||
1035+
!grandParent.isNativeLeafInvocation) {
1036+
_errorReporter.atNode(
1037+
errorNode,
1038+
FfiCode.ADDRESS_POSITION,
1039+
);
1040+
}
1041+
}
1042+
1043+
void _validateAddressPrefixedIdentifier(PrefixedIdentifier node) {
1044+
final errorNode = node.identifier;
1045+
_validateAddressPosition(node, errorNode);
1046+
final extensionName = node.staticElement?.enclosingElement?.name;
1047+
final receiver = node.prefix;
1048+
_validateAddressReceiver(node, extensionName, receiver, errorNode);
1049+
}
1050+
1051+
void _validateAddressPropertyAccess(PropertyAccess node) {
1052+
final errorNode = node.propertyName;
1053+
_validateAddressPosition(node, errorNode);
1054+
final extensionName =
1055+
node.propertyName.staticElement?.enclosingElement?.name;
1056+
final receiver = node.target;
1057+
_validateAddressReceiver(node, extensionName, receiver, errorNode);
1058+
}
1059+
1060+
void _validateAddressReceiver(
1061+
Expression node,
1062+
String? extensionName,
1063+
Expression? receiver,
1064+
AstNode errorNode,
1065+
) {
1066+
if (_addressOfCompoundExtensionNames.contains(extensionName) ||
1067+
_addressOfTypedDataExtensionNames.contains(extensionName)) {
1068+
return; // Only primitives need their reciever checked.
1069+
}
1070+
if (receiver == null) {
1071+
return;
1072+
}
1073+
switch (receiver) {
1074+
case IndexExpression _:
1075+
// Array or TypedData element.
1076+
final arrayOrTypedData = receiver.target;
1077+
final type = arrayOrTypedData?.staticType;
1078+
if (type?.isArray ?? false) {
1079+
return;
1080+
}
1081+
if (type?.isTypedData ?? false) {
1082+
return;
1083+
}
1084+
case PrefixedIdentifier _:
1085+
// Struct or Union field.
1086+
final compound = receiver.prefix;
1087+
final type = compound.staticType;
1088+
if (type?.isCompoundSubtype ?? false) {
1089+
return;
1090+
}
1091+
case PropertyAccess _:
1092+
// Struct or Union field.
1093+
final compound = receiver.target;
1094+
final type = compound?.staticType;
1095+
if (type?.isCompoundSubtype ?? false) {
1096+
return;
1097+
}
1098+
default:
1099+
}
1100+
_errorReporter.atNode(
1101+
errorNode,
1102+
FfiCode.ADDRESS_RECEIVER,
1103+
);
1104+
}
1105+
9891106
void _validateAllocate(FunctionExpressionInvocation node) {
9901107
var typeArgumentTypes = node.typeArgumentTypes;
9911108
if (typeArgumentTypes == null || typeArgumentTypes.length != 1) {
@@ -1959,6 +2076,22 @@ extension on ElementAnnotation {
19592076
// forwarding factory instead of the forwarded constructor.
19602077
}
19612078

2079+
/// @Native(isLeaf: true)
2080+
bool get isNativeLeaf {
2081+
final annotationValue = computeConstantValue();
2082+
final annotationType = annotationValue?.type; // Native<T>
2083+
if (annotationValue == null || annotationType is! InterfaceType) {
2084+
return false;
2085+
}
2086+
if (!annotationValue.isNative) {
2087+
return false;
2088+
}
2089+
return annotationValue
2090+
.getField(FfiVerifier._isLeafParamName)
2091+
?.toBoolValue() ??
2092+
false;
2093+
}
2094+
19622095
bool get isPacked {
19632096
final element = this.element;
19642097
return element is ConstructorElement &&
@@ -1973,6 +2106,44 @@ extension on ElementAnnotation {
19732106
}
19742107
}
19752108

2109+
extension on FunctionElement {
2110+
/// @Native(isLeaf: true) external function.
2111+
bool get isNativeLeaf {
2112+
for (final annotation in metadata) {
2113+
if (annotation.isNativeLeaf) {
2114+
return true;
2115+
}
2116+
}
2117+
return false;
2118+
}
2119+
}
2120+
2121+
extension on MethodElement {
2122+
/// @Native(isLeaf: true) external function.
2123+
bool get isNativeLeaf {
2124+
for (final annotation in metadata) {
2125+
if (annotation.isNativeLeaf) {
2126+
return true;
2127+
}
2128+
}
2129+
return false;
2130+
}
2131+
}
2132+
2133+
extension on MethodInvocation {
2134+
/// Calls @Native(isLeaf: true) external function.
2135+
bool get isNativeLeafInvocation {
2136+
final element = methodName.staticElement;
2137+
if (element is FunctionElement) {
2138+
return element.isNativeLeaf;
2139+
}
2140+
if (element is MethodElement) {
2141+
return element.isNativeLeaf;
2142+
}
2143+
return false;
2144+
}
2145+
}
2146+
19762147
extension on DartObject {
19772148
bool get isDefaultAsset {
19782149
return switch (type) {
@@ -2017,6 +2188,13 @@ extension on Element? {
20172188
return element is ClassElement && element.supertype.isAbiSpecificInteger;
20182189
}
20192190

2191+
bool get isAddressOfExtension {
2192+
final element = this;
2193+
return element is ExtensionElement &&
2194+
element.isFfiExtension &&
2195+
FfiVerifier._addressOfExtensionNames.contains(element.name);
2196+
}
2197+
20202198
/// Return `true` if this represents the extension `AllocatorAlloc`.
20212199
bool get isAllocatorExtension {
20222200
var element = this;

pkg/analyzer/messages.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18697,6 +18697,15 @@ FfiCode:
1869718697
const C();
1869818698
}
1869918699
```
18700+
ADDRESS_POSITION:
18701+
problemMessage: "The '.address' expression can only be used as argument to a leaf native external call."
18702+
hasPublishedDocs: false
18703+
comment: No parameters.
18704+
ADDRESS_RECEIVER:
18705+
problemMessage: "The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field."
18706+
correctionMessage: Change the receiver of '.address' to one of the allowed kinds.
18707+
hasPublishedDocs: false
18708+
comment: No parameters.
1870018709
ANNOTATION_ON_POINTER_FIELD:
1870118710
problemMessage: "Fields in a struct class whose type is 'Pointer' shouldn't have any annotations."
1870218711
correctionMessage: Try removing the annotation.

pkg/front_end/messages.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ FastaUsageShort/analyzerCode: Fail
380380
FastaUsageShort/example: Fail
381381
FfiAbiSpecificIntegerInvalid/analyzerCode: Fail
382382
FfiAbiSpecificIntegerMappingInvalid/analyzerCode: Fail
383+
FfiAddressPosition/analyzerCode: Fail
384+
FfiAddressReceiver/analyzerCode: Fail
383385
FfiCompoundImplementsFinalizable/analyzerCode: Fail
384386
FfiCreateOfStructOrUnion/analyzerCode: Fail
385387
FfiDartTypeMismatch/analyzerCode: Fail

pkg/front_end/messages.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5039,6 +5039,17 @@ FfiAbiSpecificIntegerMappingInvalid:
50395039
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size."
50405040
external: test/ffi_test.dart
50415041

5042+
FfiAddressPosition:
5043+
# Used by dart:ffi
5044+
problemMessage: "The '.address' expression can only be used as argument to a leaf native external call."
5045+
external: test/ffi_test.dart
5046+
5047+
FfiAddressReceiver:
5048+
# Used by dart:ffi
5049+
problemMessage: "The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field."
5050+
correctionMessage: "Change the receiver of '.address' to one of the allowed kinds."
5051+
external: test/ffi_test.dart
5052+
50425053
FfiCreateOfStructOrUnion:
50435054
# Used by dart:ffi
50445055
problemMessage: "Subclasses of 'Struct' and 'Union' are backed by native memory, and can't be instantiated by a generative constructor. Try allocating it via allocation, or load from a 'Pointer'."

pkg/front_end/test/spell_checking_list_messages.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ tojs
143143
trusttypes
144144
type's
145145
type3.#name
146+
typeddata
146147
typeof
147148
u
148149
unavailable

0 commit comments

Comments
 (0)