Version 2.15.0-211.0.dev
Merge commit '396e58e0f20c36005240e5ced436b18d0094a5c6' into 'dev'
diff --git a/DEPS b/DEPS
index c6cd91f..d2446c0 100644
--- a/DEPS
+++ b/DEPS
@@ -163,7 +163,7 @@
"test_reflective_loader_rev": "fcfce37666672edac849d2af6dffc0f8df236a94",
"test_rev": "099dcc4d052a30c6921489cfbefa1c8531d12975",
"typed_data_rev": "29ce5a92b03326d0b8035916ac04f528874994bd",
- "usage_rev": "e0780cd8b2f8af69a28dc52678ffe8492da27d06",
+ "usage_rev": "ac53249e5230a77624f46c07c2ed965efcfc0c59",
"vector_math_rev": "0c9f5d68c047813a6dcdeb88ba7a42daddf25025",
"watcher_rev": "3924194385fb215cef483193ed2879a618a3d69c",
"webdriver_rev": "ff5ccb1522edf4bed578ead4d65e0cbc1f2c4f02",
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index b0ff32b..073c5da 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -4106,14 +4106,79 @@
problemMessage: r"""FFI leaf call must not have Handle argument types.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeFfiNativeAnnotationMustAnnotateStatic =
- messageFfiNativeAnnotationMustAnnotateStatic;
+const Code<Null> codeFfiNativeMustBeExternal = messageFfiNativeMustBeExternal;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageFfiNativeAnnotationMustAnnotateStatic =
- const MessageCode("FfiNativeAnnotationMustAnnotateStatic",
+const MessageCode messageFfiNativeMustBeExternal = const MessageCode(
+ "FfiNativeMustBeExternal",
+ problemMessage: r"""FfiNative functions must be marked external.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeFfiNativeOnlyNativeFieldWrapperClassCanBePointer =
+ messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer =
+ const MessageCode("FfiNativeOnlyNativeFieldWrapperClassCanBePointer",
problemMessage:
- r"""FfiNative annotations can only be used on static functions.""");
+ r"""Only classes extending NativeFieldWrapperClass1 can be passed as Pointer.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(int count, int count2)>
+ templateFfiNativeUnexpectedNumberOfParameters =
+ const Template<Message Function(int count, int count2)>(
+ problemMessageTemplate:
+ r"""Unexpected number of FfiNative annotation parameters. Expected #count but has #count2.""",
+ withArguments: _withArgumentsFfiNativeUnexpectedNumberOfParameters);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(int count, int count2)>
+ codeFfiNativeUnexpectedNumberOfParameters =
+ const Code<Message Function(int count, int count2)>(
+ "FfiNativeUnexpectedNumberOfParameters",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiNativeUnexpectedNumberOfParameters(
+ int count, int count2) {
+ // ignore: unnecessary_null_comparison
+ if (count == null) throw 'No count provided';
+ // ignore: unnecessary_null_comparison
+ if (count2 == null) throw 'No count provided';
+ return new Message(codeFfiNativeUnexpectedNumberOfParameters,
+ problemMessage:
+ """Unexpected number of FfiNative annotation parameters. Expected ${count} but has ${count2}.""",
+ arguments: {'count': count, 'count2': count2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(int count, int count2)>
+ templateFfiNativeUnexpectedNumberOfParametersWithReceiver =
+ const Template<Message Function(int count, int count2)>(
+ problemMessageTemplate:
+ r"""Unexpected number of FfiNative annotation parameters. Expected #count but has #count2. FfiNative instance method annotation must have receiver as first argument.""",
+ withArguments:
+ _withArgumentsFfiNativeUnexpectedNumberOfParametersWithReceiver);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(int count, int count2)>
+ codeFfiNativeUnexpectedNumberOfParametersWithReceiver =
+ const Code<Message Function(int count, int count2)>(
+ "FfiNativeUnexpectedNumberOfParametersWithReceiver",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiNativeUnexpectedNumberOfParametersWithReceiver(
+ int count, int count2) {
+ // ignore: unnecessary_null_comparison
+ if (count == null) throw 'No count provided';
+ // ignore: unnecessary_null_comparison
+ if (count2 == null) throw 'No count provided';
+ return new Message(codeFfiNativeUnexpectedNumberOfParametersWithReceiver,
+ problemMessage:
+ """Unexpected number of FfiNative annotation parameters. Expected ${count} but has ${count2}. FfiNative instance method annotation must have receiver as first argument.""",
+ arguments: {'count': count, 'count2': count2});
+}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 26d0396..311591d 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -480,7 +480,11 @@
FfiCode.EMPTY_STRUCT,
FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD,
FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY,
- FfiCode.FFI_NATIVE_ONLY_STATIC,
+ FfiCode.FFI_NATIVE_MUST_BE_EXTERNAL,
+ FfiCode
+ .FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
+ FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS,
+ FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER,
FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER,
FfiCode.FIELD_INITIALIZER_IN_STRUCT,
FfiCode.FIELD_MUST_BE_EXTERNAL_IN_STRUCT,
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
index 3af2022..c2b7d56 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
@@ -77,10 +77,45 @@
/**
* No parameters.
*/
- static const FfiCode FFI_NATIVE_ONLY_STATIC = FfiCode(
- 'FFI_NATIVE_ONLY_STATIC',
- "FfiNative annotations can only be used on static functions.",
- correctionMessage: "Change the method to static.",
+ static const FfiCode FFI_NATIVE_MUST_BE_EXTERNAL = FfiCode(
+ 'FFI_NATIVE_MUST_BE_EXTERNAL',
+ "FfiNative functions must be declared external.",
+ correctionMessage: "Add the `external` keyword to the function.",
+ );
+
+ /**
+ * No parameters.
+ */
+ static const FfiCode
+ FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER =
+ FfiCode(
+ 'FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER',
+ "Only classes extending NativeFieldWrapperClass1 can be passed as Pointer.",
+ correctionMessage: "Pass as Handle instead.",
+ );
+
+ /**
+ * Parameters:
+ * 0: the expected number of parameters
+ * 1: the actual number of parameters
+ */
+ static const FfiCode FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS = FfiCode(
+ 'FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS',
+ "Unexpected number of FfiNative annotation parameters. Expected {0} but has {1}.",
+ correctionMessage: "Make sure parameters match the function annotated.",
+ );
+
+ /**
+ * Parameters:
+ * 0: the expected number of parameters
+ * 1: the actual number of parameters
+ */
+ static const FfiCode
+ FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER = FfiCode(
+ 'FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER',
+ "Unexpected number of FfiNative annotation parameters. Expected {0} but has {1}. FfiNative instance method annotation must have receiver as first argument.",
+ correctionMessage:
+ "Make sure parameters match the function annotated, including an extra first parameter for the receiver.",
);
/**
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index c8fc471..af80e94 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -276,24 +276,119 @@
}
for (Annotation annotation in annotations) {
- if (annotation.name.name == _ffiNativeName) {
- // All FFI Natives must be static.
- final isStatic = (node is FunctionDeclaration) ||
- ((node is MethodDeclaration) && node.isStatic);
- if (!isStatic) {
+ if (annotation.name.name != _ffiNativeName) {
+ continue;
+ }
+
+ final NodeList<Expression> arguments = annotation.arguments!.arguments;
+ final NodeList<TypeAnnotation> typeArguments =
+ annotation.typeArguments!.arguments;
+
+ final ffiSignature = typeArguments[0].type! as FunctionType;
+
+ // Leaf call FFI Natives can't use Handles.
+ _validateFfiLeafCallUsesNoHandles(arguments, ffiSignature, node);
+
+ if (node is MethodDeclaration) {
+ if (!node.declaredElement!.isExternal) {
_errorReporter.reportErrorForNode(
- FfiCode.FFI_NATIVE_ONLY_STATIC, node);
+ FfiCode.FFI_NATIVE_MUST_BE_EXTERNAL, node);
}
- // Leaf call FFI Natives can't use Handles.
- ArgumentList? argumentList = annotation.arguments;
- if (argumentList != null) {
- NodeList<Expression> arguments = argumentList.arguments;
- TypeArgumentList? typeArgumentList = annotation.typeArguments;
- if (typeArgumentList != null) {
- NodeList<TypeAnnotation> typeArguments = typeArgumentList.arguments;
- if (typeArguments.isNotEmpty && typeArguments[0].type != null) {
- _validateFfiLeafCallUsesNoHandles(
- arguments, typeArguments[0].type!, node);
+
+ List<DartType> ffiParameterTypes;
+ if (!node.isStatic) {
+ // Instance methods must have the receiver as an extra parameter in the
+ // FfiNative annotation.
+ if (node.parameters!.parameters.length + 1 !=
+ ffiSignature.parameters.length) {
+ _errorReporter.reportErrorForNode(
+ FfiCode
+ .FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER,
+ node,
+ [
+ node.parameters!.parameters.length + 1,
+ ffiSignature.parameters.length
+ ]);
+ return;
+ }
+
+ // Receiver can only be Pointer if the class extends
+ // NativeFieldWrapperClass1.
+ if (ffiSignature.normalParameterTypes[0].isPointer) {
+ final cls = node.declaredElement!.enclosingElement as ClassElement;
+ if (!_extendsNativeFieldWrapperClass1(cls.thisType)) {
+ _errorReporter.reportErrorForNode(
+ FfiCode
+ .FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
+ node);
+ }
+ }
+
+ ffiParameterTypes = ffiSignature.normalParameterTypes.sublist(1);
+ } else {
+ // Number of parameters in the FfiNative annotation must match the
+ // annotated declaration.
+ if (node.parameters!.parameters.length !=
+ ffiSignature.parameters.length) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS, node, [
+ ffiSignature.parameters.length,
+ node.parameters!.parameters.length
+ ]);
+ return;
+ }
+
+ ffiParameterTypes = ffiSignature.normalParameterTypes;
+ }
+
+ // Arguments can only be Pointer if the class extends
+ // NativeFieldWrapperClass1.
+ for (var i = 0; i < node.parameters!.parameters.length; i++) {
+ if (ffiParameterTypes[i].isPointer) {
+ final type = node.parameters!.parameters[i].declaredElement!.type;
+ if (!_extendsNativeFieldWrapperClass1(type as InterfaceType)) {
+ _errorReporter.reportErrorForNode(
+ FfiCode
+ .FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
+ node);
+ }
+ }
+ }
+
+ continue;
+ }
+
+ if (node is FunctionDeclaration) {
+ if (!node.declaredElement!.isExternal) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.FFI_NATIVE_MUST_BE_EXTERNAL, node);
+ }
+
+ // Number of parameters in the FfiNative annotation must match the
+ // annotated declaration.
+ if (node.functionExpression.parameters!.parameters.length !=
+ ffiSignature.parameters.length) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS, node, [
+ ffiSignature.parameters.length,
+ node.functionExpression.parameters!.parameters.length
+ ]);
+ return;
+ }
+
+ // Arguments can only be Pointer if the class extends
+ // NativeFieldWrapperClass1.
+ for (var i = 0;
+ i < node.functionExpression.parameters!.parameters.length;
+ i++) {
+ if (ffiSignature.normalParameterTypes[i].isPointer) {
+ final type = node.functionExpression.parameters!.parameters[i]
+ .declaredElement!.type;
+ if (!_extendsNativeFieldWrapperClass1(type as InterfaceType)) {
+ _errorReporter.reportErrorForNode(
+ FfiCode
+ .FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER,
+ node);
}
}
}
@@ -301,6 +396,17 @@
}
}
+ bool _extendsNativeFieldWrapperClass1(InterfaceType? type) {
+ while (type != null) {
+ if (type.getDisplayString(withNullability: false) ==
+ 'NativeFieldWrapperClass1') {
+ return true;
+ }
+ type = type.element.supertype;
+ }
+ return false;
+ }
+
/// Returns `true` if [nativeType] is a C type that has a size.
bool _isSized(DartType nativeType) {
switch (_primitiveNativeType(nativeType)) {
@@ -639,27 +745,26 @@
void _validateFfiLeafCallUsesNoHandles(
NodeList<Expression> args, DartType nativeType, AstNode errorNode) {
- if (args.isNotEmpty) {
- for (final arg in args) {
- if (arg is NamedExpression) {
- if (arg.element?.name == _isLeafParamName) {
- // Handles are ok for regular (non-leaf) calls. Check `isLeaf:true`.
- final bool? isLeaf = _maybeGetBoolConstValue(arg.expression);
- if (isLeaf != null && isLeaf) {
- if (nativeType is FunctionType) {
- if (_primitiveNativeType(nativeType.returnType) ==
- _PrimitiveDartType.handle) {
- _errorReporter.reportErrorForNode(
- FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, errorNode);
- }
- for (final param in nativeType.normalParameterTypes) {
- if (_primitiveNativeType(param) ==
- _PrimitiveDartType.handle) {
- _errorReporter.reportErrorForNode(
- FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, errorNode);
- }
- }
- }
+ if (args.isEmpty) {
+ return;
+ }
+ for (final arg in args) {
+ if (arg is! NamedExpression || arg.element?.name != _isLeafParamName) {
+ continue;
+ }
+ // Handles are ok for regular (non-leaf) calls. Check `isLeaf:true`.
+ final bool? isLeaf = _maybeGetBoolConstValue(arg.expression);
+ if (isLeaf != null && isLeaf) {
+ if (nativeType is FunctionType) {
+ if (_primitiveNativeType(nativeType.returnType) ==
+ _PrimitiveDartType.handle) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, errorNode);
+ }
+ for (final param in nativeType.normalParameterTypes) {
+ if (_primitiveNativeType(param) == _PrimitiveDartType.handle) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, errorNode);
}
}
}
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 7f21e4e..ebd0349 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -13760,10 +13760,28 @@
problemMessage: "'Array's must have exactly one 'Array' annotation."
correctionMessage: Try removing the extra annotation.
comment: No parameters.
- FFI_NATIVE_ONLY_STATIC:
- problemMessage: FfiNative annotations can only be used on static functions.
- correctionMessage: Change the method to static.
+ FFI_NATIVE_MUST_BE_EXTERNAL:
+ problemMessage: FfiNative functions must be declared external.
+ correctionMessage: Add the `external` keyword to the function.
comment: No parameters.
+ FFI_NATIVE_ONLY_CLASSES_EXTENDING_NATIVEFIELDWRAPPERCLASS1_CAN_BE_POINTER:
+ problemMessage: Only classes extending NativeFieldWrapperClass1 can be passed as Pointer.
+ correctionMessage: Pass as Handle instead.
+ comment: No parameters.
+ FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS:
+ problemMessage: Unexpected number of FfiNative annotation parameters. Expected {0} but has {1}.
+ correctionMessage: Make sure parameters match the function annotated.
+ comment: |-
+ Parameters:
+ 0: the expected number of parameters
+ 1: the actual number of parameters
+ FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER:
+ problemMessage: Unexpected number of FfiNative annotation parameters. Expected {0} but has {1}. FfiNative instance method annotation must have receiver as first argument.
+ correctionMessage: Make sure parameters match the function annotated, including an extra first parameter for the receiver.
+ comment: |-
+ Parameters:
+ 0: the expected number of parameters
+ 1: the actual number of parameters
FIELD_INITIALIZER_IN_STRUCT:
problemMessage: "Constructors in subclasses of 'Struct' and 'Union' can't have field initializers."
correctionMessage: Try removing the field initializer and marking the field as external.
diff --git a/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart b/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
index a11172c..834d565 100644
--- a/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
@@ -15,18 +15,6 @@
@reflectiveTest
class FfiNativeTest extends PubPackageResolutionTest {
- test_FfiNativeAnnotationOnInstanceMethod() async {
- await assertErrorsInCode(r'''
-import 'dart:ffi';
-class K {
- @FfiNative<Void Function()>('DoesntMatter')
- external void doesntMatter();
-}
-''', [
- error(FfiCode.FFI_NATIVE_ONLY_STATIC, 31, 75),
- ]);
- }
-
test_FfiNativeCanUseHandles() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@@ -35,6 +23,27 @@
''', []);
}
+ test_FfiNativeCanUseLeaf() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+@FfiNative<Int8 Function(Int64)>('DoesntMatter', isLeaf:true)
+external int doesntMatter(int);
+''', []);
+ }
+
+ test_FfiNativeInstanceMethodsMustHaveReceiver() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+class K {
+ @FfiNative<Void Function(Double)>('DoesntMatter')
+ external void doesntMatter(double x);
+}
+''', [
+ error(FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER,
+ 31, 89),
+ ]);
+ }
+
test_FfiNativeLeafMustNotReturnHandle() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@@ -54,4 +63,24 @@
error(FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, 19, 100),
]);
}
+
+ test_FfiNativeTooFewParameters() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+@FfiNative<Void Function(Double)>('DoesntMatter')
+external void doesntMatter(double x, double y);
+''', [
+ error(FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS, 19, 97),
+ ]);
+ }
+
+ test_FfiNativeTooManyParameters() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+@FfiNative<Void Function(Double, Double)>('DoesntMatter')
+external void doesntMatter(double x);
+''', [
+ error(FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS, 19, 95),
+ ]);
+ }
}
diff --git a/pkg/analyzer_utilities/lib/tools.dart b/pkg/analyzer_utilities/lib/tools.dart
index abb3375..a072d85 100644
--- a/pkg/analyzer_utilities/lib/tools.dart
+++ b/pkg/analyzer_utilities/lib/tools.dart
@@ -10,6 +10,7 @@
import 'package:analyzer_utilities/text_formatter.dart';
import 'package:html/dom.dart' as dom;
import 'package:path/path.dart';
+import 'package:test/test.dart';
final RegExp trailingSpacesInLineRegExp = RegExp(r' +$', multiLine: true);
final RegExp trailingWhitespaceRegExp = RegExp(r'[\n ]+$');
@@ -290,7 +291,7 @@
}
var generateScript = normalize(joinAll(posix.split(generatorPath)));
print(' $executable$packageRoot $generateScript ${args.join(" ")}');
- exit(1);
+ fail('Error codes need to be generated');
} else {
print('All generated files up to date.');
}
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index e981bd3..141014c 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -54,8 +54,9 @@
messageFfiExpectedConstant,
messageFfiLeafCallMustNotReturnHandle,
messageFfiLeafCallMustNotTakeHandle,
+ messageFfiNativeMustBeExternal,
+ messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
messageFfiPackedAnnotationAlignment,
- messageFfiNativeAnnotationMustAnnotateStatic,
messageNonPositiveArrayDimensions,
noLength,
templateFfiDartTypeMismatch,
@@ -69,6 +70,8 @@
templateFfiFieldInitializer,
templateFfiFieldNoAnnotation,
templateFfiFieldNull,
+ templateFfiNativeUnexpectedNumberOfParameters,
+ templateFfiNativeUnexpectedNumberOfParametersWithReceiver,
templateFfiNotStatic,
templateFfiPackedAnnotation,
templateFfiPackedNestingNonPacked,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 97ad8a0..308d0a6 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -347,7 +347,10 @@
FfiFieldNull/analyzerCode: Fail
FfiLeafCallMustNotReturnHandle/analyzerCode: Fail
FfiLeafCallMustNotTakeHandle/analyzerCode: Fail
-FfiNativeAnnotationMustAnnotateStatic/analyzerCode: Fail
+FfiNativeMustBeExternal/analyzerCode: Fail
+FfiNativeOnlyNativeFieldWrapperClassCanBePointer/analyzerCode: Fail
+FfiNativeUnexpectedNumberOfParameters/analyzerCode: Fail
+FfiNativeUnexpectedNumberOfParametersWithReceiver/analyzerCode: Fail
FfiNotStatic/analyzerCode: Fail
FfiPackedAnnotation/analyzerCode: Fail
FfiPackedAnnotationAlignment/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index bebd92b..c792862 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4672,9 +4672,24 @@
problemMessage: "FFI leaf call must not have Handle return type."
external: test/ffi_test.dart
-FfiNativeAnnotationMustAnnotateStatic:
+FfiNativeUnexpectedNumberOfParametersWithReceiver:
# Used by dart:ffi
- problemMessage: "FfiNative annotations can only be used on static functions."
+ problemMessage: "Unexpected number of FfiNative annotation parameters. Expected #count but has #count2. FfiNative instance method annotation must have receiver as first argument."
+ external: test/ffi_test.dart
+
+FfiNativeUnexpectedNumberOfParameters:
+ # Used by dart:ffi
+ problemMessage: "Unexpected number of FfiNative annotation parameters. Expected #count but has #count2."
+ external: test/ffi_test.dart
+
+FfiNativeOnlyNativeFieldWrapperClassCanBePointer:
+ # Used by dart:ffi
+ problemMessage: "Only classes extending NativeFieldWrapperClass1 can be passed as Pointer."
+ external: test/ffi_test.dart
+
+FfiNativeMustBeExternal:
+ # Used by dart:ffi
+ problemMessage: "FfiNative functions must be marked external."
external: test/ffi_test.dart
SpreadTypeMismatch:
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index e3c5e27..ffc3264 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -49,6 +49,7 @@
name.stack
nameokempty
native('native
+nativefieldwrapperclass
natively
nativetype
nnbd
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index b788180..4316715 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -157,10 +157,15 @@
if (!ffiHelper.importsFfi(component, libraries)) {
logger?.call("Skipped ffi transformation");
} else {
- // Transform @FfiNative(..) functions into ffi native call functions.
- transformFfiNative.transformLibraries(
- component, libraries, diagnosticReporter, referenceFromIndex);
+ // Transform @FfiNative(..) functions into FFI native call functions.
+ // Pass instance method receivers as implicit first argument to the static
+ // native function.
+ // Transform arguments that extend NativeFieldWrapperClass1 to Pointer if
+ // the native function expects Pointer (to avoid Handle overhead).
+ transformFfiNative.transformLibraries(component, coreTypes, hierarchy,
+ libraries, diagnosticReporter, referenceFromIndex);
logger?.call("Transformed ffi natives");
+
// TODO(jensj/dacoharkes): We can probably limit the transformations to
// libraries that transitivley depend on dart:ffi.
transformFfiDefinitions.transformLibraries(
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 25e2ac8..63727a5 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -235,7 +235,7 @@
final Class structClass;
final Class unionClass;
final Class ffiNativeClass;
- final Class nativeFieldWrapperClass;
+ final Class nativeFieldWrapperClass1Class;
final Class ffiStructLayoutClass;
final Field ffiStructLayoutTypesField;
final Field ffiStructLayoutPackingField;
@@ -291,9 +291,9 @@
final Procedure getNativeFieldFunction;
final Procedure reachabilityFenceFunction;
- late final DartType nativeFieldWrapperClassType;
- late final DartType voidType;
- late final DartType pointerType;
+ late final InterfaceType nativeFieldWrapperClass1Type;
+ late final InterfaceType voidType;
+ late final InterfaceType pointerVoidType;
/// Classes corresponding to [NativeType], indexed by [NativeType].
final List<Class> nativeTypesClasses;
@@ -355,7 +355,7 @@
structClass = index.getClass('dart:ffi', 'Struct'),
unionClass = index.getClass('dart:ffi', 'Union'),
ffiNativeClass = index.getClass('dart:ffi', 'FfiNative'),
- nativeFieldWrapperClass =
+ nativeFieldWrapperClass1Class =
index.getClass('dart:nativewrappers', 'NativeFieldWrapperClass1'),
ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'),
ffiStructLayoutTypesField =
@@ -471,11 +471,11 @@
'dart:nativewrappers', '_getNativeField'),
reachabilityFenceFunction =
index.getTopLevelProcedure('dart:_internal', 'reachabilityFence') {
- nativeFieldWrapperClassType =
- nativeFieldWrapperClass.getThisType(coreTypes, Nullability.nonNullable);
+ nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
+ coreTypes, Nullability.nonNullable);
voidType = nativeTypesClasses[NativeType.kVoid.index]
.getThisType(coreTypes, Nullability.nonNullable);
- pointerType =
+ pointerVoidType =
InterfaceType(pointerClass, Nullability.nonNullable, [voidType]);
}
diff --git a/pkg/vm/lib/transformations/ffi_native.dart b/pkg/vm/lib/transformations/ffi_native.dart
index 3427f28..ebd2cf6 100644
--- a/pkg/vm/lib/transformations/ffi_native.dart
+++ b/pkg/vm/lib/transformations/ffi_native.dart
@@ -2,35 +2,44 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:kernel/ast.dart';
-import 'package:kernel/library_index.dart' show LibraryIndex;
-import 'package:kernel/reference_from_index.dart'
- show IndexedLibrary, ReferenceFromIndex;
-import 'package:kernel/target/targets.dart' show DiagnosticReporter;
import 'package:front_end/src/api_unstable/vm.dart'
- show messageFfiNativeAnnotationMustAnnotateStatic;
+ show
+ messageFfiNativeMustBeExternal,
+ messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
+ templateFfiNativeUnexpectedNumberOfParameters,
+ templateFfiNativeUnexpectedNumberOfParametersWithReceiver;
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/library_index.dart' show LibraryIndex;
+import 'package:kernel/reference_from_index.dart' show ReferenceFromIndex;
+import 'package:kernel/target/targets.dart' show DiagnosticReporter;
+import 'package:kernel/type_environment.dart';
+
+import 'ffi.dart' show FfiTransformer;
/// Transform @FfiNative annotated functions into FFI native function pointer
/// functions.
void transformLibraries(
Component component,
+ CoreTypes coreTypes,
+ ClassHierarchy hierarchy,
List<Library> libraries,
DiagnosticReporter diagnosticReporter,
ReferenceFromIndex? referenceFromIndex) {
- final index = LibraryIndex(component, ['dart:ffi']);
+ final index = LibraryIndex(component,
+ ['dart:ffi', 'dart:_internal', 'dart:typed_data', 'dart:nativewrappers']);
// Skip if dart:ffi isn't loaded (e.g. during incremental compile).
if (index.tryGetClass('dart:ffi', 'FfiNative') == null) {
return;
}
- final transformer =
- FfiNativeTransformer(index, diagnosticReporter, referenceFromIndex);
+ final transformer = FfiNativeTransformer(
+ index, coreTypes, hierarchy, diagnosticReporter, referenceFromIndex);
libraries.forEach(transformer.visitLibrary);
}
-class FfiNativeTransformer extends Transformer {
- Library? currentLibrary;
- IndexedLibrary? currentLibraryIndex;
-
+class FfiNativeTransformer extends FfiTransformer {
final DiagnosticReporter diagnosticReporter;
final ReferenceFromIndex? referenceFromIndex;
final Class ffiNativeClass;
@@ -38,11 +47,17 @@
final Field ffiNativeNameField;
final Field ffiNativeIsLeafField;
final Field resolverField;
- final Procedure asFunctionProcedure;
- final Procedure fromAddressInternal;
+
+ // VariableDeclaration names can be null or empty string, in which case
+ // they're automatically assigned a "temporary" name like `#t0`.
+ static const variableDeclarationTemporaryName = null;
FfiNativeTransformer(
- LibraryIndex index, this.diagnosticReporter, this.referenceFromIndex)
+ LibraryIndex index,
+ CoreTypes coreTypes,
+ ClassHierarchy hierarchy,
+ this.diagnosticReporter,
+ this.referenceFromIndex)
: ffiNativeClass = index.getClass('dart:ffi', 'FfiNative'),
nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'),
ffiNativeNameField =
@@ -50,122 +65,396 @@
ffiNativeIsLeafField =
index.getField('dart:ffi', 'FfiNative', 'isLeaf'),
resolverField = index.getTopLevelField('dart:ffi', '_ffi_resolver'),
- asFunctionProcedure = index.getProcedure(
- 'dart:ffi', 'NativeFunctionPointer', 'asFunction'),
- fromAddressInternal =
- index.getTopLevelProcedure('dart:ffi', '_fromAddress') {}
+ super(index, coreTypes, hierarchy, diagnosticReporter,
+ referenceFromIndex);
- @override
- TreeNode visitLibrary(Library node) {
- assert(currentLibrary == null);
- currentLibrary = node;
- currentLibraryIndex = referenceFromIndex?.lookupLibrary(node);
- final result = super.visitLibrary(node);
- currentLibrary = null;
- return result;
- }
-
- InstanceConstant? _tryGetFfiNativeAnnotation(Member node) {
+ ConstantExpression? _tryGetFfiNativeAnnotation(Member node) {
for (final Expression annotation in node.annotations) {
- if (annotation is ConstantExpression) {
- if (annotation.constant is InstanceConstant) {
- final instConst = annotation.constant as InstanceConstant;
- if (instConst.classNode == ffiNativeClass) {
- return instConst;
- }
- }
+ if (annotation is! ConstantExpression) {
+ continue;
+ }
+ final annotationConstant = annotation.constant;
+ if (annotationConstant is! InstanceConstant) {
+ continue;
+ }
+ if (annotationConstant.classNode == ffiNativeClass) {
+ return annotation;
}
}
return null;
}
- // Transform:
- // @FfiNative<Double Function(Double)>('Math_sqrt', isLeaf:true)
- // external double _square_root(double x);
+ bool _extendsNativeFieldWrapperClass1(DartType type) {
+ if (type is InterfaceType) {
+ Class? cls = type.classNode;
+ while (cls != null) {
+ if (cls == nativeFieldWrapperClass1Class) {
+ return true;
+ }
+ cls = cls.superclass;
+ }
+ }
+ return false;
+ }
+
+ // Replaces parameters with Pointer if:
+ // 1) they extend NativeFieldWrapperClass1, and
+ // 2) the corresponding native parameter is Pointer.
+ FunctionType _pointerizeFunctionType(
+ FunctionType dartType, FunctionType nativeType) {
+ final parameters = <DartType>[];
+ for (var i = 0; i < dartType.positionalParameters.length; i++) {
+ final parameter = dartType.positionalParameters[i];
+ if (parameter is InterfaceType) {
+ final nativeParameter = nativeType.positionalParameters[i];
+ if (_extendsNativeFieldWrapperClass1(parameter) &&
+ env.isSubtypeOf(nativeParameter, pointerVoidType,
+ SubtypeCheckMode.ignoringNullabilities)) {
+ parameters.add(pointerVoidType);
+ continue;
+ }
+ }
+ parameters.add(parameter);
+ }
+ return FunctionType(parameters, dartType.returnType, dartType.nullability);
+ }
+
+ // Create field holding the resolved native function pointer.
//
- // Into:
- // final _@FfiNative__square_root =
- // Pointer<NativeFunction<Double Function(Double)>>
- // .fromAddress(_ffi_resolver('dart:math', 'Math_sqrt'))
- // .asFunction<double Function(double)>(isLeaf:true);
- // double _square_root(double x) => _@FfiNative__square_root(x);
- Statement transformFfiNative(
- Procedure node, InstanceConstant annotationConst) {
- assert(currentLibrary != null);
- final params = node.function.positionalParameters;
- final functionName = annotationConst
+ // For:
+ // @FfiNative<IntPtr Function(Pointer<Void>)>('DoXYZ', isLeaf:true)
+ // external int doXyz(NativeFieldWrapperClass1 obj);
+ //
+ // Create:
+ // static final _doXyz$FfiNative$ptr =
+ // Pointer<NativeFunction<IntPtr Function(Pointer<Void>)>>
+ // .fromAddress(_ffi_resolver('..', 'DoXYZ', 1))
+ // .asFunction<int Function(Pointer<Void>)>(isLeaf:true);
+ Field _createResolvedFfiNativeField(
+ InstanceConstant annotationConst,
+ String dartFunctionName,
+ FunctionType dartType,
+ FunctionType nativeType,
+ TreeNode? parent,
+ int fileOffset) {
+ final nativeFunctionName = annotationConst
.fieldValues[ffiNativeNameField.fieldReference] as StringConstant;
final isLeaf = annotationConst
.fieldValues[ffiNativeIsLeafField.fieldReference] as BoolConstant;
- // double Function(double)
- final DartType dartType =
- node.function.computeThisFunctionType(Nullability.nonNullable);
- // Double Function(Double)
- final nativeType = annotationConst.typeArguments[0] as FunctionType;
- // InterfaceType(NativeFunction<Double Function(Double)>)
- final DartType nativeInterfaceType = InterfaceType(
- nativeFunctionClass, Nullability.nonNullable, [nativeType]);
+ // int Function(Pointer<Void>)
+ final dartTypePointerized = _pointerizeFunctionType(dartType, nativeType);
// Derive number of arguments from the native function signature.
- final args_n = nativeType.positionalParameters.length;
+ final numberNativeArgs = nativeType.positionalParameters.length;
- // TODO(dartbug.com/31579): Add `..fileOffset`s once we can handle these in
- // patch files.
-
- // _ffi_resolver('dart:math', 'Math_sqrt', 1)
+ // _ffi_resolver('...', 'DoXYZ', 1)
final resolverInvocation = FunctionInvocation(
FunctionAccessKind.FunctionType,
StaticGet(resolverField),
Arguments([
ConstantExpression(
- StringConstant(currentLibrary!.importUri.toString())),
- ConstantExpression(functionName),
- ConstantExpression(IntConstant(args_n)),
+ StringConstant(currentLibrary.importUri.toString())),
+ ConstantExpression(nativeFunctionName),
+ ConstantExpression(IntConstant(numberNativeArgs)),
]),
- functionType: resolverField.type as FunctionType);
+ functionType: resolverField.type as FunctionType)
+ ..fileOffset = fileOffset;
// _fromAddress<NativeFunction<Double Function(Double)>>(...)
- final fromAddressInvocation = StaticInvocation(fromAddressInternal,
- Arguments([resolverInvocation], types: [nativeInterfaceType]));
+ final fromAddressInvocation = StaticInvocation(
+ fromAddressInternal,
+ Arguments([
+ resolverInvocation
+ ], types: [
+ InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeType])
+ ]))
+ ..fileOffset = fileOffset;
// NativeFunctionPointer.asFunction
// <Double Function(Double), double Function(double)>(..., isLeaf:true)
final asFunctionInvocation = StaticInvocation(
- asFunctionProcedure,
+ asFunctionMethod,
Arguments([fromAddressInvocation],
- types: [nativeType, dartType],
- named: [NamedExpression("isLeaf", BoolLiteral(isLeaf.value))]));
+ types: [nativeType, dartTypePointerized],
+ named: [NamedExpression('isLeaf', BoolLiteral(isLeaf.value))]))
+ ..fileOffset = fileOffset;
- // final _@FfiNative__square_root = ...
- final fieldName = Name('_@FfiNative_${node.name.text}', currentLibrary);
- final funcPtrField = Field.immutable(fieldName,
- type: dartType,
+ // static final _doXyz$FfiNative$Ptr = ...
+ final fieldName =
+ Name('_$dartFunctionName\$FfiNative\$Ptr', currentLibrary);
+ final functionPointerField = Field.immutable(fieldName,
+ type: dartTypePointerized,
initializer: asFunctionInvocation,
isStatic: true,
isFinal: true,
- fileUri: currentLibrary!.fileUri,
+ fileUri: currentLibrary.fileUri,
getterReference: currentLibraryIndex?.lookupGetterReference(fieldName))
- ..fileOffset = node.fileOffset;
+ ..fileOffset = fileOffset;
+
// Add field to the parent the FfiNative function belongs to.
- final parent = node.parent;
if (parent is Class) {
- parent.addField(funcPtrField);
+ parent.addField(functionPointerField);
} else if (parent is Library) {
- parent.addField(funcPtrField);
+ parent.addField(functionPointerField);
} else {
throw 'Unexpected parent of @FfiNative function. '
'Expected Class or Library, but found ${parent}.';
}
- // _@FfiNative__square_root(x)
- final callFuncPtrInvocation = FunctionInvocation(
- FunctionAccessKind.FunctionType,
- StaticGet(funcPtrField),
- Arguments(params.map<Expression>((p) => VariableGet(p)).toList()),
- functionType: dartType as FunctionType);
+ return functionPointerField;
+ }
- return ReturnStatement(callFuncPtrInvocation);
+ // FfiNative calls that pass objects extending NativeFieldWrapperClass1
+ // should be passed as Pointer instead so we don't have the overhead of
+ // converting Handles.
+ // If we find a NativeFieldWrapperClass1 object being passed to an FfiNative
+ // signature taking a Pointer, we automatically wrap the argument in a call to
+ // `Pointer.fromAddress(_getNativeField(obj))`.
+ //
+ // Example:
+ // passAsPointer(ClassWithNativeField());
+ //
+ // Becomes, roughly:
+ // {
+ // final NativeFieldWrapperClass1#t0 = ClassWithNativeField();
+ // final #t1 = passAsPointer(Pointer.fromAddress(_getNativeField(#t0)));
+ // reachabilityFence(#t0);
+ // } => #t1
+ Expression _convertArgumentsNativeFieldWrapperClass1ToPointer(
+ FunctionInvocation invocation,
+ List<DartType> ffiParameters,
+ List<VariableDeclaration> dartParameters) {
+ // Create lists of temporary variables for arguments potentially being
+ // wrapped, and the (potentially) wrapped arguments to be passed.
+ final temporariesForArguments = [];
+ final callArguments = <Expression>[];
+ final fencedArguments = [];
+ bool hasPointer = false;
+ for (int i = 0; i < invocation.arguments.positional.length; i++) {
+ if (env.isSubtypeOf(ffiParameters[i], pointerVoidType,
+ SubtypeCheckMode.ignoringNullabilities) &&
+ !env.isSubtypeOf(dartParameters[i].type, pointerVoidType,
+ SubtypeCheckMode.ignoringNullabilities)) {
+ // Only NativeFieldWrapperClass1 instances can be passed as pointer.
+ if (!_extendsNativeFieldWrapperClass1(dartParameters[i].type)) {
+ diagnosticReporter.report(
+ messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
+ dartParameters[i].fileOffset,
+ 1,
+ invocation.location?.file);
+ }
+
+ hasPointer = true;
+
+ // final NativeFieldWrapperClass1 #t0 = ClassWithNativeField();
+ final argument = VariableDeclaration(variableDeclarationTemporaryName,
+ initializer: invocation.arguments.positional[i],
+ type: nativeFieldWrapperClass1Type,
+ isFinal: true);
+ temporariesForArguments.add(argument);
+ fencedArguments.add(argument);
+
+ // Pointer.fromAddress(_getNativeField(#t0))
+ final ptr = StaticInvocation(
+ fromAddressInternal,
+ Arguments([
+ StaticInvocation(
+ getNativeFieldFunction, Arguments([VariableGet(argument)]))
+ ], types: [
+ voidType
+ ]));
+ callArguments.add(ptr);
+
+ continue;
+ }
+ // Note: We also evaluate, and assign temporaries for, non-wrapped
+ // arguments as we need to preserve the original evaluation order.
+ final argument = VariableDeclaration(variableDeclarationTemporaryName,
+ initializer: invocation.arguments.positional[i],
+ type: dartParameters[i].type,
+ isFinal: true);
+ temporariesForArguments.add(argument);
+ callArguments.add(VariableGet(argument));
+ }
+
+ // If there are no arguments to convert then we can drop the whole wrap.
+ if (!hasPointer) {
+ return invocation;
+ }
+
+ invocation.arguments = Arguments(callArguments);
+
+ // {
+ // final NativeFieldWrapperClass1 #t0 = ClassWithNativeField();
+ // final T #t1 = foo(Pointer.fromAddress(_getNativeField(#t0)));
+ // reachabilityFence(#t0);
+ // } => #t1
+ final result = VariableDeclaration(variableDeclarationTemporaryName,
+ initializer: invocation,
+ type: invocation.functionType!.returnType,
+ isFinal: true);
+ return BlockExpression(
+ Block([
+ ...temporariesForArguments,
+ result,
+ for (final argument in fencedArguments)
+ ExpressionStatement(StaticInvocation(
+ reachabilityFenceFunction, Arguments([VariableGet(argument)])))
+ ]),
+ VariableGet(result),
+ );
+ }
+
+ // Transform FfiNative instance methods.
+ // Example:
+ // class MyNativeClass extends NativeFieldWrapperClass1 {
+ // @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyClass_MyMethod')
+ // external int myMethod(int x);
+ // }
+ // Becomes, roughly:
+ // ... {
+ // static final _myMethod$FfiNative$Ptr = ...
+ // static _myMethod$FfiNative(MyNativeClass self, int x)
+ // => _myMethod$FfiNative$Ptr(
+ // Pointer<Void>.fromAddress(_getNativeField(self)), x);
+ // int myMethod(int x) => _myMethod$FfiNative(this, x);
+ // }
+ Procedure _transformInstanceMethod(
+ Procedure node, InstanceConstant ffiConstant, int annotationOffset) {
+ final ffiSignature = ffiConstant.typeArguments[0] as FunctionType;
+
+ // The FfiNative annotation should have an extra parameter for `self`.
+ if (node.function.positionalParameters.length + 1 !=
+ ffiSignature.positionalParameters.length) {
+ diagnosticReporter.report(
+ templateFfiNativeUnexpectedNumberOfParametersWithReceiver
+ .withArguments(node.function.positionalParameters.length + 1,
+ ffiSignature.positionalParameters.length),
+ annotationOffset,
+ 1,
+ node.location?.file);
+ return node;
+ }
+
+ final cls = node.parent as Class;
+
+ final staticParameters = [
+ // Add the implicit `self` for the inner glue functions.
+ VariableDeclaration('self',
+ type: cls.getThisType(coreTypes, Nullability.nonNullable)),
+ for (final parameter in node.function.positionalParameters)
+ VariableDeclaration(parameter.name, type: parameter.type)
+ ];
+
+ final staticFunctionType = FunctionType(
+ staticParameters.map((e) => e.type).toList(),
+ node.function.returnType,
+ Nullability.nonNullable);
+
+ // static final _myMethod$FfiNative$Ptr = ..
+ final resolvedField = _createResolvedFfiNativeField(
+ ffiConstant,
+ node.name.text,
+ staticFunctionType,
+ ffiSignature,
+ node.parent,
+ node.fileOffset);
+
+ // _myMethod$FfiNative$Ptr(self, x)
+ final functionPointerInvocation = FunctionInvocation(
+ FunctionAccessKind.FunctionType,
+ StaticGet(resolvedField),
+ Arguments(
+ [for (final parameter in staticParameters) VariableGet(parameter)]),
+ functionType: staticFunctionType)
+ ..fileOffset = node.fileOffset;
+
+ // static _myMethod$FfiNative(MyNativeClass self, int x)
+ // => _myMethod$FfiNative$Ptr(
+ // Pointer<Void>.fromAddress(_getNativeField(self)), x)
+ final ffiProcedure = Procedure(
+ Name('_${node.name.text}\$FfiNative', currentLibrary),
+ ProcedureKind.Method,
+ FunctionNode(
+ ReturnStatement(_convertArgumentsNativeFieldWrapperClass1ToPointer(
+ functionPointerInvocation,
+ ffiSignature.positionalParameters,
+ staticParameters)),
+ positionalParameters: staticParameters),
+ isStatic: true,
+ fileUri: node.fileUri)
+ ..fileOffset = node.fileOffset;
+ cls.addProcedure(ffiProcedure);
+
+ // => _myMethod$FfiNative(this, x)
+ node.function.body = ReturnStatement(StaticInvocation(
+ ffiProcedure,
+ Arguments([
+ ThisExpression(),
+ for (var parameter in node.function.positionalParameters)
+ VariableGet(parameter)
+ ])))
+ ..parent = node.function;
+
+ return node;
+ }
+
+ // Transform FfiNative static functions.
+ // Example:
+ // @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('MyFunction')
+ // external int myFunction(MyNativeClass obj, int x);
+ // Becomes, roughly:
+ // static final _myFunction$FfiNative$Ptr = ...
+ // int myFunction(MyNativeClass obj, int x)
+ // => myFunction$FfiNative$Ptr(
+ // Pointer<Void>.fromAddress(_getNativeField(obj)), x);
+ Procedure _transformStaticFunction(
+ Procedure node, InstanceConstant ffiConstant, int annotationOffset) {
+ final ffiSignature = ffiConstant.typeArguments[0] as FunctionType;
+
+ if (node.function.positionalParameters.length !=
+ ffiSignature.positionalParameters.length) {
+ diagnosticReporter.report(
+ templateFfiNativeUnexpectedNumberOfParameters.withArguments(
+ node.function.positionalParameters.length,
+ ffiSignature.positionalParameters.length),
+ annotationOffset,
+ 1,
+ node.location?.file);
+ return node;
+ }
+
+ // _myFunction$FfiNative$Ptr = ..
+ final resolvedField = _createResolvedFfiNativeField(
+ ffiConstant,
+ node.name.text,
+ node.function.computeThisFunctionType(Nullability.nonNullable),
+ ffiSignature,
+ node.parent,
+ node.fileOffset);
+
+ // _myFunction$FfiNative$Ptr(obj, x)
+ final functionPointerInvocation = FunctionInvocation(
+ FunctionAccessKind.FunctionType,
+ StaticGet(resolvedField),
+ Arguments([
+ for (final parameter in node.function.positionalParameters)
+ VariableGet(parameter)
+ ]),
+ functionType: resolvedField.type as FunctionType)
+ ..fileOffset = node.fileOffset;
+
+ // => _myFunction$FfiNative$Ptr(
+ // Pointer<Void>.fromAddress(_getNativeField(obj)), x)
+ node.function.body = ReturnStatement(
+ _convertArgumentsNativeFieldWrapperClass1ToPointer(
+ functionPointerInvocation,
+ ffiSignature.positionalParameters,
+ node.function.positionalParameters))
+ ..parent = node.function;
+
+ return node;
}
@override
@@ -173,23 +462,30 @@
// Only transform functions that are external and have FfiNative annotation:
// @FfiNative<Double Function(Double)>('Math_sqrt')
// external double _square_root(double x);
- if (!node.isExternal) {
- return node;
- }
- InstanceConstant? ffiNativeAnnotation = _tryGetFfiNativeAnnotation(node);
+ final ffiNativeAnnotation = _tryGetFfiNativeAnnotation(node);
if (ffiNativeAnnotation == null) {
return node;
}
+ if (!node.isExternal) {
+ diagnosticReporter.report(messageFfiNativeMustBeExternal, node.fileOffset,
+ 1, node.location?.file);
+ return node;
+ }
+ node.isExternal = false;
+
+ node.annotations.remove(ffiNativeAnnotation);
+
if (!node.isStatic) {
- diagnosticReporter.report(messageFfiNativeAnnotationMustAnnotateStatic,
- node.fileOffset, 1, node.location!.file);
+ return _transformInstanceMethod(
+ node,
+ ffiNativeAnnotation.constant as InstanceConstant,
+ ffiNativeAnnotation.fileOffset);
}
- node.isExternal = false;
- node.function.body = transformFfiNative(node, ffiNativeAnnotation)
- ..parent = node.function;
-
- return node;
+ return _transformStaticFunction(
+ node,
+ ffiNativeAnnotation.constant as InstanceConstant,
+ ffiNativeAnnotation.fileOffset);
}
}
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 2dc593a..eb17eb0 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -131,20 +131,6 @@
return result;
}
- InstanceConstant? _tryGetFfiNativeAnnotation(Member node) {
- for (final Expression annotation in node.annotations) {
- if (annotation is ConstantExpression) {
- if (annotation.constant is InstanceConstant) {
- final instConst = annotation.constant as InstanceConstant;
- if (instConst.classNode == ffiNativeClass) {
- return instConst;
- }
- }
- }
- }
- return null;
- }
-
@override
visitStaticInvocation(StaticInvocation node) {
super.visitStaticInvocation(node);
@@ -366,88 +352,6 @@
.substituteType(allocateFunctionType
.withoutTypeParameters) as FunctionType);
}
- } else if (target is Procedure) {
- // FfiNative calls that pass objects extending NativeFieldWrapperClass1
- // (NFWC1) should be passed as Pointer instead so we don't have the
- // overhead of converting Handles.
- // If we find an NFWC1 object being passed to an FfiNative signature
- // taking a Pointer, we automatically wrap the argument in a call to
- // `Pointer.fromAddress(_getNativeField(obj))`.
- // Example:
- // passAsPointer(ClassWithNativeField());
- // Becomes, roughly:
- // #t0 = PointerClassWithNativeField();
- // passAsPointer(Pointer.fromAddress(_getNativeField(#t0)));
- // reachabilityFence(#t0);
- final ffiNativeAnn = _tryGetFfiNativeAnnotation(target);
- if (ffiNativeAnn != null) {
- final DartType ffiSignature = ffiNativeAnn.typeArguments[0];
- if (ffiSignature is FunctionType) {
- final List<DartType> ffiParams = ffiSignature.positionalParameters;
- final List<VariableDeclaration> dartParams =
- target.function.positionalParameters;
-
- List<VariableDeclaration> tmpsArgs = [];
- List<Expression> callArgs = [];
- final origArgs = node.arguments.positional;
- for (int i = 0; i < origArgs.length; i++) {
- if (env.isSubtypeOf(
- dartParams[i].type,
- nativeFieldWrapperClassType,
- SubtypeCheckMode.ignoringNullabilities) &&
- env.isSubtypeOf(ffiParams[i], pointerType,
- SubtypeCheckMode.ignoringNullabilities)) {
- // final NativeFieldWrapperClass1 #t1 = MyNFWC1();.
- final tmpPtr = VariableDeclaration(null,
- initializer: origArgs[i],
- type: nativeFieldWrapperClassType,
- isFinal: true);
- tmpsArgs.add(tmpPtr);
-
- // Pointer.fromAddress(_getNativeField(#t1)).
- final ptr = StaticInvocation(
- fromAddressInternal,
- Arguments([
- StaticInvocation(getNativeFieldFunction,
- Arguments([VariableGet(tmpPtr)]))
- ], types: [
- voidType
- ]));
- callArgs.add(ptr);
-
- continue;
- }
- // Note: We also evaluate, and assign temporaries for, non-wrapped
- // arguments as we need to preserve the original evaluation order.
- final tmpArg = VariableDeclaration(null,
- initializer: origArgs[i], isFinal: true);
- tmpsArgs.add(tmpArg);
- callArgs.add(VariableGet(tmpArg));
- }
-
- final targetCall = StaticInvocation(target, Arguments(callArgs));
-
- // {
- // T #t0;
- // final NativeFieldWrapperClass1 #t1 = MyNFWC1();
- // #t0 = foo(Pointer.fromAddress(_getNativeField(#t1)));
- // reachabilityFence(#t1);
- // } => #t0
- final tmpResult =
- VariableDeclaration(null, type: target.function.returnType);
- return BlockExpression(
- Block([
- tmpResult,
- ...tmpsArgs,
- ExpressionStatement(VariableSet(tmpResult, targetCall)),
- for (final ta in tmpsArgs)
- ExpressionStatement(StaticInvocation(
- reachabilityFenceFunction, Arguments([VariableGet(ta)])))
- ]),
- VariableGet(tmpResult),
- );
- }
- }
}
} on _FfiStaticTypeError {
// It's OK to swallow the exception because the diagnostics issued will
@@ -809,22 +713,6 @@
return pointerType is InterfaceType ? pointerType.typeArguments[0] : null;
}
- // Replaces all NativeFieldWrapperClass1 parameters with Pointer.
- FunctionType _pointerizeFunctionType(FunctionType dartType) {
- List<DartType> parameters = [];
- for (final parameter in dartType.positionalParameters) {
- if (parameter is InterfaceType) {
- if (env.isSubtypeOf(parameter, nativeFieldWrapperClassType,
- SubtypeCheckMode.ignoringNullabilities)) {
- parameters.add(pointerType);
- continue;
- }
- }
- parameters.add(parameter);
- }
- return FunctionType(parameters, dartType.returnType, dartType.nullability);
- }
-
void _ensureNativeTypeToDartType(
DartType nativeType, DartType dartType, Expression node,
{bool allowHandle: false}) {
@@ -837,15 +725,6 @@
SubtypeCheckMode.ignoringNullabilities)) {
return;
}
- // We do automatic argument conversion from NativeFieldWrapperClass1 to
- // Pointer, so we specifically allow for NFWC1 to be passed as Pointer.
- if (dartType is FunctionType) {
- final ptrDartType = _pointerizeFunctionType(dartType);
- if (env.isSubtypeOf(correspondingDartType, ptrDartType,
- SubtypeCheckMode.ignoringNullabilities)) {
- return;
- }
- }
diagnosticReporter.report(
templateFfiTypeMismatch.withArguments(dartType, correspondingDartType,
nativeType, currentLibrary.isNonNullableByDefault),
diff --git a/pkg/vm/test/transformations/ffinative_test.dart b/pkg/vm/test/transformations/ffinative_test.dart
index 76eba47..dfcb6f4 100644
--- a/pkg/vm/test/transformations/ffinative_test.dart
+++ b/pkg/vm/test/transformations/ffinative_test.dart
@@ -5,8 +5,9 @@
import 'dart:io';
import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/core_types.dart';
import 'package:kernel/kernel.dart';
-import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart';
@@ -30,11 +31,15 @@
Component component = await compileTestCaseToKernelProgram(source,
target: target, experimentalFlags: ['generic-metadata']);
- final ReferenceFromIndex? referenceFromIndex = null;
- final DiagnosticReporter diagnosticReporter = TestDiagnosticReporter();
+ final coreTypes = CoreTypes(component);
transformLibraries(
- component, component.libraries, diagnosticReporter, referenceFromIndex);
+ component,
+ coreTypes,
+ ClassHierarchy(component, coreTypes),
+ component.libraries,
+ TestDiagnosticReporter(),
+ /*referenceFromIndex=*/ null);
verifyComponent(component);
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart b/pkg/vm/testcases/transformations/ffi/ffinative.dart
index 5cb2397..c611214 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart
@@ -20,8 +20,30 @@
external static int returnIntPtrStatic(int x);
}
+class NativeClassy extends NativeFieldWrapperClass1 {
+ @FfiNative<Void Function(Pointer<Void>, IntPtr)>('doesntmatter')
+ external void goodHasReceiverPointer(int v);
+
+ @FfiNative<Void Function(Handle, IntPtr)>('doesntmatter')
+ external void goodHasReceiverHandle(int v);
+
+ @FfiNative<Void Function(Handle, Pointer<Void>)>('doesntmatter')
+ external void goodHasReceiverHandleAndPtr(NativeClassy v);
+
+ @FfiNative<Void Function(Handle, Handle)>('doesntmatter')
+ external void goodHasReceiverHandleAndHandle(NativeClassy v);
+
+ @FfiNative<Void Function(Pointer<Void>, Handle)>('doesntmatter')
+ external void goodHasReceiverPtrAndHandle(NativeClassy v);
+}
+
void main() {
returnIntPtr(13);
returnIntPtrLeaf(37);
Classy.returnIntPtrStatic(0xDE);
+ NativeClassy().goodHasReceiverPointer(0xAF);
+ NativeClassy().goodHasReceiverHandle(0xAF);
+ NativeClassy().goodHasReceiverHandleAndPtr(NativeClassy());
+ NativeClassy().goodHasReceiverHandleAndHandle(NativeClassy());
+ NativeClassy().goodHasReceiverPtrAndHandle(NativeClassy());
}
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
index 09cb43b..eec7f88 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
@@ -2,54 +2,85 @@
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
+import "dart:nativewrappers" as nat;
import "dart:_internal" as _in;
import "dart:ffi";
import "dart:nativewrappers";
class Classy extends core::Object {
- static final field (core::int) → core::int _@FfiNative_returnIntPtrStatic = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ static final field (core::int) → core::int _returnIntPtrStatic$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
synthetic constructor •() → self::Classy
: super core::Object::•()
;
- @#C5
static method returnIntPtrStatic(core::int x) → core::int
- return self::Classy::_@FfiNative_returnIntPtrStatic(x){(core::int) → core::int};
+ return self::Classy::_returnIntPtrStatic$FfiNative$Ptr(x){(core::int) → core::int};
}
-static final field (core::int) → core::int _@FfiNative_returnIntPtr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
-static final field (core::int) → core::int _@FfiNative_returnIntPtrLeaf = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
-@#C5
+class NativeClassy extends nat::NativeFieldWrapperClass1 {
+ static final field (ffi::Pointer<ffi::Void>, core::int) → void _goodHasReceiverPointer$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::int) → void, (ffi::Pointer<ffi::Void*>*, ffi::IntPtr*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::IntPtr*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ static final field (self::NativeClassy, core::int) → void _goodHasReceiverHandle$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, core::int) → void, (ffi::Handle*, ffi::IntPtr*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::IntPtr*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle*, ffi::Pointer<ffi::Void*>*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::Pointer<ffi::Void*>*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle*, ffi::Handle*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::Handle*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void*>*, ffi::Handle*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::Handle*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+ synthetic constructor •() → self::NativeClassy
+ : super nat::NativeFieldWrapperClass1::•()
+ ;
+ method goodHasReceiverPointer(core::int v) → void
+ return self::NativeClassy::_goodHasReceiverPointer$FfiNative(this, v);
+ method goodHasReceiverHandle(core::int v) → void
+ return self::NativeClassy::_goodHasReceiverHandle$FfiNative(this, v);
+ method goodHasReceiverHandleAndPtr(self::NativeClassy v) → void
+ return self::NativeClassy::_goodHasReceiverHandleAndPtr$FfiNative(this, v);
+ method goodHasReceiverHandleAndHandle(self::NativeClassy v) → void
+ return self::NativeClassy::_goodHasReceiverHandleAndHandle$FfiNative(this, v);
+ method goodHasReceiverPtrAndHandle(self::NativeClassy v) → void
+ return self::NativeClassy::_goodHasReceiverPtrAndHandle$FfiNative(this, v);
+ static method /*isLegacy*/ _goodHasReceiverPointer$FfiNative(self::NativeClassy self, core::int v) → dynamic
+ return block {
+ final nat::NativeFieldWrapperClass1 #t1 = self;
+ final core::int #t2 = v;
+ final void #t3 = self::NativeClassy::_goodHasReceiverPointer$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t1)), #t2){(self::NativeClassy, core::int) → void};
+ _in::reachabilityFence(#t1);
+ } =>#t3;
+ static method /*isLegacy*/ _goodHasReceiverHandle$FfiNative(self::NativeClassy self, core::int v) → dynamic
+ return self::NativeClassy::_goodHasReceiverHandle$FfiNative$Ptr(self, v){(self::NativeClassy, core::int) → void};
+ static method /*isLegacy*/ _goodHasReceiverHandleAndPtr$FfiNative(self::NativeClassy self, self::NativeClassy v) → dynamic
+ return block {
+ final self::NativeClassy #t4 = self;
+ final nat::NativeFieldWrapperClass1 #t5 = v;
+ final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5))){(self::NativeClassy, self::NativeClassy) → void};
+ _in::reachabilityFence(#t5);
+ } =>#t6;
+ static method /*isLegacy*/ _goodHasReceiverHandleAndHandle$FfiNative(self::NativeClassy self, self::NativeClassy v) → dynamic
+ return self::NativeClassy::_goodHasReceiverHandleAndHandle$FfiNative$Ptr(self, v){(self::NativeClassy, self::NativeClassy) → void};
+ static method /*isLegacy*/ _goodHasReceiverPtrAndHandle$FfiNative(self::NativeClassy self, self::NativeClassy v) → dynamic
+ return block {
+ final nat::NativeFieldWrapperClass1 #t7 = self;
+ final self::NativeClassy #t8 = v;
+ final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t7)), #t8){(self::NativeClassy, self::NativeClassy) → void};
+ _in::reachabilityFence(#t7);
+ } =>#t9;
+}
+static final field (core::int) → core::int _returnIntPtr$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+static final field (core::int) → core::int _returnIntPtrLeaf$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
static method returnIntPtr(core::int x) → core::int
- return self::_@FfiNative_returnIntPtr(x){(core::int) → core::int};
-@#C7
+ return self::_returnIntPtr$FfiNative$Ptr(x){(core::int) → core::int};
static method returnIntPtrLeaf(core::int x) → core::int
- return self::_@FfiNative_returnIntPtrLeaf(x){(core::int) → core::int};
+ return self::_returnIntPtrLeaf$FfiNative$Ptr(x){(core::int) → core::int};
static method main() → void {
- block {
- core::int #t1;
- final dynamic #t2 = 13;
- #t1 = self::returnIntPtr(#t2);
- _in::reachabilityFence(#t2);
- } =>#t1;
- block {
- core::int #t3;
- final dynamic #t4 = 37;
- #t3 = self::returnIntPtrLeaf(#t4);
- _in::reachabilityFence(#t4);
- } =>#t3;
- block {
- core::int #t5;
- final dynamic #t6 = 222;
- #t5 = self::Classy::returnIntPtrStatic(#t6);
- _in::reachabilityFence(#t6);
- } =>#t5;
+ self::returnIntPtr(13);
+ self::returnIntPtrLeaf(37);
+ self::Classy::returnIntPtrStatic(222);
+ new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPointer}(175){(core::int) → void};
+ new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandle}(175){(core::int) → void};
+ new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndPtr}(new self::NativeClassy::•()){(self::NativeClassy) → void};
+ new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndHandle}(new self::NativeClassy::•()){(self::NativeClassy) → void};
+ new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPtrAndHandle}(new self::NativeClassy::•()){(self::NativeClassy) → void};
}
constants {
#C1 = "#lib"
#C2 = "ReturnIntPtr"
#C3 = 1
- #C4 = false
- #C5 = ffi::FfiNative<(ffi::IntPtr*) →* ffi::IntPtr*> {nativeName:#C2, isLeaf:#C4}
- #C6 = true
- #C7 = ffi::FfiNative<(ffi::IntPtr*) →* ffi::IntPtr*> {nativeName:#C2, isLeaf:#C6}
+ #C4 = "doesntmatter"
+ #C5 = 2
}
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index a705710..cf10a4e 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -1133,6 +1133,44 @@
DummyResourceFinalizer);
}
+intptr_t AddPtrAndInt(void* self, intptr_t x) {
+ return reinterpret_cast<intptr_t>(self) + x;
+}
+
+intptr_t AddHandleFieldAndInt(Dart_Handle self, intptr_t x) {
+ intptr_t field = 0;
+ ENSURE(!Dart_IsError(Dart_GetNativeInstanceField(self, 0, &field)));
+ return field + x;
+}
+
+intptr_t AddPtrAndPtr(void* self, void* other) {
+ return reinterpret_cast<intptr_t>(self) + reinterpret_cast<intptr_t>(other);
+}
+
+intptr_t AddHandleFieldAndPtr(Dart_Handle self, void* other) {
+ intptr_t field = 0;
+ ENSURE(!Dart_IsError(Dart_GetNativeInstanceField(self, 0, &field)));
+ return field + reinterpret_cast<intptr_t>(other);
+}
+
+intptr_t AddHandleFieldAndHandleField(Dart_Handle self, Dart_Handle other) {
+ intptr_t field1 = 0;
+ ENSURE(!Dart_IsError(Dart_GetNativeInstanceField(self, 0, &field1)));
+ intptr_t field2 = 0;
+ ENSURE(!Dart_IsError(Dart_GetNativeInstanceField(other, 0, &field2)));
+ return field1 + field2;
+}
+
+intptr_t AddPtrAndHandleField(void* self, Dart_Handle other) {
+ intptr_t field = 0;
+ ENSURE(!Dart_IsError(Dart_GetNativeInstanceField(other, 0, &field)));
+ return reinterpret_cast<intptr_t>(self) + field;
+}
+
+intptr_t ReturnIntPtrMethod(Dart_Handle self, intptr_t value) {
+ return value;
+}
+
static void* FfiNativeResolver(const char* name, uintptr_t args_n) {
if (strcmp(name, "Dart_SetNativeInstanceField") == 0 && args_n == 3) {
return reinterpret_cast<void*>(Dart_SetNativeInstanceField);
@@ -1167,6 +1205,27 @@
if (strcmp(name, "SetResourceFinalizer") == 0 && args_n == 2) {
return reinterpret_cast<void*>(SetResourceFinalizer);
}
+ if (strcmp(name, "AddPtrAndInt") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(AddPtrAndInt);
+ }
+ if (strcmp(name, "AddHandleFieldAndInt") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(AddHandleFieldAndInt);
+ }
+ if (strcmp(name, "AddPtrAndPtr") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(AddPtrAndPtr);
+ }
+ if (strcmp(name, "AddHandleFieldAndPtr") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(AddHandleFieldAndPtr);
+ }
+ if (strcmp(name, "AddHandleFieldAndHandleField") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(AddHandleFieldAndHandleField);
+ }
+ if (strcmp(name, "AddPtrAndHandleField") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(AddPtrAndHandleField);
+ }
+ if (strcmp(name, "ReturnIntPtrMethod") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(ReturnIntPtrMethod);
+ }
// This should be unreachable in tests.
ENSURE(false);
}
diff --git a/tests/co19_2/co19_2-co19.status b/tests/co19_2/co19_2-co19.status
index f8609dc..fd3ae01e 100644
--- a/tests/co19_2/co19_2-co19.status
+++ b/tests/co19_2/co19_2-co19.status
@@ -8,6 +8,128 @@
Language/Generics/typedef_A08_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
Language/Generics/typedef_A08_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
Language/Generics/typedef_A08_t04: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/class/static/class_typedef_l1_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/class/static/class_typedef_l2_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/class/static/class_typedef_l2_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l1_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l1_t04: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l1_t05: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l1_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l1_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l1_t10: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t01: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t04: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t06: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t10: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t11: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t12: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t13: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t14: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t16: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t18: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_01_t20: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t01: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t06: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t10: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t11: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t14: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t16: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t18: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_02_t20: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t04: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t06: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t12: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t13: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t14: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t16: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t18: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_03_t20: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t01: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t04: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t06: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t10: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t11: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t12: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t13: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t14: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t16: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t18: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_04_t20: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t01: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t04: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t06: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t10: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t11: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t12: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t13: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t14: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t16: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t18: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_05_t20: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t01: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t06: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t08: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t10: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t11: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t12: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t13: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t14: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t16: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t18: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_l2_06_t20: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t03: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t07: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t09: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t11: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t13: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t15: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t17: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
+LanguageFeatures/Instantiate-to-bound/typedef/static/typedef_typedef_l1_t19: SkipByDesign # https://github.com/dart-lang/sdk/issues/46483
LibTest/io/RawDatagramSocket/*: Skip # https://github.com/dart-lang/co19/issues/195
[ $compiler == dart2js || $compiler == dartdevk ]
diff --git a/tests/ffi/ffi_native_test.dart b/tests/ffi/ffi_native_test.dart
index 074e236..2874007 100644
--- a/tests/ffi/ffi_native_test.dart
+++ b/tests/ffi/ffi_native_test.dart
@@ -6,6 +6,7 @@
// with type arguments isn't supported in that version of Dart.
import 'dart:ffi';
+import 'dart:nativewrappers';
// Error: FFI leaf call must not have Handle return type.
@FfiNative<Handle Function()>("foo", isLeaf: true) //# 01: compile-time error
@@ -20,11 +21,48 @@
@FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr')
external static int returnIntPtrStatic(int x);
- // Error: FfiNative annotations can only be used on static functions.
- @FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr') //# 03: compile-time error
- external int returnIntPtrMethod(int x); //# 03: compile-time error
+ // Error: Missing receiver in FfiNative annotation.
+ @FfiNative<Void Function(IntPtr)>('doesntmatter') //# 03: compile-time error
+ external void badMissingReceiver(int v); //# 03: compile-time error
+
+ // Error: Class doesn't extend NativeFieldWrapperClass1 - can't be converted
+ // to Pointer.
+ @FfiNative<Void Function(Pointer<Void>, IntPtr)>(//# 04: compile-time error
+ 'doesntmatter') //# 04: compile-time error
+ external void badHasReceiverPointer(int v); //# 04: compile-time error
+
+ @FfiNative<Void Function(Handle, IntPtr)>('doesntmatter')
+ external void goodHasReceiverHandle(int v);
}
+class NativeClassy extends NativeFieldWrapperClass1 {
+ @FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr')
+ external static int returnIntPtrStatic(int x);
+
+ // Error: Missing receiver in FfiNative annotation.
+ @FfiNative<Void Function(IntPtr)>('doesntmatter') //# 05: compile-time error
+ external void badMissingReceiver(int v); //# 05: compile-time error
+
+ @FfiNative<Void Function(Pointer<Void>, IntPtr)>('doesntmatter')
+ external void goodHasReceiverPointer(int v);
+
+ @FfiNative<Void Function(Handle, IntPtr)>('doesntmatter')
+ external void goodHasReceiverHandle(int v);
+}
+
+// Error: Too many FfiNative parameters.
+@FfiNative<Handle Function(IntPtr, IntPtr)>(//# 06: compile-time error
+ 'doesntmatter') //# 06: compile-time error
+external Object badTooManyFfiParameter(int v); //# 06: compile-time error
+
+// Error: Too few FfiNative parameters.
+@FfiNative<Handle Function(IntPtr)>('doesntmatter') //# 07: compile-time error
+external Object badTooFewFfiParameter(int v, int v2); //# 07: compile-time error
+
+// Error: FfiNatives must be marked external (and by extension have no body).
+@FfiNative<Void Function()>('doesntmatter') //# 08: compile-time error
+void mustBeMarkedExternal() {} //# 08: compile-time error
+
// Regression test: Ensure same-name FfiNative functions don't collide in the
// top-level namespace, but instead live under their parent (Library, Class).
class A {
@@ -37,4 +75,24 @@
external static void foo();
}
+class DoesNotExtend implements NativeFieldWrapperClass1 {
+ // Error: Receiver type can't be converted to Pointer since it doesn't extend
+ // NativeFieldWrapperClass1.
+ @FfiNative<IntPtr Function(Pointer<Void>, Handle)>(//# 09: compile-time error
+ 'doesntmatter') //# 09: compile-time error
+ external int bad1(DoesNotExtend obj); //# 09: compile-time error
+
+ // Error: Parameter type can't be converted to Pointer since it doesn't extend
+ // NativeFieldWrapperClass1.
+ @FfiNative<IntPtr Function(Handle, Pointer<Void>)>(//# 10: compile-time error
+ 'doesntmatter') //# 10: compile-time error
+ external int bad2(DoesNotExtend obj); //# 10: compile-time error
+
+ // Error: Parameter type can't be converted to Pointer since it doesn't extend
+ // NativeFieldWrapperClass1.
+ @FfiNative<IntPtr Function(Pointer<Void>)>(//# 11: compile-time error
+ 'doesntmatter') //# 11: compile-time error
+ external static int bad3(DoesNotExtend obj); //# 11: compile-time error
+}
+
void main() {/* Intentionally empty: Compile-time error tests. */}
diff --git a/tests/ffi/vmspecific_ffi_native_test.dart b/tests/ffi/vmspecific_ffi_native_test.dart
index 9caa53d..dac63ef 100644
--- a/tests/ffi/vmspecific_ffi_native_test.dart
+++ b/tests/ffi/vmspecific_ffi_native_test.dart
@@ -52,6 +52,36 @@
ClassWithNativeField(int value) {
setNativeInstanceField(this, 0, value);
}
+
+ // Instance methods implicitly pass a 'self' reference as the first argument.
+ // Passed as Pointer if the native function takes that (and the class can be
+ // converted).
+ @FfiNative<IntPtr Function(Pointer<Void>, IntPtr)>('AddPtrAndInt')
+ external int addSelfPtrAndIntMethod(int x);
+
+ // Instance methods implicitly pass a 'self' reference as the first argument.
+ // Passed as Handle if the native function takes that.
+ @FfiNative<IntPtr Function(Handle, IntPtr)>('AddHandleFieldAndInt')
+ external int addSelfHandleFieldAndIntMethod(int x);
+
+ @FfiNative<IntPtr Function(Pointer<Void>, Pointer<Void>)>('AddPtrAndPtr')
+ external int addSelfPtrAndPtrMethod(ClassWithNativeField other);
+
+ @FfiNative<IntPtr Function(Handle, Pointer<Void>)>('AddHandleFieldAndPtr')
+ external int addSelfHandleFieldAndPtrMethod(ClassWithNativeField other);
+
+ @FfiNative<IntPtr Function(Handle, Handle)>('AddHandleFieldAndHandleField')
+ external int addSelfHandleFieldAndHandleFieldMethod(
+ ClassWithNativeField other);
+
+ @FfiNative<IntPtr Function(Pointer<Void>, Handle)>('AddPtrAndHandleField')
+ external int addselfPtrAndHandleFieldMethod(ClassWithNativeField other);
+}
+
+class ClassWithoutNativeField {
+ // Instance methods implicitly pass their handle as the first arg.
+ @FfiNative<IntPtr Function(Handle, IntPtr)>('ReturnIntPtrMethod')
+ external int returnIntPtrMethod(int x);
}
// Native function takes a Handle, so a Handle is passed as-is.
@@ -111,4 +141,26 @@
state = 0;
passAsValueAndPointer(setState(7), StateSetter(3));
Expect.equals(3, state);
+
+ // Test transforms of instance methods.
+ Expect.equals(234, ClassWithoutNativeField().returnIntPtrMethod(234));
+ Expect.equals(1012, ClassWithNativeField(12).addSelfPtrAndIntMethod(1000));
+ Expect.equals(
+ 2021, ClassWithNativeField(21).addSelfHandleFieldAndIntMethod(2000));
+ Expect.equals(
+ 3031,
+ ClassWithNativeField(31)
+ .addSelfPtrAndPtrMethod(ClassWithNativeField(3000)));
+ Expect.equals(
+ 4041,
+ ClassWithNativeField(41)
+ .addSelfHandleFieldAndPtrMethod(ClassWithNativeField(4000)));
+ Expect.equals(
+ 5051,
+ ClassWithNativeField(51)
+ .addSelfHandleFieldAndHandleFieldMethod(ClassWithNativeField(5000)));
+ Expect.equals(
+ 6061,
+ ClassWithNativeField(61)
+ .addselfPtrAndHandleFieldMethod(ClassWithNativeField(6000)));
}
diff --git a/tools/VERSION b/tools/VERSION
index 2a7370b..d089db4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 210
+PRERELEASE 211
PRERELEASE_PATCH 0
\ No newline at end of file