Version 2.13.0-121.0.dev
Merge commit 'bcf06bad4a1d925cc329bb94f1fbaff358a9207b' into 'dev'
diff --git a/DEPS b/DEPS
index f3aec31..8c55dd0 100644
--- a/DEPS
+++ b/DEPS
@@ -39,7 +39,7 @@
# Checked-in SDK version. The checked-in SDK is a Dart SDK distribution in a
# cipd package used to run Dart scripts in the build and test infrastructure.
- "sdk_tag": "version:2.12.0-259.8.beta",
+ "sdk_tag": "version:2.12.0",
# co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
# hashes. It requires access to the dart-build-access group, which EngProd
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 766b57a..fe7cfc1 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -4065,7 +4065,7 @@
Variable? exceptionVariable, Variable? stackTraceVariable) {
_TryContext<Variable, Type> context =
_stack.last as _TryContext<Variable, Type>;
- _current = context._beforeCatch;
+ _current = context._beforeCatch!;
if (exceptionVariable != null) {
_current = _current.declare(exceptionVariable, true);
}
@@ -4086,7 +4086,7 @@
void tryCatchStatement_end() {
_TryContext<Variable, Type> context =
_stack.removeLast() as _TryContext<Variable, Type>;
- _current = context._afterBodyAndCatches.unsplit();
+ _current = context._afterBodyAndCatches!.unsplit();
}
@override
@@ -4101,7 +4101,7 @@
'No assigned variables info should have been stored for $finallyBlock');
_TryFinallyContext<Variable, Type> context =
_stack.removeLast() as _TryFinallyContext<Variable, Type>;
- _current = context._afterBodyAndCatches
+ _current = context._afterBodyAndCatches!
.attachFinally(typeOperations, context._beforeFinally, _current);
}
@@ -5051,14 +5051,14 @@
extends _SimpleContext<Variable, Type> {
/// If the statement is a "try/catch" statement, the flow model representing
/// program state at the top of any `catch` block.
- late final FlowModel<Variable, Type> _beforeCatch;
+ FlowModel<Variable, Type>? _beforeCatch;
/// If the statement is a "try/catch" statement, the accumulated flow model
/// representing program state after the `try` block or one of the `catch`
/// blocks has finished executing. If the statement is a "try/finally"
/// statement, the flow model representing program state after the `try` block
/// has finished executing.
- late FlowModel<Variable, Type> _afterBodyAndCatches;
+ FlowModel<Variable, Type>? _afterBodyAndCatches;
_TryContext(FlowModel<Variable, Type> previous) : super(previous);
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 d77d6dc..f57c05b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -3926,6 +3926,32 @@
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String
+ name)> templateFfiSizeAnnotationDimensions = const Template<
+ Message Function(String name)>(
+ messageTemplate:
+ r"""Field '#name' must have an 'Array' annotation that matches the dimensions.""",
+ withArguments: _withArgumentsFfiSizeAnnotationDimensions);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeFfiSizeAnnotationDimensions =
+ const Code<Message Function(String name)>(
+ "FfiSizeAnnotationDimensions",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiSizeAnnotationDimensions(String name) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ return new Message(codeFfiSizeAnnotationDimensions,
+ message:
+ """Field '${name}' must have an 'Array' annotation that matches the dimensions.""",
+ arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)> templateFfiStructGeneric =
const Template<Message Function(String name)>(
messageTemplate: r"""Struct '#name' should not be generic.""",
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 04edcc1..fd9f767 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -476,6 +476,7 @@
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
FfiCode.NON_SIZED_TYPE_ARGUMENT,
+ FfiCode.SIZE_ANNOTATION_DIMENSIONS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_WITH,
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
index 83f1a49..08f603f 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
@@ -207,6 +207,15 @@
"or subtype of 'Struct'.");
/**
+ * No parameters.
+ */
+ static const FfiCode SIZE_ANNOTATION_DIMENSIONS = FfiCode(
+ name: 'SIZE_ANNOTATION_DIMENSIONS',
+ message:
+ "'Array's must have an 'Array' annotation that matches the dimensions.",
+ correction: "Try adjusting the arguments in the 'Array' annotation.");
+
+ /**
* Parameters:
* 0: the name of the subclass
* 1: the name of the class being extended, implemented, or mixed in
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 7ab1196..99f293a 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -18,7 +18,7 @@
static const _allocatorClassName = 'Allocator';
static const _allocateExtensionMethodName = 'call';
static const _allocatorExtensionName = 'AllocatorAlloc';
- static const _cArrayClassName = 'Array';
+ static const _arrayClassName = 'Array';
static const _dartFfiLibraryName = 'dart.ffi';
static const _opaqueClassName = 'Opaque';
@@ -248,6 +248,9 @@
if (nativeType.isPointer) {
return true;
}
+ if (nativeType.isArray) {
+ return true;
+ }
return false;
}
@@ -554,9 +557,10 @@
final typeArg = (declaredType as InterfaceType).typeArguments.single;
if (!_isSized(typeArg)) {
_errorReporter.reportErrorForNode(FfiCode.NON_SIZED_TYPE_ARGUMENT,
- fieldType, [_cArrayClassName, typeArg.toString()]);
+ fieldType, [_arrayClassName, typeArg.toString()]);
}
- _validateSizeOfAnnotation(fieldType, annotations);
+ final arrayDimensions = declaredType.arrayDimensions;
+ _validateSizeOfAnnotation(fieldType, annotations, arrayDimensions);
} else if (declaredType.isStructSubtype) {
final clazz = (declaredType as InterfaceType).element;
if (clazz.isEmptyStruct) {
@@ -708,8 +712,8 @@
/// Validate that the [annotations] include exactly one size annotation. If
/// an error is produced that cannot be associated with an annotation,
/// associate it with the [errorNode].
- void _validateSizeOfAnnotation(
- AstNode errorNode, NodeList<Annotation> annotations) {
+ void _validateSizeOfAnnotation(AstNode errorNode,
+ NodeList<Annotation> annotations, int arrayDimensions) {
final ffiSizeAnnotations = annotations.where((annotation) {
final element = annotation.element;
return element is ConstructorElement &&
@@ -720,7 +724,9 @@
if (ffiSizeAnnotations.isEmpty) {
_errorReporter.reportErrorForNode(
FfiCode.MISSING_SIZE_ANNOTATION_CARRAY, errorNode);
+ return;
}
+
if (ffiSizeAnnotations.length > 1) {
final extraAnnotations = ffiSizeAnnotations.skip(1);
for (final annotation in extraAnnotations) {
@@ -728,6 +734,24 @@
FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY, annotation);
}
}
+
+ // Check number of dimensions.
+ final annotation = ffiSizeAnnotations.first;
+ final expressions = annotation.arguments!.arguments;
+ int annotationDimensions = 0;
+ for (var expression in expressions) {
+ if (expression is IntegerLiteral) {
+ // Element of `@Array(1, 2, 3)`.
+ annotationDimensions++;
+ } else if (expression is ListLiteral) {
+ // Element of `@Array.multi([1, 2, 3])`.
+ annotationDimensions += expression.elements.length;
+ }
+ }
+ if (annotationDimensions != arrayDimensions) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.SIZE_ANNOTATION_DIMENSIONS, annotation);
+ }
}
/// Validate that the given [typeArgument] has a constant value. Return `true`
@@ -845,11 +869,23 @@
final self = this;
if (self is InterfaceType) {
final element = self.element;
- return element.name == FfiVerifier._cArrayClassName && element.isFfiClass;
+ return element.name == FfiVerifier._arrayClassName && element.isFfiClass;
}
return false;
}
+ int get arrayDimensions {
+ DartType iterator = this;
+ int dimensions = 0;
+ while (iterator is InterfaceType &&
+ iterator.element.name == FfiVerifier._arrayClassName &&
+ iterator.element.isFfiClass) {
+ dimensions++;
+ iterator = iterator.typeArguments.single;
+ }
+ return dimensions;
+ }
+
bool get isPointer {
final self = this;
return self is InterfaceType && self.element.isPointer;
diff --git a/pkg/analyzer/lib/src/services/available_declarations.dart b/pkg/analyzer/lib/src/services/available_declarations.dart
index 07c970a..577424b 100644
--- a/pkg/analyzer/lib/src/services/available_declarations.dart
+++ b/pkg/analyzer/lib/src/services/available_declarations.dart
@@ -596,7 +596,7 @@
if (_scheduledFiles.isNotEmpty) {
var scheduledFile = _scheduledFiles.removeLast();
- var file = _getFileByPath(scheduledFile.context, scheduledFile.path)!;
+ var file = _getFileByPath(scheduledFile.context, [], scheduledFile.path)!;
if (!file.isLibrary) return;
@@ -653,6 +653,16 @@
}
}
+ /// TODO(scheglov) Remove after fixing
+ /// https://github.com/dart-lang/sdk/issues/45233
+ void _addPathOrUri(List<String> pathOrUriList, String path, Uri uri) {
+ pathOrUriList.add('(uri: $uri, path: $path)');
+
+ if (pathOrUriList.length > 200) {
+ throw StateError('Suspected cycle. $pathOrUriList');
+ }
+ }
+
/// Compute exported declarations for the given [libraries].
void _computeExportedDeclarations(Set<_File> libraries) {
var walker = _LibraryWalker();
@@ -686,7 +696,8 @@
return null;
}
- _File? _getFileByPath(DeclarationsContext context, String path) {
+ _File? _getFileByPath(
+ DeclarationsContext context, List<String> partOrUriList, String path) {
var file = _pathToFile[path];
if (file == null) {
var uri = context._restoreUri(path);
@@ -694,13 +705,16 @@
file = _File(this, path, uri);
_pathToFile[path] = file;
_uriToFile[uri] = file;
- file.refresh(context);
+ _addPathOrUri(partOrUriList, path, uri);
+ file.refresh(context, partOrUriList);
+ partOrUriList.removeLast();
}
}
return file;
}
- _File? _getFileByUri(DeclarationsContext context, Uri uri) {
+ _File? _getFileByUri(
+ DeclarationsContext context, List<String> partOrUriList, Uri uri) {
var file = _uriToFile[uri];
if (file != null) {
return file;
@@ -726,7 +740,9 @@
_pathToFile[path] = file;
_uriToFile[uri] = file;
- file.refresh(context);
+ _addPathOrUri(partOrUriList, path, uri);
+ file.refresh(context, partOrUriList);
+ partOrUriList.removeLast();
return file;
}
@@ -745,13 +761,13 @@
var containingContext = _findContextOfPath(path);
if (containingContext == null) return;
- var file = _getFileByPath(containingContext, path);
+ var file = _getFileByPath(containingContext, [], path);
if (file == null) return;
var wasLibrary = file.isLibrary;
var oldLibrary = wasLibrary ? file : file.library;
- file.refresh(containingContext);
+ file.refresh(containingContext, []);
var isLibrary = file.isLibrary;
var newLibrary = isLibrary ? file : file.library;
@@ -763,17 +779,17 @@
} else {
notLibraries.add(file);
if (newLibrary != null) {
- newLibrary.refresh(containingContext);
+ newLibrary.refresh(containingContext, []);
_invalidateExportedDeclarations(invalidatedLibraries, newLibrary);
}
}
} else {
if (oldLibrary != null) {
- oldLibrary.refresh(containingContext);
+ oldLibrary.refresh(containingContext, []);
_invalidateExportedDeclarations(invalidatedLibraries, oldLibrary);
}
if (newLibrary != null && newLibrary != oldLibrary) {
- newLibrary.refresh(containingContext);
+ newLibrary.refresh(containingContext, []);
_invalidateExportedDeclarations(invalidatedLibraries, newLibrary);
}
}
@@ -1164,7 +1180,7 @@
String get uriStr => uri.toString();
- void refresh(DeclarationsContext context) {
+ void refresh(DeclarationsContext context, List<String> partOrUriList) {
var resource = tracker._resourceProvider.getFile(path);
int modificationStamp;
@@ -1225,10 +1241,10 @@
// Resolve exports and parts.
for (var export in exports) {
- export.file = _fileForRelativeUri(context, export.uri);
+ export.file = _fileForRelativeUri(context, partOrUriList, export.uri);
}
for (var part in parts) {
- part.file = _fileForRelativeUri(context, part.uri);
+ part.file = _fileForRelativeUri(context, partOrUriList, part.uri);
}
exports.removeWhere((e) => e.file == null);
parts.removeWhere((e) => e.file == null);
@@ -1753,9 +1769,13 @@
}
/// Return the [_File] for the given [relative] URI, maybe `null`.
- _File? _fileForRelativeUri(DeclarationsContext context, Uri relative) {
+ _File? _fileForRelativeUri(
+ DeclarationsContext context,
+ List<String> partOrUriList,
+ Uri relative,
+ ) {
var absoluteUri = resolveRelativeUri(uri, relative);
- return tracker._getFileByUri(context, absoluteUri);
+ return tracker._getFileByUri(context, partOrUriList, absoluteUri);
}
void _putFileDeclarationsToByteStore(String contentKey) {
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index 04e8cc2..f43dcc2 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -691,7 +691,10 @@
}
class Array<T extends NativeType> extends NativeType {
- external const factory Array(int dimension1);
+ external const factory Array(int dimension1,
+ [int dimension2, int dimension3, int dimension4, int dimension5]);
+
+ external const factory Array.multi(List<int> dimensions);
}
extension StructPointer<T extends Struct> on Pointer<T> {
diff --git a/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart b/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart
index 85b1793..ec3f26d 100644
--- a/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/non_sized_type_argument_test.dart
@@ -32,10 +32,10 @@
class C extends Struct {
@Array(8)
- Array<Array<Uint8>> a0;
+ Array<Void> a0;
}
''', [
- error(FfiCode.NON_SIZED_TYPE_ARGUMENT, 59, 19),
+ error(FfiCode.NON_SIZED_TYPE_ARGUMENT, 59, 11),
]);
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/size_annotation_dimensions_test.dart b/pkg/analyzer/test/src/diagnostics/size_annotation_dimensions_test.dart
new file mode 100644
index 0000000..8a53708
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/size_annotation_dimensions_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SizeAnnotationDimensions);
+ });
+}
+
+@reflectiveTest
+class SizeAnnotationDimensions extends PubPackageResolutionTest {
+ test_error_array_2_3() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @Array(8, 8)
+ Array<Array<Array<Uint8>>> a0;
+}
+''', [
+ error(FfiCode.SIZE_ANNOTATION_DIMENSIONS, 47, 12),
+ ]);
+ }
+
+ test_error_array_3_2() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @Array(8, 8, 8)
+ Array<Array<Uint8>> a0;
+}
+''', [
+ error(FfiCode.SIZE_ANNOTATION_DIMENSIONS, 47, 15),
+ ]);
+ }
+
+ test_error_multi_2_3() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @Array.multi([8, 8])
+ Array<Array<Array<Uint8>>> a0;
+}
+''', [
+ error(FfiCode.SIZE_ANNOTATION_DIMENSIONS, 47, 20),
+ ]);
+ }
+
+ test_no_error() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ @Array(8, 8)
+ Array<Array<Uint8>> a0;
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 11cd8e8..3916b75 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -571,6 +571,7 @@
import 'set_element_type_not_assignable_test.dart'
as set_element_type_not_assignable;
import 'shared_deferred_prefix_test.dart' as shared_deferred_prefix;
+import 'size_annotation_dimensions_test.dart' as size_annotation_dimensions;
import 'spread_expression_from_deferred_library_test.dart'
as spread_expression_from_deferred_library;
import 'static_access_to_instance_member_test.dart'
@@ -1044,6 +1045,7 @@
sdk_version_ui_as_code_in_const_context.main();
set_element_type_not_assignable.main();
shared_deferred_prefix.main();
+ size_annotation_dimensions.main();
spread_expression_from_deferred_library.main();
static_access_to_instance_member.main();
strict_raw_type.main();
diff --git a/pkg/compiler/lib/src/constants/constant_system.dart b/pkg/compiler/lib/src/constants/constant_system.dart
index 11ac356..b10c628 100644
--- a/pkg/compiler/lib/src/constants/constant_system.dart
+++ b/pkg/compiler/lib/src/constants/constant_system.dart
@@ -36,6 +36,7 @@
const remainder = const RemainderOperation();
const shiftLeft = const ShiftLeftOperation();
const shiftRight = const ShiftRightOperation();
+const shiftRightUnsigned = const ShiftRightUnsignedOperation();
const subtract = const SubtractOperation();
const truncatingDivide = const TruncatingDivideOperation();
const codeUnitAt = const CodeUnitAtOperation();
@@ -472,6 +473,24 @@
apply(left, right) => left >> right;
}
+class ShiftRightUnsignedOperation extends BinaryBitOperation {
+ @override
+ final String name = '>>>';
+
+ const ShiftRightUnsignedOperation();
+
+ @override
+ BigInt foldInts(BigInt left, BigInt right) {
+ if (right < BigInt.zero) return null;
+ return left.toUnsigned(32) >> right.toInt();
+ }
+
+ @override
+ apply(left, right) {
+ throw UnimplementedError('ShiftRightUnsignedOperation.apply');
+ }
+}
+
abstract class BinaryBoolOperation implements BinaryOperation {
const BinaryBoolOperation();
diff --git a/pkg/compiler/lib/src/elements/operators.dart b/pkg/compiler/lib/src/elements/operators.dart
index 4f3e95c..182511b 100644
--- a/pkg/compiler/lib/src/elements/operators.dart
+++ b/pkg/compiler/lib/src/elements/operators.dart
@@ -152,7 +152,7 @@
/// The binary >>> operator.
static const BinaryOperator SHRU =
- const BinaryOperator._(BinaryOperatorKind.SHRU, '>>');
+ const BinaryOperator._(BinaryOperatorKind.SHRU, '>>>');
/// The binary >= operator.
static const BinaryOperator GTEQ =
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index f791743..02b8bab 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -1307,6 +1307,13 @@
}
return null;
+ case '>>>':
+ if (isEmpty(argument)) return tryLater();
+ if (isUInt31(receiver)) {
+ return inferrer.types.uint31Type;
+ }
+ return null;
+
case '&':
if (isEmpty(argument)) return tryLater();
if (isUInt31(receiver) || isUInt31(argument)) {
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 61d417fb..87e47b5 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -67,8 +67,9 @@
if (name == '/') return const DivideSpecializer();
if (name == '~/') return const TruncatingDivideSpecializer();
if (name == '%') return const ModuloSpecializer();
- if (name == '>>') return const ShiftRightSpecializer();
if (name == '<<') return const ShiftLeftSpecializer();
+ if (name == '>>') return const ShiftRightSpecializer();
+ if (name == '>>>') return const ShiftRightUnsignedSpecializer();
if (name == '&') return const BitAndSpecializer();
if (name == '|') return const BitOrSpecializer();
if (name == '^') return const BitXorSpecializer();
@@ -1045,6 +1046,71 @@
}
}
+class ShiftRightUnsignedSpecializer extends BinaryBitOpSpecializer {
+ const ShiftRightUnsignedSpecializer();
+
+ @override
+ AbstractValue computeTypeFromInputTypes(HInvokeDynamic instruction,
+ GlobalTypeInferenceResults results, JClosedWorld closedWorld) {
+ HInstruction left = instruction.inputs[1];
+ if (left.isUInt32(closedWorld.abstractValueDomain).isDefinitelyTrue) {
+ return left.instructionType;
+ }
+ return super.computeTypeFromInputTypes(instruction, results, closedWorld);
+ }
+
+ @override
+ HInstruction tryConvertToBuiltin(
+ HInvokeDynamic instruction,
+ HGraph graph,
+ GlobalTypeInferenceResults results,
+ JCommonElements commonElements,
+ JClosedWorld closedWorld,
+ OptimizationTestLog log) {
+ HInstruction left = instruction.inputs[1];
+ HInstruction right = instruction.inputs[2];
+ if (left.isInteger(closedWorld.abstractValueDomain).isDefinitelyTrue) {
+ if (argumentLessThan32(right)) {
+ HInstruction converted =
+ newBuiltinVariant(instruction, results, closedWorld);
+ if (log != null) {
+ registerOptimization(log, instruction, converted);
+ }
+ return converted;
+ }
+ // Even if there is no builtin equivalent instruction, we know
+ // the instruction does not have any side effect, and that it
+ // can be GVN'ed.
+ clearAllSideEffects(instruction);
+ if (isPositive(right, closedWorld)) {
+ redirectSelector(instruction, '_shruOtherPositive', commonElements);
+ if (log != null) {
+ registerOptimization(log, instruction, null);
+ }
+ }
+ }
+ return null;
+ }
+
+ @override
+ HInstruction newBuiltinVariant(HInvokeDynamic instruction,
+ GlobalTypeInferenceResults results, JClosedWorld closedWorld) {
+ return HShiftRight(instruction.inputs[1], instruction.inputs[2],
+ computeTypeFromInputTypes(instruction, results, closedWorld));
+ }
+
+ @override
+ constant_system.BinaryOperation operation() {
+ return constant_system.shiftRightUnsigned;
+ }
+
+ @override
+ void registerOptimization(
+ OptimizationTestLog log, HInstruction original, HInstruction converted) {
+ log.registerShiftRightUnsigned(original, converted);
+ }
+}
+
class BitOrSpecializer extends BinaryBitOpSpecializer {
const BitOrSpecializer();
diff --git a/pkg/compiler/lib/src/ssa/logging.dart b/pkg/compiler/lib/src/ssa/logging.dart
index 4eff5a9..e2c63af 100644
--- a/pkg/compiler/lib/src/ssa/logging.dart
+++ b/pkg/compiler/lib/src/ssa/logging.dart
@@ -179,6 +179,12 @@
'ShiftRight.${original.selector.name}');
}
+ void registerShiftRightUnsigned(
+ HInvokeDynamic original, HShiftRight converted) {
+ _registerSpecializer(original, converted, 'ShiftRightUnsigned',
+ 'ShiftRightUnsigned.${original.selector.name}');
+ }
+
void registerBitOr(HInvokeDynamic original, HBitOr converted) {
_registerSpecializer(original, converted, 'BitOr');
}
diff --git a/pkg/compiler/test/codegen/codegen_test_helper.dart b/pkg/compiler/test/codegen/codegen_test_helper.dart
index f28f670..e4f9c73 100644
--- a/pkg/compiler/test/codegen/codegen_test_helper.dart
+++ b/pkg/compiler/test/codegen/codegen_test_helper.dart
@@ -33,7 +33,7 @@
shards: 2,
directory: 'data',
skip: skip,
- options: [Flags.soundNullSafety]);
+ options: [Flags.soundNullSafety, '--enable-experiment=triple-shift']);
}
runTests2(List<String> args, [int shardIndex]) {
diff --git a/pkg/compiler/test/codegen/data/shift_right_unsigned.dart b/pkg/compiler/test/codegen/data/shift_right_unsigned.dart
new file mode 100644
index 0000000..755d743
--- /dev/null
+++ b/pkg/compiler/test/codegen/data/shift_right_unsigned.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+//@dart=2.12
+
+/*member: main:ignore*/
+void main() {
+ for (var a in [false, true]) {
+ sink = cannotRecognize(a ? 10 : C());
+ sink = unspecialized(a ? -1 : 1);
+ sink = otherPositive2(a);
+ sink = shiftBySix(a);
+ sink = shiftByMasked(a, 9);
+ sink = shiftByMasked(a, -9);
+ }
+
+ sink = cannotConstantFold();
+ sink = constantFoldPositive();
+ sink = constantFoldNegative();
+ test6();
+}
+
+Object? sink;
+
+@pragma('dart2js:noInline')
+/*spec.member: cannotRecognize:function(thing) {
+ return H._asInt(J.$shru$n(thing, 1));
+}*/
+/*prod.member: cannotRecognize:function(thing) {
+ return J.$shru$n(thing, 1);
+}*/
+int cannotRecognize(dynamic thing) {
+ return thing >>> 1;
+}
+
+@pragma('dart2js:noInline')
+/*member: cannotConstantFold:function() {
+ return C.JSInt_methods.$shru(1, -1);
+}*/
+int cannotConstantFold() {
+ var a = 1;
+ return a >>> -1;
+}
+
+@pragma('dart2js:noInline')
+/*member: constantFoldPositive:function() {
+ return 25;
+}*/
+int constantFoldPositive() {
+ var a = 100;
+ return a >>> 2;
+}
+
+@pragma('dart2js:noInline')
+/*member: constantFoldNegative:function() {
+ return 3;
+}*/
+int constantFoldNegative() {
+ var a = -1;
+ return a >>> 30;
+}
+
+@pragma('dart2js:noInline')
+/*member: unspecialized:function(a) {
+ return C.JSInt_methods.$shru(1, a);
+}*/
+int unspecialized(int a) {
+ return 1 >>> a;
+}
+
+@pragma('dart2js:noInline')
+/*member: otherPositive2:function(param) {
+ return C.JSInt_methods._shruOtherPositive$1(1, param ? 1 : 2);
+}*/
+int otherPositive2(bool param) {
+ var a = param ? 1 : 2;
+ return 1 >>> a;
+}
+
+@pragma('dart2js:noInline')
+/*member: shiftBySix:function(param) {
+ return (param ? 4294967295 : -1) >>> 6;
+}*/
+int shiftBySix(bool param) {
+ var a = param ? 0xFFFFFFFF : -1;
+ return a >>> 6;
+}
+
+@pragma('dart2js:noInline')
+/*member: shiftByMasked:function(param1, shift) {
+ var a = param1 ? 4294967295 : 0;
+ return a >>> (shift & 31);
+}*/
+int shiftByMasked(bool param1, int shift) {
+ var a = param1 ? 0xFFFFFFFF : 0;
+ return a >>> (shift & 31);
+}
+
+@pragma('dart2js:noInline')
+/*member: otherPositive6:function(a, b) {
+ return C.JSInt_methods._shruOtherPositive$1(a, b);
+}*/
+int otherPositive6(int a, int b) {
+ return a >>> b;
+}
+
+void test6() {
+ sink = otherPositive6(1, 3);
+ sink = otherPositive6(0, 4);
+ sink = otherPositive6(-1, 2);
+}
+
+class C {
+ /*member: C.>>>:ignore*/
+ C operator >>>(int i) => this;
+}
diff --git a/pkg/compiler/test/codegen/shift_right_unsigned_test.dart b/pkg/compiler/test/codegen/shift_right_unsigned_test.dart
new file mode 100644
index 0000000..786bc71
--- /dev/null
+++ b/pkg/compiler/test/codegen/shift_right_unsigned_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.11
+
+library shru_test;
+
+import 'dart:async';
+import 'package:async_helper/async_helper.dart';
+import '../helpers/compiler_helper.dart';
+
+const COMMON = r"""
+int g1 = 0, g2 = 0;
+int sink1 = 0, sink2 = 0;
+
+main() {
+ for (int i = 0; i < 0x100000000; i = i + (i >> 4) + 1) {
+ g1 = g2 = i;
+ sink1 = callFoo(i, 1 - i, i);
+ sink2 = callFoo(2 - i, i, 3 - i);
+ }
+}
+""";
+
+const tests = [
+ r"""
+// constant-fold positive
+int foo(int param) {
+ int a = 100;
+ int b = 2;
+ return a >>> b;
+ // present: 'return 25;'
+}
+""",
+
+ r"""
+// constant-fold negative
+int foo(int param) {
+ int a = -1;
+ int b = 30;
+ return a >>> b;
+ // present: 'return 3;'
+}
+ """,
+
+ r"""
+// base case
+int foo(int value, int shift) {
+ return value >>> shift;
+ // Default code pattern:
+ // present: 'return C.JSInt_methods.$shru(value, shift);'
+}
+int callFoo(int a, int b, int c) => foo(a, b);
+""",
+
+ r"""
+// shift by zero
+int foo(int param) {
+ return param >>> 0;
+ // present: 'return param >>> 0;'
+}
+ """,
+
+ r"""
+// shift by one
+int foo(int param) {
+ return param >>> 1;
+ // present: 'return param >>> 1;'
+}
+""",
+
+ r"""
+// shift masked into safe range
+int foo(int value, int shift) {
+ return value >>> (shift & 31);
+ // present: 'return value >>> (shift & 31);'
+}
+int callFoo(int a, int b, int c) => foo(a, b);
+""",
+
+ r"""
+// idempotent shift by zero
+int foo(int param) {
+ return param >>> 0 >>> 0 >>> 0;
+ // present: 'return param >>> 0;'
+}
+""",
+
+ r"""
+// idempotent shift by zero #2
+int foo(int param) {
+ return (param & 15) >>> 0;
+ // present: 'return param & 15;'
+}
+ """,
+
+// TODO(sra): shift-shift reduction.
+// r"""
+//// shift-shift-reduction
+//int foo(int param) {
+// return param >>> 1 >>> 2;
+// // present: 'return param >>> 3'
+//}
+//""",
+
+ r"""
+// mask-shift to shift-mask reduction
+int foo(int param) {
+ return (param & 0xF0) >>> 4;
+ // present: 'return param >>> 4 & 15'
+}
+""",
+
+ r"""
+// mask-shift to shift-mask reduction enabling mask reduction
+int foo(int param) {
+ return (param & 0x7FFFFFFF) >>> 31;
+ // present: 'return 0;'
+}
+""",
+];
+
+main() {
+ runTests() async {
+ Future check(String test) {
+ String program = COMMON + '\n\n' + test;
+ if (!test.contains('callFoo')) {
+ program += 'int callFoo(int a, int b, int c) => foo(a);\n';
+ }
+ return compile(program,
+ entry: 'main',
+ methodName: 'foo',
+ disableTypeInference: false,
+ enableTripleShift: true,
+ check: checkerForAbsentPresent(test));
+ }
+
+ for (final test in tests) {
+ String name = 'unnamed';
+ if (test.startsWith('//')) {
+ final comment = test.split('\n').first.replaceFirst('//', '').trim();
+ if (comment.isNotEmpty) name = comment;
+ }
+ print('-- $name');
+ await check(test);
+ }
+ }
+
+ asyncTest(() async {
+ print('--test from kernel------------------------------------------------');
+ await runTests();
+ });
+}
diff --git a/pkg/compiler/test/helpers/compiler_helper.dart b/pkg/compiler/test/helpers/compiler_helper.dart
index a2ebf41..9b5006b 100644
--- a/pkg/compiler/test/helpers/compiler_helper.dart
+++ b/pkg/compiler/test/helpers/compiler_helper.dart
@@ -46,6 +46,7 @@
bool disableInlining: true,
bool disableTypeInference: true,
bool omitImplicitChecks: true,
+ bool enableTripleShift: false, // TODO(30890): remove.
bool enableVariance: false,
void check(String generatedEntry),
bool returnAll: false,
@@ -67,6 +68,9 @@
if (disableInlining) {
options.add(Flags.disableInlining);
}
+ if (enableTripleShift) {
+ options.add('${Flags.enableLanguageExperiments}=triple-shift');
+ }
if (enableVariance) {
options.add('${Flags.enableLanguageExperiments}=variance');
}
diff --git a/pkg/compiler/test/inference/data/shift_right_unsigned.dart b/pkg/compiler/test/inference/data/shift_right_unsigned.dart
new file mode 100644
index 0000000..5faf990
--- /dev/null
+++ b/pkg/compiler/test/inference/data/shift_right_unsigned.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.12
+
+/*member: main:[null]*/
+main() {
+ g1 = -1;
+ g1 = 2;
+ test1();
+ test2();
+ test3();
+ test4();
+}
+
+/*member: g1:[subclass=JSInt]*/
+int g1 = 0;
+
+/*member: test1:[exact=JSUInt31]*/
+test1() {
+ int a = 1234;
+ int b = 2;
+ return a /*invoke: [exact=JSUInt31]*/>>> b;
+}
+
+/*member: test2:[subclass=JSUInt32]*/
+test2() {
+ return g1 /*invoke: [subclass=JSInt]*/>>> g1;
+}
+
+/*member: test3:[subclass=JSUInt32]*/
+test3() {
+ return g1 /*invoke: [subclass=JSInt]*/>>> 1;
+}
+
+/*member: test4:[exact=JSUInt31]*/
+test4() {
+ return 10 /*invoke: [exact=JSUInt31]*/>>> g1;
+}
diff --git a/pkg/compiler/test/inference/inference_test_helper.dart b/pkg/compiler/test/inference/inference_test_helper.dart
index d049559..701d33d 100644
--- a/pkg/compiler/test/inference/inference_test_helper.dart
+++ b/pkg/compiler/test/inference/inference_test_helper.dart
@@ -32,7 +32,7 @@
await checkTests(dataDir, const TypeMaskDataComputer(),
forUserLibrariesOnly: true,
args: args,
- options: [stopAfterTypeInference],
+ options: [stopAfterTypeInference, '--enable-experiment=triple-shift'],
testedConfigs: allInternalConfigs,
skip: skip,
shardIndex: shardIndex ?? 0,
diff --git a/pkg/compiler/test/optimization/data/shift_right_unsigned.dart b/pkg/compiler/test/optimization/data/shift_right_unsigned.dart
new file mode 100644
index 0000000..6a8aaf3
--- /dev/null
+++ b/pkg/compiler/test/optimization/data/shift_right_unsigned.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+main() {
+ shru1(1, 1);
+ shru1(2, 2);
+ shru1(-1, -1);
+
+ shruOtherInferredPositive(1, 1);
+ shruOtherInferredPositive(99, 99);
+ shruOtherInferredPositive(-1, 2);
+
+ shruSix(1);
+ shruSix(-1);
+
+ shruMaskedCount(1, 1);
+ shruMaskedCount(999, 999);
+ shruMaskedCount(-1, -2);
+
+ shruMaskedCount2(1, 1);
+ shruMaskedCount2(999, 999);
+ shruMaskedCount2(-1, -2);
+}
+
+@pragma('dart2js:noInline')
+shru1(a, b) {
+ return a >>> b;
+}
+
+@pragma('dart2js:noInline')
+/*member: shruOtherInferredPositive:Specializer=[ShiftRightUnsigned._shruOtherPositive]*/
+shruOtherInferredPositive(a, b) {
+ return a >>> b;
+}
+
+@pragma('dart2js:noInline')
+/*member: shruSix:Specializer=[ShiftRightUnsigned]*/
+shruSix(int a) {
+ return a >>> 6;
+}
+
+@pragma('dart2js:noInline')
+/*member: shruMaskedCount:Specializer=[BitAnd,ShiftRightUnsigned]*/
+shruMaskedCount(int a, int b) {
+ return a >>> (b & 31);
+}
+
+@pragma('dart2js:noInline')
+/*member: shruMaskedCount2:Specializer=[BitAnd,ShiftRightUnsigned._shruOtherPositive]*/
+shruMaskedCount2(int a, int b) {
+ return a >>> (b & 127);
+}
diff --git a/pkg/compiler/test/optimization/optimization_test.dart b/pkg/compiler/test/optimization/optimization_test.dart
index b5f2987..baf38d0 100644
--- a/pkg/compiler/test/optimization/optimization_test.dart
+++ b/pkg/compiler/test/optimization/optimization_test.dart
@@ -27,7 +27,8 @@
Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
bool strict = args.contains('-s');
await checkTests(dataDir, new OptimizationDataComputer(strict: strict),
- options: [Flags.disableInlining], args: args);
+ options: [Flags.disableInlining, '--enable-experiment=triple-shift'],
+ args: args);
});
}
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 5fe07cd..80da869 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -65,6 +65,7 @@
templateFfiFieldNull,
templateFfiNotStatic,
templateFfiSizeAnnotation,
+ templateFfiSizeAnnotationDimensions,
templateFfiStructGeneric,
templateFfiTypeInvalid,
templateFfiTypeMismatch;
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
index 127d61d..bd8a55f 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
@@ -4732,6 +4732,43 @@
const Template<
Message Function(
String name, DartType _type, bool isNonNullableByDefault)>
+ templateUndefinedExtensionGetter = const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>(
+ messageTemplate:
+ r"""The getter '#name' isn't defined for the extension '#type'.""",
+ tipTemplate:
+ r"""Try correcting the name to the name of an existing getter, or defining a getter or field named '#name'.""",
+ withArguments: _withArgumentsUndefinedExtensionGetter);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+ Message Function(String name, DartType _type,
+ bool isNonNullableByDefault)> codeUndefinedExtensionGetter = const Code<
+ Message Function(String name, DartType _type, bool isNonNullableByDefault)>(
+ "UndefinedExtensionGetter",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsUndefinedExtensionGetter(
+ String name, DartType _type, bool isNonNullableByDefault) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+ List<Object> typeParts = labeler.labelType(_type);
+ String type = typeParts.join();
+ return new Message(codeUndefinedExtensionGetter,
+ message:
+ """The getter '${name}' isn't defined for the extension '${type}'.""" +
+ labeler.originMessages,
+ tip: """Try correcting the name to the name of an existing getter, or defining a getter or field named '${name}'.""",
+ arguments: {'name': name, 'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>
templateUndefinedExtensionMethod = const Template<
Message Function(
String name, DartType _type, bool isNonNullableByDefault)>(
@@ -4767,6 +4804,82 @@
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>
+ templateUndefinedExtensionOperator = const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>(
+ messageTemplate:
+ r"""The operator '#name' isn't defined for the extension '#type'.""",
+ tipTemplate:
+ r"""Try correcting the operator to an existing operator, or defining a '#name' operator.""",
+ withArguments: _withArgumentsUndefinedExtensionOperator);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>
+ codeUndefinedExtensionOperator = const Code<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>(
+ "UndefinedExtensionOperator",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsUndefinedExtensionOperator(
+ String name, DartType _type, bool isNonNullableByDefault) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+ List<Object> typeParts = labeler.labelType(_type);
+ String type = typeParts.join();
+ return new Message(codeUndefinedExtensionOperator,
+ message:
+ """The operator '${name}' isn't defined for the extension '${type}'.""" +
+ labeler.originMessages,
+ tip: """Try correcting the operator to an existing operator, or defining a '${name}' operator.""",
+ arguments: {'name': name, 'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>
+ templateUndefinedExtensionSetter = const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>(
+ messageTemplate:
+ r"""The setter '#name' isn't defined for the extension '#type'.""",
+ tipTemplate:
+ r"""Try correcting the name to the name of an existing setter, or defining a setter or field named '#name'.""",
+ withArguments: _withArgumentsUndefinedExtensionSetter);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+ Message Function(String name, DartType _type,
+ bool isNonNullableByDefault)> codeUndefinedExtensionSetter = const Code<
+ Message Function(String name, DartType _type, bool isNonNullableByDefault)>(
+ "UndefinedExtensionSetter",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsUndefinedExtensionSetter(
+ String name, DartType _type, bool isNonNullableByDefault) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+ List<Object> typeParts = labeler.labelType(_type);
+ String type = typeParts.join();
+ return new Message(codeUndefinedExtensionSetter,
+ message:
+ """The setter '${name}' isn't defined for the extension '${type}'.""" +
+ labeler.originMessages,
+ tip: """Try correcting the name to the name of an existing setter, or defining a setter or field named '${name}'.""",
+ arguments: {'name': name, 'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
Message Function(
String name,
DartType _type,
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 7c9aad9..e414321 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -809,7 +809,7 @@
}
}
-class ConstantEvaluator extends ExpressionVisitor<Constant> {
+class ConstantEvaluator implements ExpressionVisitor<Constant> {
final ConstantsBackend backend;
final NumberSemantics numberSemantics;
ConstantIntFolder intFolder;
@@ -3198,6 +3198,60 @@
node = node.parent;
}
}
+
+ @override
+ Constant defaultBasicLiteral(BasicLiteral node) => defaultExpression(node);
+
+ @override
+ Constant visitAwaitExpression(AwaitExpression node) =>
+ defaultExpression(node);
+
+ @override
+ Constant visitBlockExpression(BlockExpression node) =>
+ defaultExpression(node);
+
+ @override
+ Constant visitDynamicSet(DynamicSet node) => defaultExpression(node);
+
+ @override
+ Constant visitInstanceGetterInvocation(InstanceGetterInvocation node) =>
+ defaultExpression(node);
+
+ @override
+ Constant visitInstanceSet(InstanceSet node) => defaultExpression(node);
+
+ @override
+ Constant visitLoadLibrary(LoadLibrary node) => defaultExpression(node);
+
+ @override
+ Constant visitPropertySet(PropertySet node) => defaultExpression(node);
+
+ @override
+ Constant visitRethrow(Rethrow node) => defaultExpression(node);
+
+ @override
+ Constant visitStaticSet(StaticSet node) => defaultExpression(node);
+
+ @override
+ Constant visitSuperMethodInvocation(SuperMethodInvocation node) =>
+ defaultExpression(node);
+
+ @override
+ Constant visitSuperPropertyGet(SuperPropertyGet node) =>
+ defaultExpression(node);
+
+ @override
+ Constant visitSuperPropertySet(SuperPropertySet node) =>
+ defaultExpression(node);
+
+ @override
+ Constant visitThisExpression(ThisExpression node) => defaultExpression(node);
+
+ @override
+ Constant visitThrow(Throw node) => defaultExpression(node);
+
+ @override
+ Constant visitVariableSet(VariableSet node) => defaultExpression(node);
}
class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
diff --git a/pkg/front_end/lib/src/fasta/problems.dart b/pkg/front_end/lib/src/fasta/problems.dart
index db9552f..cd06b1f 100644
--- a/pkg/front_end/lib/src/fasta/problems.dart
+++ b/pkg/front_end/lib/src/fasta/problems.dart
@@ -45,9 +45,10 @@
///
/// Before printing the message, the string `"Internal error: "` is prepended.
dynamic internalProblem(Message message, int charOffset, Uri uri) {
- throw CompilerContext.current.format(
- message.withLocation(uri, charOffset, noLength),
- Severity.internalProblem);
+ throw CompilerContext.current
+ .format(message.withLocation(uri, charOffset, noLength),
+ Severity.internalProblem)
+ .plain;
}
dynamic unimplemented(String what, int charOffset, Uri uri) {
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index d01bde9..be88ff0 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -883,30 +883,46 @@
ObjectAccessTarget _findDirectExtensionMember(
ExtensionType receiverType, Name name, int fileOffset,
{ObjectAccessTarget defaultTarget}) {
- Member targetMethod;
+ Member targetMember;
Member targetTearoff;
+ ProcedureKind targetKind;
for (ExtensionMemberDescriptor descriptor
in receiverType.extensionNode.members) {
if (descriptor.name == name) {
switch (descriptor.kind) {
case ExtensionMemberKind.Method:
- targetMethod = descriptor.member.asMember;
+ targetMember = descriptor.member.asMember;
+ targetTearoff ??= targetMember;
+ targetKind = ProcedureKind.Method;
break;
case ExtensionMemberKind.TearOff:
targetTearoff = descriptor.member.asMember;
break;
+ case ExtensionMemberKind.Getter:
+ targetMember = descriptor.member.asMember;
+ targetTearoff = null;
+ targetKind = ProcedureKind.Getter;
+ break;
+ case ExtensionMemberKind.Setter:
+ targetMember = descriptor.member.asMember;
+ targetTearoff = null;
+ targetKind = ProcedureKind.Setter;
+ break;
+ case ExtensionMemberKind.Operator:
+ targetMember = descriptor.member.asMember;
+ targetTearoff = null;
+ targetKind = ProcedureKind.Operator;
+ break;
default:
- unhandled("${descriptor.kind}", "findInterfaceMember", fileOffset,
- library.fileUri);
+ unhandled("${descriptor.kind}", "_findDirectExtensionMember",
+ fileOffset, library.fileUri);
}
}
}
- if (targetMethod != null) {
+ if (targetMember != null) {
+ assert(targetKind != null);
return new ObjectAccessTarget.extensionMember(
- targetMethod,
- targetTearoff ?? targetMethod,
- ProcedureKind.Method,
- receiverType.typeArguments);
+ targetMember, targetTearoff, targetKind, receiverType.typeArguments);
} else {
return defaultTarget;
}
@@ -4134,13 +4150,19 @@
return engine.forest
.createPropertyGet(fileOffset, receiver, propertyName);
} else {
+ Template<Message Function(String, DartType, bool)> templateMissing;
+ if (receiverType is ExtensionType) {
+ templateMissing = templateUndefinedExtensionGetter;
+ } else {
+ templateMissing = templateUndefinedGetter;
+ }
return _reportMissingOrAmbiguousMember(
fileOffset,
propertyName.text.length,
receiverType,
propertyName,
extensionAccessCandidates,
- templateUndefinedGetter,
+ templateMissing,
templateAmbiguousExtensionProperty);
}
}
@@ -4155,13 +4177,19 @@
fileOffset, receiver, propertyName, value,
forEffect: forEffect);
} else {
+ Template<Message Function(String, DartType, bool)> templateMissing;
+ if (receiverType is ExtensionType) {
+ templateMissing = templateUndefinedExtensionSetter;
+ } else {
+ templateMissing = templateUndefinedSetter;
+ }
return _reportMissingOrAmbiguousMember(
fileOffset,
propertyName.text.length,
receiverType,
propertyName,
extensionAccessCandidates,
- templateUndefinedSetter,
+ templateMissing,
templateAmbiguousExtensionProperty);
}
}
@@ -4172,13 +4200,19 @@
if (isTopLevel) {
return engine.forest.createIndexGet(fileOffset, receiver, index);
} else {
+ Template<Message Function(String, DartType, bool)> templateMissing;
+ if (receiverType is ExtensionType) {
+ templateMissing = templateUndefinedExtensionOperator;
+ } else {
+ templateMissing = templateUndefinedOperator;
+ }
return _reportMissingOrAmbiguousMember(
fileOffset,
noLength,
receiverType,
indexGetName,
extensionAccessCandidates,
- templateUndefinedOperator,
+ templateMissing,
templateAmbiguousExtensionOperator);
}
}
@@ -4192,13 +4226,19 @@
return engine.forest.createIndexSet(fileOffset, receiver, index, value,
forEffect: forEffect);
} else {
+ Template<Message Function(String, DartType, bool)> templateMissing;
+ if (receiverType is ExtensionType) {
+ templateMissing = templateUndefinedExtensionOperator;
+ } else {
+ templateMissing = templateUndefinedOperator;
+ }
return _reportMissingOrAmbiguousMember(
fileOffset,
noLength,
receiverType,
indexSetName,
extensionAccessCandidates,
- templateUndefinedOperator,
+ templateMissing,
templateAmbiguousExtensionOperator);
}
}
@@ -4211,13 +4251,19 @@
return engine.forest.createMethodInvocation(fileOffset, left, binaryName,
engine.forest.createArguments(fileOffset, <Expression>[right]));
} else {
+ Template<Message Function(String, DartType, bool)> templateMissing;
+ if (leftType is ExtensionType) {
+ templateMissing = templateUndefinedExtensionOperator;
+ } else {
+ templateMissing = templateUndefinedOperator;
+ }
return _reportMissingOrAmbiguousMember(
fileOffset,
binaryName.text.length,
leftType,
binaryName,
extensionAccessCandidates,
- templateUndefinedOperator,
+ templateMissing,
templateAmbiguousExtensionOperator);
}
}
@@ -4229,13 +4275,19 @@
return new UnaryExpression(unaryName, expression)
..fileOffset = fileOffset;
} else {
+ Template<Message Function(String, DartType, bool)> templateMissing;
+ if (expressionType is ExtensionType) {
+ templateMissing = templateUndefinedExtensionOperator;
+ } else {
+ templateMissing = templateUndefinedOperator;
+ }
return _reportMissingOrAmbiguousMember(
fileOffset,
unaryName == unaryMinusName ? 1 : unaryName.text.length,
expressionType,
unaryName,
extensionAccessCandidates,
- templateUndefinedOperator,
+ templateMissing,
templateAmbiguousExtensionOperator);
}
}
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 9c120af..7d556ba 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -330,6 +330,7 @@
FfiFieldNull/analyzerCode: Fail
FfiNotStatic/analyzerCode: Fail
FfiSizeAnnotation/analyzerCode: Fail
+FfiSizeAnnotationDimensions/analyzerCode: Fail
FfiStructAnnotation/analyzerCode: Fail
FfiStructGeneric/analyzerCode: Fail
FfiTypeInvalid/analyzerCode: Fail
@@ -772,8 +773,14 @@
TypedefNullableType/analyzerCode: Fail
TypedefTypeVariableNotConstructor/analyzerCode: Fail # Feature not yet enabled by default.
TypedefTypeVariableNotConstructor/example: Fail # Feature not yet enabled by default.
+UndefinedExtensionGetter/analyzerCode: Fail
+UndefinedExtensionGetter/example: Fail
UndefinedExtensionMethod/analyzerCode: Fail
UndefinedExtensionMethod/example: Fail
+UndefinedExtensionOperator/analyzerCode: Fail
+UndefinedExtensionOperator/example: Fail
+UndefinedExtensionSetter/analyzerCode: Fail
+UndefinedExtensionSetter/example: Fail
UnexpectedToken/part_wrapped_script1: Fail
UnexpectedToken/script1: Fail
UnmatchedToken/part_wrapped_script1: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 36d6bcc..ec1663f 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3491,10 +3491,22 @@
c + 0;
}
+UndefinedExtensionGetter:
+ template: "The getter '#name' isn't defined for the extension '#type'."
+ tip: "Try correcting the name to the name of an existing getter, or defining a getter or field named '#name'."
+
+UndefinedExtensionSetter:
+ template: "The setter '#name' isn't defined for the extension '#type'."
+ tip: "Try correcting the name to the name of an existing setter, or defining a setter or field named '#name'."
+
UndefinedExtensionMethod:
template: "The method '#name' isn't defined for the extension '#type'."
tip: "Try correcting the name to the name of an existing method, or defining a method name '#name'."
+UndefinedExtensionOperator:
+ template: "The operator '#name' isn't defined for the extension '#type'."
+ tip: "Try correcting the operator to an existing operator, or defining a '#name' operator."
+
AmbiguousExtensionMethod:
template: "The method '#name' is defined in multiple extensions for '#type' and neither is more specific."
tip: "Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope."
@@ -4272,6 +4284,11 @@
template: "Field '#name' must have exactly one 'Array' annotation."
external: test/ffi_test.dart
+FfiSizeAnnotationDimensions:
+ # Used by dart:ffi
+ template: "Field '#name' must have an 'Array' annotation that matches the dimensions."
+ external: test/ffi_test.dart
+
FfiStructGeneric:
# Used by dart:ffi
template: "Struct '#name' should not be generic."
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 3b69a38..d4cc164 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -159,6 +159,8 @@
StdioProcess;
import 'package:vm/target/vm.dart' show VmTarget;
+import 'package:vm/transformations/type_flow/transformer.dart' as type_flow;
+import 'package:vm/transformations/pragma.dart' as type_flow;
import '../../utils/kernel_chain.dart'
show
@@ -312,10 +314,6 @@
final bool semiFuzz;
final bool verify;
final bool soundNullSafety;
- final Map<Component, KernelTarget> componentToTarget =
- <Component, KernelTarget>{};
- final Map<Component, List<Iterable<String>>> componentToDiagnostics =
- <Component, List<Iterable<String>>>{};
final Uri platformBinaries;
final Map<UriConfiguration, UriTranslator> _uriTranslators = {};
final Map<Uri, FolderOptions> _folderOptions = {};
@@ -393,6 +391,7 @@
}
if (fullCompile) {
steps.add(const Transform());
+ steps.add(const Verify(true));
steps.add(const StressConstantEvaluatorStep());
if (!ignoreExpectations) {
steps.add(new MatchExpectation("$fullPrefix.transformed.expect",
@@ -678,18 +677,27 @@
Expectation get verificationError => expectationSet["VerificationError"];
- Future<Component> loadPlatform(Target target, NnbdMode nnbdMode) async {
+ Uri _getPlatformUri(Target target, NnbdMode nnbdMode) {
String fileName = computePlatformDillName(
target,
nnbdMode,
() => throw new UnsupportedError(
"No platform dill for target '${target.name}' with $nnbdMode."));
- Uri uri = platformBinaries.resolve(fileName);
+ return platformBinaries.resolve(fileName);
+ }
+
+ Future<Component> loadPlatform(Target target, NnbdMode nnbdMode) async {
+ Uri uri = _getPlatformUri(target, nnbdMode);
return _platforms.putIfAbsent(uri, () {
return loadComponentFromBytes(new File.fromUri(uri).readAsBytesSync());
});
}
+ void clearPlatformCache(Target target, NnbdMode nnbdMode) async {
+ Uri uri = _getPlatformUri(target, nnbdMode);
+ _platforms.remove(uri);
+ }
+
@override
Result processTestResult(
TestDescription description, Result result, bool last) {
@@ -715,7 +723,9 @@
static Future<FastaContext> create(
Chain suite, Map<String, String> environment) async {
- Uri vm = Uri.base.resolveUri(new Uri.file(Platform.resolvedExecutable));
+ String resolvedExecutable = Platform.environment['resolvedExecutable'] ??
+ Platform.resolvedExecutable;
+ Uri vm = Uri.base.resolveUri(new Uri.file(resolvedExecutable));
Map<ExperimentalFlag, bool> experimentalFlags = <ExperimentalFlag, bool>{};
void addForcedExperimentalFlag(String name, ExperimentalFlag flag) {
@@ -812,11 +822,12 @@
}
return new Result<ComponentResult>(
result, runResult.outcome, runResult.error);
+ case "aot":
case "none":
case "noneWithJs":
case "dart2js":
case "dartdevc":
- // TODO(johnniwinther): Support running dart2js and/or dartdevc.
+ // TODO(johnniwinther): Support running vm aot, dart2js and/or dartdevc.
return pass(result);
default:
throw new ArgumentError(
@@ -1648,6 +1659,9 @@
case "vm":
target = new TestVmTarget(targetFlags);
break;
+ case "aot":
+ target = new TestVmAotTarget(targetFlags);
+ break;
case "none":
target = new NoneTarget(targetFlags);
break;
@@ -1781,10 +1795,6 @@
await instrumentation.loadExpectations(description.uri);
sourceTarget.loader.instrumentation = instrumentation;
Component p = await sourceTarget.buildOutlines();
- context.componentToTarget.clear();
- context.componentToTarget[p] = sourceTarget;
- context.componentToDiagnostics.clear();
- context.componentToDiagnostics[p] = compilationSetup.errors;
Set<Uri> userLibraries =
createUserLibrariesImportUriSet(p, sourceTarget.uriTranslator);
if (fullCompile) {
@@ -1799,7 +1809,7 @@
} else {
return new Result<ComponentResult>(
new ComponentResult(description, p, userLibraries,
- compilationSetup.options, sourceTarget),
+ compilationSetup, sourceTarget),
context.expectationSet["InstrumentationMismatch"],
instrumentation.problemsAsString,
autoFixCommand: '${UPDATE_COMMENTS}=true',
@@ -1807,8 +1817,8 @@
}
}
}
- return pass(new ComponentResult(description, p, userLibraries,
- compilationSetup.options, sourceTarget));
+ return pass(new ComponentResult(
+ description, p, userLibraries, compilationSetup, sourceTarget));
});
}
@@ -1850,8 +1860,7 @@
ComponentResult result, FastaContext context) async {
return await CompilerContext.runWithOptions(result.options, (_) async {
Component component = result.component;
- KernelTarget sourceTarget = context.componentToTarget[component];
- context.componentToTarget.remove(component);
+ KernelTarget sourceTarget = result.sourceTarget;
Target backendTarget = sourceTarget.backendTarget;
if (backendTarget is TestTarget) {
backendTarget.performModularTransformations = true;
@@ -1872,7 +1881,18 @@
context.expectationSet["TransformVerificationError"],
errors.join('\n'));
}
- return pass(result);
+ if (backendTarget is TestTarget &&
+ backendTarget.hasGlobalTransformation) {
+ component =
+ backendTarget.performGlobalTransformations(sourceTarget, component);
+ // Clear the currently cached platform since the global transformation
+ // might have modified it.
+ context.clearPlatformCache(
+ backendTarget, result.compilationSetup.options.nnbdMode);
+ }
+
+ return pass(new ComponentResult(result.description, component,
+ result.userLibraries, result.compilationSetup, sourceTarget));
});
}
}
@@ -1973,12 +1993,34 @@
logger: logger);
}
}
+
+ bool get hasGlobalTransformation => false;
+
+ Component performGlobalTransformations(
+ KernelTarget kernelTarget, Component component) =>
+ component;
}
class TestVmTarget extends VmTarget with TestTarget {
TestVmTarget(TargetFlags flags) : super(flags);
}
+class TestVmAotTarget extends TestVmTarget {
+ TestVmAotTarget(TargetFlags flags) : super(flags);
+
+ @override
+ bool get hasGlobalTransformation => true;
+
+ @override
+ Component performGlobalTransformations(
+ KernelTarget kernelTarget, Component component) {
+ return type_flow.transformComponent(
+ this, kernelTarget.loader.coreTypes, component,
+ matcher: new type_flow.ConstantPragmaAnnotationParser(
+ kernelTarget.loader.coreTypes));
+ }
+}
+
class EnsureNoErrors
extends Step<ComponentResult, ComponentResult, FastaContext> {
const EnsureNoErrors();
@@ -1987,8 +2029,7 @@
Future<Result<ComponentResult>> run(
ComponentResult result, FastaContext context) async {
- List<Iterable<String>> errors =
- context.componentToDiagnostics[result.component];
+ List<Iterable<String>> errors = result.compilationSetup.errors;
return errors.isEmpty
? pass(result)
: fail(
@@ -2009,7 +2050,7 @@
Component component = result.component;
Uri uri =
component.uriToSource.keys.firstWhere((uri) => uri?.scheme == "file");
- KernelTarget target = context.componentToTarget[component];
+ KernelTarget target = result.sourceTarget;
ClassHierarchyBuilder hierarchy = target.loader.builderHierarchy;
StringBuffer sb = new StringBuffer();
for (ClassHierarchyNode node in hierarchy.nodes.values) {
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 9a5012d..25e141b 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -857,6 +857,9 @@
digit
digits
dill
+dimension
+dimensional
+dimensions
dir
direct
direction
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 59a96f5..b3ade10 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -33,6 +33,7 @@
annoying
anon
aoo
+aot
approval
approximation
arbitrarily
@@ -98,6 +99,7 @@
c59cdee365b94ce066344840f9e3412d642019b
ca
cafebabe
+calloc
camel
capitalized
causal
diff --git a/pkg/front_end/test/utils/kernel_chain.dart b/pkg/front_end/test/utils/kernel_chain.dart
index 5afe941..f7b52fc 100644
--- a/pkg/front_end/test/utils/kernel_chain.dart
+++ b/pkg/front_end/test/utils/kernel_chain.dart
@@ -61,6 +61,8 @@
Step,
TestDescription;
+import '../fasta/testing/suite.dart' show CompilationSetup;
+
final Uri platformBinariesLocation = computePlatformBinariesLocation();
abstract class MatchContext implements ChainContext {
@@ -241,8 +243,7 @@
StringBuffer buffer = new StringBuffer();
- List<Iterable<String>> errors =
- (context as dynamic).componentToDiagnostics[component];
+ List<Iterable<String>> errors = result.compilationSetup.errors;
Set<String> reportedErrors = <String>{};
for (Iterable<String> message in errors) {
reportedErrors.add(message.join('\n'));
@@ -420,8 +421,13 @@
Uri uri = tmp.uri.resolve("generated.dill");
File generated = new File.fromUri(uri);
IOSink sink = generated.openWrite();
- result = new ComponentResult(result.description, result.component,
- result.userLibraries, result.options, result.sourceTarget, uri);
+ result = new ComponentResult(
+ result.description,
+ result.component,
+ result.userLibraries,
+ result.compilationSetup,
+ result.sourceTarget,
+ uri);
try {
new BinaryPrinter(sink).writeComponentFile(component);
} catch (e, s) {
@@ -517,12 +523,12 @@
final Component component;
final Set<Uri> userLibraries;
final Uri outputUri;
- final ProcessedOptions options;
+ final CompilationSetup compilationSetup;
final KernelTarget sourceTarget;
final List<String> extraConstantStrings = [];
ComponentResult(this.description, this.component, this.userLibraries,
- this.options, this.sourceTarget,
+ this.compilationSetup, this.sourceTarget,
[this.outputUri]);
bool isUserLibrary(Library library) {
@@ -532,4 +538,6 @@
bool isUserLibraryImportUri(Uri importUri) {
return userLibraries.contains(importUri);
}
+
+ ProcessedOptions get options => compilationSetup.options;
}
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart
new file mode 100644
index 0000000..d2ee09c
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'main_lib.dart';
+
+main() {
+ List list = [];
+ if (list.isNotEmpty) {
+ new Class().method(null as dynamic);
+ }
+}
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.expect
new file mode 100644
index 0000000..09282c5
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.expect
@@ -0,0 +1,49 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → dynamic {
+ core::List<dynamic> list = <dynamic>[];
+ if(list.{core::Iterable::isNotEmpty}) {
+ new mai::Class::•().{mai::Class::method}((null as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::Enum);
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class Enum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<mai::Enum> values = #C4;
+ static const field mai::Enum a = #C3;
+ const constructor •(core::int index, core::String _name) → mai::Enum
+ : mai::Enum::index = index, mai::Enum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{mai::Enum::_name};
+}
+class Class extends core::Object {
+ synthetic constructor •() → mai::Class
+ : super core::Object::•()
+ ;
+ method method(mai::Enum e) → core::int
+ return e.{mai::Enum::index};
+}
+
+constants {
+ #C1 = 0
+ #C2 = "Enum.a"
+ #C3 = mai::Enum {index:#C1, _name:#C2}
+ #C4 = <mai::Enum>[#C3]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Enum. (from org-dartlang-testcase:///main_lib.dart:5:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect
new file mode 100644
index 0000000..24d6d3b
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+import "dart:_internal" as _in;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → dynamic {
+ core::List<dynamic> list = core::_GrowableList::•<dynamic>(0);
+ if(list.{core::Iterable::isNotEmpty}) {
+ let dynamic #t1 = new mai::Class::•() in let dynamic #t2 = _in::unsafeCast<dynamic>(null) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::Enum in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+abstract class Enum extends core::Object {
+}
+class Class extends core::Object {
+ synthetic constructor •() → mai::Class
+ : super core::Object::•()
+ ;
+}
+
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.textual_outline.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.textual_outline.expect
new file mode 100644
index 0000000..82be6bb
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+import 'main_lib.dart';
+
+main() {}
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..82be6bb
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@
+import 'main_lib.dart';
+
+main() {}
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.expect
new file mode 100644
index 0000000..d3516e5
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.expect
@@ -0,0 +1,49 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → dynamic {
+ core::List<dynamic> list = <dynamic>[];
+ if(list.{core::Iterable::isNotEmpty}) {
+ new mai::Class::•().{mai::Class::method}((null as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::Enum);
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class Enum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<mai::Enum> values = #C4;
+ static const field mai::Enum a = #C3;
+ const constructor •(core::int index, core::String _name) → mai::Enum
+ : mai::Enum::index = index, mai::Enum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{mai::Enum::_name};
+}
+class Class extends core::Object {
+ synthetic constructor •() → mai::Class
+ : super core::Object::•()
+ ;
+ method method(mai::Enum e) → core::int
+ return e.{mai::Enum::index};
+}
+
+constants {
+ #C1 = 0
+ #C2 = "Enum.a"
+ #C3 = mai::Enum {index:#C1, _name:#C2}
+ #C4 = <mai::Enum*>[#C3]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Enum. (from org-dartlang-testcase:///main_lib.dart:5:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.outline.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.outline.expect
new file mode 100644
index 0000000..5574f01
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.outline.expect
@@ -0,0 +1,42 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → dynamic
+ ;
+
+library /*isNonNullableByDefault*/;
+import self as self2;
+import "dart:core" as core;
+
+class Enum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self2::Enum> values = #C4;
+ static const field self2::Enum a = #C3;
+ const constructor •(core::int index, core::String _name) → self2::Enum
+ : self2::Enum::index = index, self2::Enum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self2::Enum::_name};
+}
+class Class extends core::Object {
+ synthetic constructor •() → self2::Class
+ ;
+ method method(self2::Enum e) → core::int
+ ;
+}
+
+constants {
+ #C1 = 0
+ #C2 = "Enum.a"
+ #C3 = self2::Enum {index:#C1, _name:#C2}
+ #C4 = <self2::Enum*>[#C3]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Enum. (from org-dartlang-testcase:///main_lib.dart:5:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.transformed.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..040adbc
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.weak.transformed.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+import "dart:_internal" as _in;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → dynamic {
+ core::List<dynamic> list = core::_GrowableList::•<dynamic>(0);
+ if(list.{core::Iterable::isNotEmpty}) {
+ new mai::Class::•().{mai::Class::method}(_in::unsafeCast<mai::Enum>(_in::unsafeCast<dynamic>(null)));
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+abstract class Enum extends core::Object {
+ abstract get /*isLegacy*/ index() → core::int;
+}
+class Class extends core::Object {
+ synthetic constructor •() → mai::Class
+ : super core::Object::•()
+ ;
+ method method(mai::Enum e) → core::int
+ return e.{mai::Enum::index};
+}
+
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main_lib.dart b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main_lib.dart
new file mode 100644
index 0000000..e5600e4
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main_lib.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+enum Enum { a }
+
+class Class {
+ int method(Enum e) => e.index;
+}
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/test.options b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/test.options
new file mode 100644
index 0000000..bfe6dc8
--- /dev/null
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/test.options
@@ -0,0 +1 @@
+main_lib.dart
\ No newline at end of file
diff --git a/pkg/front_end/testcases/aot/folder.options b/pkg/front_end/testcases/aot/folder.options
new file mode 100644
index 0000000..a8a8650
--- /dev/null
+++ b/pkg/front_end/testcases/aot/folder.options
@@ -0,0 +1 @@
+--target=aot
\ No newline at end of file
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart b/pkg/front_end/testcases/aot/tree_shake/main.dart
new file mode 100644
index 0000000..8958414
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'main_lib.dart';
+
+enum UnusedEnum { a, b }
+enum UsedEnum {
+ unusedValue,
+ usedValue,
+}
+
+usedMethod(UnusedInterface c) {
+ c.usedInterfaceField = c.usedInterfaceField;
+}
+
+unusedMethod() {}
+
+class UnusedInterface {
+ int? usedInterfaceField;
+
+ UnusedInterface(this.usedInterfaceField);
+}
+
+class UsedClass implements UnusedInterface {
+ int? unusedField;
+ int? usedField;
+ int? usedInterfaceField;
+}
+
+class UnusedClass {}
+
+main() {
+ usedMethod(new UsedClass()..usedField);
+ UsedEnum.usedValue;
+ List<UnusedEnum> list = [];
+ if (list.isNotEmpty) {
+ new ConstClass().method(null as dynamic);
+ }
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.expect
new file mode 100644
index 0000000..57eb8a2
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.expect
@@ -0,0 +1,116 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class UnusedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::UnusedEnum> values = #C7;
+ static const field self::UnusedEnum a = #C3;
+ static const field self::UnusedEnum b = #C6;
+ const constructor •(core::int index, core::String _name) → self::UnusedEnum
+ : self::UnusedEnum::index = index, self::UnusedEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self::UnusedEnum::_name};
+}
+class UsedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::UsedEnum> values = #C12;
+ static const field self::UsedEnum unusedValue = #C9;
+ static const field self::UsedEnum usedValue = #C11;
+ const constructor •(core::int index, core::String _name) → self::UsedEnum
+ : self::UsedEnum::index = index, self::UsedEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self::UsedEnum::_name};
+}
+class UnusedInterface extends core::Object {
+ field core::int? usedInterfaceField;
+ constructor •(core::int? usedInterfaceField) → self::UnusedInterface
+ : self::UnusedInterface::usedInterfaceField = usedInterfaceField, super core::Object::•()
+ ;
+}
+class UsedClass extends core::Object implements self::UnusedInterface {
+ field core::int? unusedField = null;
+ field core::int? usedField = null;
+ field core::int? usedInterfaceField = null;
+ synthetic constructor •() → self::UsedClass
+ : super core::Object::•()
+ ;
+}
+class UnusedClass extends core::Object {
+ synthetic constructor •() → self::UnusedClass
+ : super core::Object::•()
+ ;
+}
+static method usedMethod(self::UnusedInterface c) → dynamic {
+ c.{self::UnusedInterface::usedInterfaceField} = c.{self::UnusedInterface::usedInterfaceField};
+}
+static method unusedMethod() → dynamic {}
+static method main() → dynamic {
+ self::usedMethod(let final self::UsedClass #t1 = new self::UsedClass::•() in block {
+ #t1.{self::UsedClass::usedField};
+ } =>#t1);
+ #C11;
+ core::List<self::UnusedEnum> list = <self::UnusedEnum>[];
+ if(list.{core::Iterable::isNotEmpty}) {
+ new mai::ConstClass::•().{mai::ConstClass::method}((null as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::ConstEnum);
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class ConstEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<mai::ConstEnum> values = #C15;
+ static const field mai::ConstEnum value = #C14;
+ const constructor •(core::int index, core::String _name) → mai::ConstEnum
+ : mai::ConstEnum::index = index, mai::ConstEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{mai::ConstEnum::_name};
+}
+class ConstClass extends core::Object {
+ synthetic constructor •() → mai::ConstClass
+ : super core::Object::•()
+ ;
+ method method(mai::ConstEnum e) → core::int
+ return e.{mai::ConstEnum::index};
+}
+
+constants {
+ #C1 = 0
+ #C2 = "UnusedEnum.a"
+ #C3 = self::UnusedEnum {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "UnusedEnum.b"
+ #C6 = self::UnusedEnum {index:#C4, _name:#C5}
+ #C7 = <self::UnusedEnum>[#C3, #C6]
+ #C8 = "UsedEnum.unusedValue"
+ #C9 = self::UsedEnum {index:#C1, _name:#C8}
+ #C10 = "UsedEnum.usedValue"
+ #C11 = self::UsedEnum {index:#C4, _name:#C10}
+ #C12 = <self::UsedEnum>[#C9, #C11]
+ #C13 = "ConstEnum.value"
+ #C14 = mai::ConstEnum {index:#C1, _name:#C13}
+ #C15 = <mai::ConstEnum>[#C14]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- ConstEnum. (from org-dartlang-testcase:///main_lib.dart:5:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+
+org-dartlang-testcase:///main.dart:
+- UnusedEnum. (from org-dartlang-testcase:///main.dart:7:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- UsedEnum. (from org-dartlang-testcase:///main.dart:8:6)
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect
new file mode 100644
index 0000000..23b7ce8
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect
@@ -0,0 +1,66 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+import "dart:_internal" as _in;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+abstract class UnusedEnum extends core::Object {
+}
+class UsedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ method toString() → core::String
+ return this.{self::UsedEnum::_name};
+}
+abstract class UnusedInterface extends core::Object {
+ abstract get /*isLegacy*/ usedInterfaceField() → core::int?;
+ abstract set /*isLegacy*/ usedInterfaceField(core::int? value) → void;
+}
+class UsedClass extends core::Object implements self::UnusedInterface {
+ field core::int? usedField = null;
+ field core::int? usedInterfaceField = null;
+ synthetic constructor •() → self::UsedClass
+ : super core::Object::•()
+ ;
+}
+static method usedMethod(self::UnusedInterface c) → dynamic {
+ c.{self::UnusedInterface::usedInterfaceField} = c.{self::UnusedInterface::usedInterfaceField};
+}
+static method main() → dynamic {
+ self::usedMethod(let final self::UsedClass #t1 = new self::UsedClass::•() in block {
+ #t1.{self::UsedClass::usedField};
+ } =>#t1);
+ #C3;
+ core::List<self::UnusedEnum> list = core::_GrowableList::•<self::UnusedEnum>(0);
+ if(list.{core::Iterable::isNotEmpty}) {
+ let dynamic #t2 = new mai::ConstClass::•() in let dynamic #t3 = _in::unsafeCast<dynamic>(null) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::ConstEnum in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+abstract class ConstEnum extends core::Object {
+}
+class ConstClass extends core::Object {
+ synthetic constructor •() → mai::ConstClass
+ : super core::Object::•()
+ ;
+}
+
+constants {
+ #C1 = 1
+ #C2 = "UsedEnum.usedValue"
+ #C3 = self::UsedEnum {index:#C1, _name:#C2}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+
+org-dartlang-testcase:///main.dart:
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.textual_outline.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.textual_outline.expect
new file mode 100644
index 0000000..97f788b
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.textual_outline.expect
@@ -0,0 +1,24 @@
+import 'main_lib.dart';
+
+enum UnusedEnum { a, b }
+enum UsedEnum {
+ unusedValue,
+ usedValue,
+}
+usedMethod(UnusedInterface c) {}
+unusedMethod() {}
+
+class UnusedInterface {
+ int? usedInterfaceField;
+ UnusedInterface(this.usedInterfaceField);
+}
+
+class UsedClass implements UnusedInterface {
+ int? unusedField;
+ int? usedField;
+ int? usedInterfaceField;
+}
+
+class UnusedClass {}
+
+main() {}
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..a10c6f3
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.textual_outline_modelled.expect
@@ -0,0 +1,23 @@
+import 'main_lib.dart';
+
+class UnusedClass {}
+
+class UnusedInterface {
+ UnusedInterface(this.usedInterfaceField);
+ int? usedInterfaceField;
+}
+
+class UsedClass implements UnusedInterface {
+ int? unusedField;
+ int? usedField;
+ int? usedInterfaceField;
+}
+
+enum UnusedEnum { a, b }
+enum UsedEnum {
+ unusedValue,
+ usedValue,
+}
+main() {}
+unusedMethod() {}
+usedMethod(UnusedInterface c) {}
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.expect
new file mode 100644
index 0000000..55eeff3
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.expect
@@ -0,0 +1,116 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class UnusedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::UnusedEnum> values = #C7;
+ static const field self::UnusedEnum a = #C3;
+ static const field self::UnusedEnum b = #C6;
+ const constructor •(core::int index, core::String _name) → self::UnusedEnum
+ : self::UnusedEnum::index = index, self::UnusedEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self::UnusedEnum::_name};
+}
+class UsedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::UsedEnum> values = #C12;
+ static const field self::UsedEnum unusedValue = #C9;
+ static const field self::UsedEnum usedValue = #C11;
+ const constructor •(core::int index, core::String _name) → self::UsedEnum
+ : self::UsedEnum::index = index, self::UsedEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self::UsedEnum::_name};
+}
+class UnusedInterface extends core::Object {
+ field core::int? usedInterfaceField;
+ constructor •(core::int? usedInterfaceField) → self::UnusedInterface
+ : self::UnusedInterface::usedInterfaceField = usedInterfaceField, super core::Object::•()
+ ;
+}
+class UsedClass extends core::Object implements self::UnusedInterface {
+ field core::int? unusedField = null;
+ field core::int? usedField = null;
+ field core::int? usedInterfaceField = null;
+ synthetic constructor •() → self::UsedClass
+ : super core::Object::•()
+ ;
+}
+class UnusedClass extends core::Object {
+ synthetic constructor •() → self::UnusedClass
+ : super core::Object::•()
+ ;
+}
+static method usedMethod(self::UnusedInterface c) → dynamic {
+ c.{self::UnusedInterface::usedInterfaceField} = c.{self::UnusedInterface::usedInterfaceField};
+}
+static method unusedMethod() → dynamic {}
+static method main() → dynamic {
+ self::usedMethod(let final self::UsedClass #t1 = new self::UsedClass::•() in block {
+ #t1.{self::UsedClass::usedField};
+ } =>#t1);
+ #C11;
+ core::List<self::UnusedEnum> list = <self::UnusedEnum>[];
+ if(list.{core::Iterable::isNotEmpty}) {
+ new mai::ConstClass::•().{mai::ConstClass::method}((null as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::ConstEnum);
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class ConstEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<mai::ConstEnum> values = #C15;
+ static const field mai::ConstEnum value = #C14;
+ const constructor •(core::int index, core::String _name) → mai::ConstEnum
+ : mai::ConstEnum::index = index, mai::ConstEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{mai::ConstEnum::_name};
+}
+class ConstClass extends core::Object {
+ synthetic constructor •() → mai::ConstClass
+ : super core::Object::•()
+ ;
+ method method(mai::ConstEnum e) → core::int
+ return e.{mai::ConstEnum::index};
+}
+
+constants {
+ #C1 = 0
+ #C2 = "UnusedEnum.a"
+ #C3 = self::UnusedEnum {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "UnusedEnum.b"
+ #C6 = self::UnusedEnum {index:#C4, _name:#C5}
+ #C7 = <self::UnusedEnum*>[#C3, #C6]
+ #C8 = "UsedEnum.unusedValue"
+ #C9 = self::UsedEnum {index:#C1, _name:#C8}
+ #C10 = "UsedEnum.usedValue"
+ #C11 = self::UsedEnum {index:#C4, _name:#C10}
+ #C12 = <self::UsedEnum*>[#C9, #C11]
+ #C13 = "ConstEnum.value"
+ #C14 = mai::ConstEnum {index:#C1, _name:#C13}
+ #C15 = <mai::ConstEnum*>[#C14]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- ConstEnum. (from org-dartlang-testcase:///main_lib.dart:5:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+
+org-dartlang-testcase:///main.dart:
+- UnusedEnum. (from org-dartlang-testcase:///main.dart:7:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- UsedEnum. (from org-dartlang-testcase:///main.dart:8:6)
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.outline.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.outline.expect
new file mode 100644
index 0000000..43c42d9
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.outline.expect
@@ -0,0 +1,96 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class UnusedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::UnusedEnum> values = const <self::UnusedEnum>[self::UnusedEnum::a, self::UnusedEnum::b];
+ static const field self::UnusedEnum a = const self::UnusedEnum::•(0, "UnusedEnum.a");
+ static const field self::UnusedEnum b = const self::UnusedEnum::•(1, "UnusedEnum.b");
+ const constructor •(core::int index, core::String _name) → self::UnusedEnum
+ : self::UnusedEnum::index = index, self::UnusedEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self::UnusedEnum::_name};
+}
+class UsedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::UsedEnum> values = const <self::UsedEnum>[self::UsedEnum::unusedValue, self::UsedEnum::usedValue];
+ static const field self::UsedEnum unusedValue = const self::UsedEnum::•(0, "UsedEnum.unusedValue");
+ static const field self::UsedEnum usedValue = const self::UsedEnum::•(1, "UsedEnum.usedValue");
+ const constructor •(core::int index, core::String _name) → self::UsedEnum
+ : self::UsedEnum::index = index, self::UsedEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self::UsedEnum::_name};
+}
+class UnusedInterface extends core::Object {
+ field core::int? usedInterfaceField;
+ constructor •(core::int? usedInterfaceField) → self::UnusedInterface
+ ;
+}
+class UsedClass extends core::Object implements self::UnusedInterface {
+ field core::int? unusedField;
+ field core::int? usedField;
+ field core::int? usedInterfaceField;
+ synthetic constructor •() → self::UsedClass
+ ;
+}
+class UnusedClass extends core::Object {
+ synthetic constructor •() → self::UnusedClass
+ ;
+}
+static method usedMethod(self::UnusedInterface c) → dynamic
+ ;
+static method unusedMethod() → dynamic
+ ;
+static method main() → dynamic
+ ;
+
+library /*isNonNullableByDefault*/;
+import self as self2;
+import "dart:core" as core;
+
+class ConstEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self2::ConstEnum> values = #C4;
+ static const field self2::ConstEnum value = #C3;
+ const constructor •(core::int index, core::String _name) → self2::ConstEnum
+ : self2::ConstEnum::index = index, self2::ConstEnum::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{self2::ConstEnum::_name};
+}
+class ConstClass extends core::Object {
+ synthetic constructor •() → self2::ConstClass
+ ;
+ method method(self2::ConstEnum e) → core::int
+ ;
+}
+
+constants {
+ #C1 = 0
+ #C2 = "ConstEnum.value"
+ #C3 = self2::ConstEnum {index:#C1, _name:#C2}
+ #C4 = <self2::ConstEnum*>[#C3]
+}
+
+Extra constant evaluation status:
+Evaluated: ListLiteral @ org-dartlang-testcase:///main.dart:7:6 -> ListConstant(const <UnusedEnum*>[const UnusedEnum{UnusedEnum.index: 0, UnusedEnum._name: "UnusedEnum.a"}, const UnusedEnum{UnusedEnum.index: 1, UnusedEnum._name: "UnusedEnum.b"}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///main.dart:7:19 -> InstanceConstant(const UnusedEnum{UnusedEnum.index: 0, UnusedEnum._name: "UnusedEnum.a"})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///main.dart:7:22 -> InstanceConstant(const UnusedEnum{UnusedEnum.index: 1, UnusedEnum._name: "UnusedEnum.b"})
+Evaluated: ListLiteral @ org-dartlang-testcase:///main.dart:8:6 -> ListConstant(const <UsedEnum*>[const UsedEnum{UsedEnum.index: 0, UsedEnum._name: "UsedEnum.unusedValue"}, const UsedEnum{UsedEnum.index: 1, UsedEnum._name: "UsedEnum.usedValue"}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///main.dart:9:3 -> InstanceConstant(const UsedEnum{UsedEnum.index: 0, UsedEnum._name: "UsedEnum.unusedValue"})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///main.dart:10:3 -> InstanceConstant(const UsedEnum{UsedEnum.index: 1, UsedEnum._name: "UsedEnum.usedValue"})
+Extra constant evaluation: evaluated: 18, effectively constant: 6
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- ConstEnum. (from org-dartlang-testcase:///main_lib.dart:5:6)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.transformed.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..80c0d6a
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.weak.transformed.expect
@@ -0,0 +1,69 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+import "dart:_internal" as _in;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+abstract class UnusedEnum extends core::Object {
+}
+class UsedEnum extends core::Object /*isEnum*/ {
+ final field core::int index;
+ final field core::String _name;
+ method toString() → core::String
+ return this.{self::UsedEnum::_name};
+}
+abstract class UnusedInterface extends core::Object {
+ abstract get /*isLegacy*/ usedInterfaceField() → core::int?;
+ abstract set /*isLegacy*/ usedInterfaceField(core::int? value) → void;
+}
+class UsedClass extends core::Object implements self::UnusedInterface {
+ field core::int? usedField = null;
+ field core::int? usedInterfaceField = null;
+ synthetic constructor •() → self::UsedClass
+ : super core::Object::•()
+ ;
+}
+static method usedMethod(self::UnusedInterface c) → dynamic {
+ c.{self::UnusedInterface::usedInterfaceField} = c.{self::UnusedInterface::usedInterfaceField};
+}
+static method main() → dynamic {
+ self::usedMethod(let final self::UsedClass #t1 = new self::UsedClass::•() in block {
+ #t1.{self::UsedClass::usedField};
+ } =>#t1);
+ #C3;
+ core::List<self::UnusedEnum> list = core::_GrowableList::•<self::UnusedEnum>(0);
+ if(list.{core::Iterable::isNotEmpty}) {
+ new mai::ConstClass::•().{mai::ConstClass::method}(_in::unsafeCast<mai::ConstEnum>(_in::unsafeCast<dynamic>(null)));
+ }
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+abstract class ConstEnum extends core::Object {
+ abstract get /*isLegacy*/ index() → core::int;
+}
+class ConstClass extends core::Object {
+ synthetic constructor •() → mai::ConstClass
+ : super core::Object::•()
+ ;
+ method method(mai::ConstEnum e) → core::int
+ return e.{mai::ConstEnum::index};
+}
+
+constants {
+ #C1 = 1
+ #C2 = "UsedEnum.usedValue"
+ #C3 = self::UsedEnum {index:#C1, _name:#C2}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+
+org-dartlang-testcase:///main.dart:
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/aot/tree_shake/main_lib.dart b/pkg/front_end/testcases/aot/tree_shake/main_lib.dart
new file mode 100644
index 0000000..bc873dc
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/main_lib.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+enum ConstEnum { value }
+
+class ConstClass {
+ int method(ConstEnum e) => e.index;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake/test.options b/pkg/front_end/testcases/aot/tree_shake/test.options
new file mode 100644
index 0000000..bfe6dc8
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake/test.options
@@ -0,0 +1 @@
+main_lib.dart
\ No newline at end of file
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart
new file mode 100644
index 0000000..53ab22e
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'main_lib.dart';
+
+class Class implements Interface {
+ int? field1;
+ int? field2;
+ int? field3;
+}
+
+void method(Interface i) {
+ i.field2 = i.field1;
+ i.field3 = i.field3;
+}
+
+main() {
+ method(new Class());
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.strong.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.strong.expect
new file mode 100644
index 0000000..fce2045
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.strong.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class Class extends core::Object implements mai::Interface {
+ field core::int? field1 = null;
+ field core::int? field2 = null;
+ field core::int? field3 = null;
+ synthetic constructor •() → self::Class
+ : super core::Object::•()
+ ;
+}
+static method method(mai::Interface i) → void {
+ i.{mai::Interface::field2} = i.{mai::Interface::field1};
+ i.{mai::Interface::field3} = i.{mai::Interface::field3};
+}
+static method main() → dynamic {
+ self::method(new self::Class::•());
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class Interface extends core::Object {
+ field core::int? field1 = null;
+ field core::int? field2 = null;
+ field core::int? field3 = null;
+ synthetic constructor •() → mai::Interface
+ : super core::Object::•()
+ ;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.strong.transformed.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.strong.transformed.expect
new file mode 100644
index 0000000..9a18bdf
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.strong.transformed.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class Class extends core::Object implements mai::Interface {
+ field core::int? field1 = null;
+ field core::int? field3 = null;
+ synthetic constructor •() → self::Class
+ : super core::Object::•()
+ ;
+ set /*isLegacy*/ field2(core::int? value) → void;
+}
+static method method(mai::Interface i) → void {
+ i.{mai::Interface::field2} = i.{mai::Interface::field1};
+ i.{mai::Interface::field3} = i.{mai::Interface::field3};
+}
+static method main() → dynamic {
+ self::method(new self::Class::•());
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+abstract class Interface extends core::Object {
+ abstract get /*isLegacy*/ field1() → core::int?;
+ abstract set /*isLegacy*/ field2(core::int? value) → void;
+ abstract get /*isLegacy*/ field3() → core::int?;
+ abstract set /*isLegacy*/ field3(core::int? value) → void;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.textual_outline.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.textual_outline.expect
new file mode 100644
index 0000000..433a1aa
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+import 'main_lib.dart';
+
+class Class implements Interface {
+ int? field1;
+ int? field2;
+ int? field3;
+}
+
+void method(Interface i) {}
+main() {}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..60b9b0d
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.textual_outline_modelled.expect
@@ -0,0 +1,10 @@
+import 'main_lib.dart';
+
+class Class implements Interface {
+ int? field1;
+ int? field2;
+ int? field3;
+}
+
+main() {}
+void method(Interface i) {}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.expect
new file mode 100644
index 0000000..fce2045
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class Class extends core::Object implements mai::Interface {
+ field core::int? field1 = null;
+ field core::int? field2 = null;
+ field core::int? field3 = null;
+ synthetic constructor •() → self::Class
+ : super core::Object::•()
+ ;
+}
+static method method(mai::Interface i) → void {
+ i.{mai::Interface::field2} = i.{mai::Interface::field1};
+ i.{mai::Interface::field3} = i.{mai::Interface::field3};
+}
+static method main() → dynamic {
+ self::method(new self::Class::•());
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class Interface extends core::Object {
+ field core::int? field1 = null;
+ field core::int? field2 = null;
+ field core::int? field3 = null;
+ synthetic constructor •() → mai::Interface
+ : super core::Object::•()
+ ;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.outline.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.outline.expect
new file mode 100644
index 0000000..7018a21
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.outline.expect
@@ -0,0 +1,30 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class Class extends core::Object implements mai::Interface {
+ field core::int? field1;
+ field core::int? field2;
+ field core::int? field3;
+ synthetic constructor •() → self::Class
+ ;
+}
+static method method(mai::Interface i) → void
+ ;
+static method main() → dynamic
+ ;
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+class Interface extends core::Object {
+ field core::int? field1;
+ field core::int? field2;
+ field core::int? field3;
+ synthetic constructor •() → mai::Interface
+ ;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.transformed.expect b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..9a18bdf
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main.dart.weak.transformed.expect
@@ -0,0 +1,33 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "main_lib.dart" as mai;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+class Class extends core::Object implements mai::Interface {
+ field core::int? field1 = null;
+ field core::int? field3 = null;
+ synthetic constructor •() → self::Class
+ : super core::Object::•()
+ ;
+ set /*isLegacy*/ field2(core::int? value) → void;
+}
+static method method(mai::Interface i) → void {
+ i.{mai::Interface::field2} = i.{mai::Interface::field1};
+ i.{mai::Interface::field3} = i.{mai::Interface::field3};
+}
+static method main() → dynamic {
+ self::method(new self::Class::•());
+}
+
+library /*isNonNullableByDefault*/;
+import self as mai;
+import "dart:core" as core;
+
+abstract class Interface extends core::Object {
+ abstract get /*isLegacy*/ field1() → core::int?;
+ abstract set /*isLegacy*/ field2(core::int? value) → void;
+ abstract get /*isLegacy*/ field3() → core::int?;
+ abstract set /*isLegacy*/ field3(core::int? value) → void;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main_lib.dart b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main_lib.dart
new file mode 100644
index 0000000..190ab1a
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/main_lib.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class Interface {
+ int? field1;
+ int? field2;
+ int? field3;
+}
diff --git a/pkg/front_end/testcases/aot/tree_shake_field_from_lib/test.options b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/test.options
new file mode 100644
index 0000000..bfe6dc8
--- /dev/null
+++ b/pkg/front_end/testcases/aot/tree_shake_field_from_lib/test.options
@@ -0,0 +1 @@
+main_lib.dart
\ No newline at end of file
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart
new file mode 100644
index 0000000..114ac6f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A {
+ int get foo => 42;
+}
+
+extension E on A {
+ double get bar => 3.14;
+}
+
+test(A a, E e) {
+ a.foo; // Ok.
+ a.bar; // Ok.
+ e.foo; // Error.
+ e.bar; // Ok.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.strong.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.strong.expect
new file mode 100644
index 0000000..8c6d2ea
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.strong.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+// e.foo; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::int
+ return 42;
+}
+extension E on self::A {
+ get bar = self::E|get#bar;
+}
+static method E|get#bar(lowered final self::A #this) → core::double
+ return 3.14;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo};
+ self::E|get#bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+ e.foo; // Error.
+ ^^^";
+ self::E|get#bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.strong.transformed.expect
new file mode 100644
index 0000000..8c6d2ea
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.strong.transformed.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+// e.foo; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::int
+ return 42;
+}
+extension E on self::A {
+ get bar = self::E|get#bar;
+}
+static method E|get#bar(lowered final self::A #this) → core::double
+ return 3.14;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo};
+ self::E|get#bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+ e.foo; // Error.
+ ^^^";
+ self::E|get#bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.textual_outline.expect
new file mode 100644
index 0000000..587163f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+class A {
+ int get foo => 42;
+}
+
+extension E on A {
+ double get bar => 3.14;
+}
+
+test(A a, E e) {}
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..8d51c64
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.textual_outline_modelled.expect
@@ -0,0 +1,10 @@
+class A {
+ int get foo => 42;
+}
+
+extension E on A {
+ double get bar => 3.14;
+}
+
+main() {}
+test(A a, E e) {}
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.expect
new file mode 100644
index 0000000..8c6d2ea
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+// e.foo; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::int
+ return 42;
+}
+extension E on self::A {
+ get bar = self::E|get#bar;
+}
+static method E|get#bar(lowered final self::A #this) → core::double
+ return 3.14;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo};
+ self::E|get#bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+ e.foo; // Error.
+ ^^^";
+ self::E|get#bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.outline.expect
new file mode 100644
index 0000000..08cef61
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.outline.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+ get foo() → core::int
+ ;
+}
+extension E on self::A {
+ get bar = self::E|get#bar;
+}
+static method E|get#bar(lowered final self::A #this) → core::double
+ ;
+static method test(self::A a, self::E e) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.transformed.expect
new file mode 100644
index 0000000..8c6d2ea
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_getter_resolution.dart.weak.transformed.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+// e.foo; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::int
+ return 42;
+}
+extension E on self::A {
+ get bar = self::E|get#bar;
+}
+static method E|get#bar(lowered final self::A #this) → core::double
+ return 3.14;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo};
+ self::E|get#bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_getter_resolution.dart:16:5: Error: The getter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+ e.foo; // Error.
+ ^^^";
+ self::E|get#bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart
new file mode 100644
index 0000000..7f1f10b
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A {
+ dynamic operator*(dynamic other) => 42;
+ dynamic operator[](int index) => 42;
+ void operator[]=(int index, dynamic value) {}
+ dynamic operator-() => 42;
+}
+
+extension E on A {
+ dynamic operator+(dynamic other) => 42;
+}
+
+test(A a, E e) {
+ a * "foobar"; // Ok.
+ a[0]; // Ok.
+ a[0] = "foobar"; // Ok.
+ -a; // Ok.
+ a + "foobar"; // Ok.
+
+ e * "foobar"; // Error.
+ e[0]; // Error.
+ e[0] = "foobar"; // Error.
+ -e; // Error.
+ e + "foobar"; // Ok.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.strong.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.strong.expect
new file mode 100644
index 0000000..80d58c8
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.strong.expect
@@ -0,0 +1,69 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '*' operator.
+// e * "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]' operator.
+// e[0]; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]=' operator.
+// e[0] = "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+// -e; // Error.
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ operator *(dynamic other) → dynamic
+ return 42;
+ operator [](core::int index) → dynamic
+ return 42;
+ operator []=(core::int index, dynamic value) → void {}
+ operator unary-() → dynamic
+ return 42;
+}
+extension E on self::A {
+ operator + = self::E|+;
+}
+static method E|+(lowered final self::A #this, dynamic other) → dynamic
+ return 42;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::*}("foobar");
+ a.{self::A::[]}(0);
+ a.{self::A::[]=}(0, "foobar");
+ a.{self::A::unary-}();
+ self::E|+(a, "foobar");
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '*' operator.
+ e * \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]' operator.
+ e[0]; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]=' operator.
+ e[0] = \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+ -e; // Error.
+ ^";
+ self::E|+(e, "foobar");
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.strong.transformed.expect
new file mode 100644
index 0000000..80d58c8
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.strong.transformed.expect
@@ -0,0 +1,69 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '*' operator.
+// e * "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]' operator.
+// e[0]; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]=' operator.
+// e[0] = "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+// -e; // Error.
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ operator *(dynamic other) → dynamic
+ return 42;
+ operator [](core::int index) → dynamic
+ return 42;
+ operator []=(core::int index, dynamic value) → void {}
+ operator unary-() → dynamic
+ return 42;
+}
+extension E on self::A {
+ operator + = self::E|+;
+}
+static method E|+(lowered final self::A #this, dynamic other) → dynamic
+ return 42;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::*}("foobar");
+ a.{self::A::[]}(0);
+ a.{self::A::[]=}(0, "foobar");
+ a.{self::A::unary-}();
+ self::E|+(a, "foobar");
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '*' operator.
+ e * \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]' operator.
+ e[0]; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]=' operator.
+ e[0] = \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+ -e; // Error.
+ ^";
+ self::E|+(e, "foobar");
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.textual_outline.expect
new file mode 100644
index 0000000..b773643
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+class A {
+ dynamic operator *(dynamic other) => 42;
+ dynamic operator [](int index) => 42;
+ void operator []=(int index, dynamic value) {}
+ dynamic operator -() => 42;
+}
+
+extension E on A {
+ dynamic operator +(dynamic other) => 42;
+}
+
+test(A a, E e) {}
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3cbb6bf
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+class A {
+ dynamic operator *(dynamic other) => 42;
+ dynamic operator -() => 42;
+ dynamic operator [](int index) => 42;
+ void operator []=(int index, dynamic value) {}
+}
+
+extension E on A {
+ dynamic operator +(dynamic other) => 42;
+}
+
+main() {}
+test(A a, E e) {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.expect
new file mode 100644
index 0000000..80d58c8
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.expect
@@ -0,0 +1,69 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '*' operator.
+// e * "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]' operator.
+// e[0]; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]=' operator.
+// e[0] = "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+// -e; // Error.
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ operator *(dynamic other) → dynamic
+ return 42;
+ operator [](core::int index) → dynamic
+ return 42;
+ operator []=(core::int index, dynamic value) → void {}
+ operator unary-() → dynamic
+ return 42;
+}
+extension E on self::A {
+ operator + = self::E|+;
+}
+static method E|+(lowered final self::A #this, dynamic other) → dynamic
+ return 42;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::*}("foobar");
+ a.{self::A::[]}(0);
+ a.{self::A::[]=}(0, "foobar");
+ a.{self::A::unary-}();
+ self::E|+(a, "foobar");
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '*' operator.
+ e * \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]' operator.
+ e[0]; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]=' operator.
+ e[0] = \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+ -e; // Error.
+ ^";
+ self::E|+(e, "foobar");
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.outline.expect
new file mode 100644
index 0000000..af29517
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.outline.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+ operator *(dynamic other) → dynamic
+ ;
+ operator [](core::int index) → dynamic
+ ;
+ operator []=(core::int index, dynamic value) → void
+ ;
+ operator unary-() → dynamic
+ ;
+}
+extension E on self::A {
+ operator + = self::E|+;
+}
+static method E|+(lowered final self::A #this, dynamic other) → dynamic
+ ;
+static method test(self::A a, self::E e) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.transformed.expect
new file mode 100644
index 0000000..80d58c8
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_operator_resolution.dart.weak.transformed.expect
@@ -0,0 +1,69 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '*' operator.
+// e * "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]' operator.
+// e[0]; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a '[]=' operator.
+// e[0] = "foobar"; // Error.
+// ^
+//
+// pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+// Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+// -e; // Error.
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ operator *(dynamic other) → dynamic
+ return 42;
+ operator [](core::int index) → dynamic
+ return 42;
+ operator []=(core::int index, dynamic value) → void {}
+ operator unary-() → dynamic
+ return 42;
+}
+extension E on self::A {
+ operator + = self::E|+;
+}
+static method E|+(lowered final self::A #this, dynamic other) → dynamic
+ return 42;
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::*}("foobar");
+ a.{self::A::[]}(0);
+ a.{self::A::[]=}(0, "foobar");
+ a.{self::A::unary-}();
+ self::E|+(a, "foobar");
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:23:5: Error: The operator '*' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '*' operator.
+ e * \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:24:4: Error: The operator '[]' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]' operator.
+ e[0]; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:25:4: Error: The operator '[]=' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a '[]=' operator.
+ e[0] = \"foobar\"; // Error.
+ ^";
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_operator_resolution.dart:26:3: Error: The operator 'unary-' isn't defined for the extension 'E'.
+Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+ -e; // Error.
+ ^";
+ self::E|+(e, "foobar");
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart
new file mode 100644
index 0000000..20f43d6
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A {
+ void set foo(int value) {}
+}
+
+extension E on A {
+ void set bar(int value) {}
+}
+
+test(A a, E e) {
+ a.foo = 42; // Ok.
+ a.bar = 42; // Ok.
+ e.foo = 42; // Error.
+ e.bar = 42; // Ok.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.strong.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.strong.expect
new file mode 100644
index 0000000..c7f4354
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.strong.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+// e.foo = 42; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ set foo(core::int value) → void {}
+}
+extension E on self::A {
+ set bar = self::E|set#bar;
+}
+static method E|set#bar(lowered final self::A #this, core::int value) → void {}
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo} = 42;
+ self::E|set#bar(a, 42);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+ e.foo = 42; // Error.
+ ^^^";
+ self::E|set#bar(e, 42);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.strong.transformed.expect
new file mode 100644
index 0000000..c7f4354
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.strong.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+// e.foo = 42; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ set foo(core::int value) → void {}
+}
+extension E on self::A {
+ set bar = self::E|set#bar;
+}
+static method E|set#bar(lowered final self::A #this, core::int value) → void {}
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo} = 42;
+ self::E|set#bar(a, 42);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+ e.foo = 42; // Error.
+ ^^^";
+ self::E|set#bar(e, 42);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.textual_outline.expect
new file mode 100644
index 0000000..d7b5425
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+class A {
+ void set foo(int value) {}
+}
+
+extension E on A {
+ void set bar(int value) {}
+}
+
+test(A a, E e) {}
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..74b9e08
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.textual_outline_modelled.expect
@@ -0,0 +1,10 @@
+class A {
+ void set foo(int value) {}
+}
+
+extension E on A {
+ void set bar(int value) {}
+}
+
+main() {}
+test(A a, E e) {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.expect
new file mode 100644
index 0000000..c7f4354
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+// e.foo = 42; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ set foo(core::int value) → void {}
+}
+extension E on self::A {
+ set bar = self::E|set#bar;
+}
+static method E|set#bar(lowered final self::A #this, core::int value) → void {}
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo} = 42;
+ self::E|set#bar(a, 42);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+ e.foo = 42; // Error.
+ ^^^";
+ self::E|set#bar(e, 42);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.outline.expect
new file mode 100644
index 0000000..05a6bb6
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.outline.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+ set foo(core::int value) → void
+ ;
+}
+extension E on self::A {
+ set bar = self::E|set#bar;
+}
+static method E|set#bar(lowered final self::A #this, core::int value) → void
+ ;
+static method test(self::A a, self::E e) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.transformed.expect
new file mode 100644
index 0000000..c7f4354
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_setter_resolution.dart.weak.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+// e.foo = 42; // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ set foo(core::int value) → void {}
+}
+extension E on self::A {
+ set bar = self::E|set#bar;
+}
+static method E|set#bar(lowered final self::A #this, core::int value) → void {}
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo} = 42;
+ self::E|set#bar(a, 42);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_setter_resolution.dart:16:5: Error: The setter 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+ e.foo = 42; // Error.
+ ^^^";
+ self::E|set#bar(e, 42);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect
index ad8a087..41e48cc 100644
--- a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect
@@ -6,8 +6,8 @@
}
library from "package:foo/foo.dart" as foo {
additionalExports = (a::example,
- a::example,
- a::a)
+ a::a,
+ a::example)
export "package:example/a.dart";
diff --git a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect
index ad8a087..41e48cc 100644
--- a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect
@@ -6,8 +6,8 @@
}
library from "package:foo/foo.dart" as foo {
additionalExports = (a::example,
- a::example,
- a::a)
+ a::a,
+ a::example)
export "package:example/a.dart";
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect
@@ -10,8 +10,8 @@
}
library from "org-dartlang-test:///libExporter.dart" as lib2 {
additionalExports = (lib::libField,
- lib::libField,
- lib::requireStuffFromLibExporter)
+ lib::requireStuffFromLibExporter,
+ lib::libField)
export "org-dartlang-test:///lib.dart";
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect
@@ -10,8 +10,8 @@
}
library from "org-dartlang-test:///libExporter.dart" as lib2 {
additionalExports = (lib::libField,
- lib::libField,
- lib::requireStuffFromLibExporter)
+ lib::requireStuffFromLibExporter,
+ lib::libField)
export "org-dartlang-test:///lib.dart";
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect
@@ -10,8 +10,8 @@
}
library from "org-dartlang-test:///libExporter.dart" as lib2 {
additionalExports = (lib::libField,
- lib::libField,
- lib::requireStuffFromLibExporter)
+ lib::requireStuffFromLibExporter,
+ lib::libField)
export "org-dartlang-test:///lib.dart";
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect
@@ -10,8 +10,8 @@
}
library from "org-dartlang-test:///libExporter.dart" as lib2 {
additionalExports = (lib::libField,
- lib::libField,
- lib::requireStuffFromLibExporter)
+ lib::requireStuffFromLibExporter,
+ lib::libField)
export "org-dartlang-test:///lib.dart";
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
index fa7c510..0564c29 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
@@ -9,19 +9,20 @@
synthetic constructor •() → self::StructInlineArray
: super ffi::Struct::•()
;
- @#C2
+ @#C3
external get a0() → ffi::Array<ffi::Uint8>;
- @#C2
+ @#C3
external set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void;
}
static method main() → dynamic {}
constants {
#C1 = 8
- #C2 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1}
+ #C2 = null
+ #C3 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C2, dimension3:#C2, dimension4:#C2, dimension5:#C2, dimensions:#C2}
}
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:133:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
index 77d29d6..58634b4 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
@@ -23,7 +23,7 @@
return new ffi::Array::_<ffi::Array<ffi::Uint8>>( block {
core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
core::int #offset = (#C14).{core::List::[]}(ffi::_abi());
- } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3);
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3, #C15);
@#C12
set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C8).{core::List::[]}(ffi::_abi()));
@@ -42,12 +42,13 @@
#C9 = "vm:entry-point"
#C10 = null
#C11 = core::pragma {name:#C9, options:#C10}
- #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3}
+ #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C10, dimension3:#C10, dimension4:#C10, dimension5:#C10, dimensions:#C10}
#C13 = 0
#C14 = <core::int*>[#C13, #C13, #C13]
+ #C15 = <core::int*>[]
}
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:133:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
index 5125b89..3637817 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
@@ -9,19 +9,20 @@
synthetic constructor •() → self::StructInlineArray
: super ffi::Struct::•()
;
- @#C2
+ @#C3
external get a0() → ffi::Array<ffi::Uint8>;
- @#C2
+ @#C3
external set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void;
}
static method main() → dynamic {}
constants {
#C1 = 8
- #C2 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C1}
+ #C2 = null
+ #C3 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C1, dimension2:#C2, dimension3:#C2, dimension4:#C2, dimension5:#C2, dimensions:#C2}
}
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:133:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.outline.expect
index 75350ad..209ea1d 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.outline.expect
@@ -18,6 +18,6 @@
Extra constant evaluation status:
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType*>{_ArraySize.dimension1: 8})
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType*>{_ArraySize.dimension1: 8})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType*>{_ArraySize.dimension1: 8, _ArraySize.dimension2: null, _ArraySize.dimension3: null, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType*>{_ArraySize.dimension1: 8, _ArraySize.dimension2: null, _ArraySize.dimension3: null, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
Extra constant evaluation: evaluated: 2, effectively constant: 2
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
index bd41225..5ea5b51 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
@@ -23,7 +23,7 @@
return new ffi::Array::_<ffi::Array<ffi::Uint8>>( block {
core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
core::int #offset = (#C14).{core::List::[]}(ffi::_abi());
- } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3);
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3, #C15);
@#C12
set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C8).{core::List::[]}(ffi::_abi()));
@@ -42,12 +42,13 @@
#C9 = "vm:entry-point"
#C10 = null
#C11 = core::pragma {name:#C9, options:#C10}
- #C12 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C3}
+ #C12 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C3, dimension2:#C10, dimension3:#C10, dimension4:#C10, dimension5:#C10, dimensions:#C10}
#C13 = 0
#C14 = <core::int*>[#C13, #C13, #C13]
+ #C15 = <core::int*>[]
}
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:133:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart
new file mode 100644
index 0000000..08618c8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:ffi';
+
+import "package:ffi/ffi.dart";
+
+class StructInlineArrayMultiDimensional extends Struct {
+ @Array(2, 2, 2)
+ external Array<Array<Array<Uint8>>> a0;
+}
+
+main() {
+ final pointer = calloc<StructInlineArrayMultiDimensional>();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ final subArray = array[0];
+ array[1] = subArray;
+ calloc.free(pointer);
+}
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
new file mode 100644
index 0000000..7d5a7e1
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:ffi" as ffi;
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+class StructInlineArrayMultiDimensional extends ffi::Struct {
+ synthetic constructor •() → self::StructInlineArrayMultiDimensional
+ : super ffi::Struct::•()
+ ;
+ @#C3
+ external get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>;
+ @#C3
+ external set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void;
+}
+static method main() → dynamic {
+ final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = ffi::AllocatorAlloc|call<self::StructInlineArrayMultiDimensional>(#C4);
+ final self::StructInlineArrayMultiDimensional struct = ffi::StructPointer|get#ref<self::StructInlineArrayMultiDimensional>(pointer);
+ final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0};
+ final ffi::Array<ffi::Array<ffi::Uint8>> subArray = ffi::ArrayArray|[]<ffi::Array<ffi::Uint8>>(array, 0);
+ ffi::ArrayArray|[]=<ffi::Array<ffi::Uint8>>(array, 1, subArray);
+ (#C4).{ffi::Allocator::free}(pointer);
+}
+
+constants {
+ #C1 = 2
+ #C2 = null
+ #C3 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C1, dimension3:#C1, dimension4:#C2, dimension5:#C2, dimensions:#C2}
+ #C4 = all::_CallocAllocator {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
new file mode 100644
index 0000000..c6d3600
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+@#C7
+class StructInlineArrayMultiDimensional extends ffi::Struct {
+ static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ synthetic constructor •() → self::StructInlineArrayMultiDimensional
+ : super ffi::Struct::•()
+ ;
+ @#C11
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
+ : super ffi::Struct::_fromPointer(#pointer)
+ ;
+ @#C13
+ get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
+ return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>>( block {
+ core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
+ core::int #offset = (#C15).{core::List::[]}(ffi::_abi());
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C12, #C16);
+ @#C13
+ set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
+ return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C8).{core::List::[]}(ffi::_abi()));
+}
+static method main() → dynamic {
+ final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C17).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
+ final self::StructInlineArrayMultiDimensional struct = new self::StructInlineArrayMultiDimensional::#fromTypedDataBase(pointer!);
+ final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0};
+ final ffi::Array<ffi::Array<ffi::Uint8>> subArray = block {
+ ffi::Array<dynamic> #array = array!;
+ core::int #index = 0!;
+ #array.{ffi::Array::_checkIndex}(#index);
+ core::int #singleElementSize = #C18;
+ core::int #elementSize = #singleElementSize.{core::num::*}(#array.{ffi::Array::_nestedDimensionsFlattened});
+ core::int #offset = #elementSize.{core::num::*}(#index);
+ } =>new ffi::Array::_<ffi::Array<ffi::Uint8>>( block {
+ core::Object #typedDataBase = #array.{ffi::Array::_typedDataBase};
+ core::int #offset = #offset;
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Uint8>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), #elementSize), #array.{ffi::Array::_nestedDimensionsFirst}, #array.{ffi::Array::_nestedDimensionsRest});
+ block {
+ ffi::Array<dynamic> #array = array!;
+ core::int #index = 1!;
+ #array.{ffi::Array::_checkIndex}(#index);
+ core::int #singleElementSize = #C18;
+ core::int #elementSize = #singleElementSize.{core::num::*}(#array.{ffi::Array::_nestedDimensionsFlattened});
+ core::int #offset = #elementSize.{core::num::*}(#index);
+ } =>ffi::_memCopy(#array.{ffi::Array::_typedDataBase}, #offset, subArray.{ffi::Array::_typedDataBase}, #C14, #elementSize);
+ (#C17).{ffi::Allocator::free}(pointer);
+}
+
+constants {
+ #C1 = "vm:ffi:struct-fields"
+ #C2 = TypeLiteralConstant(ffi::Uint8)
+ #C3 = 8
+ #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
+ #C5 = <core::Type>[#C4]
+ #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
+ #C7 = core::pragma {name:#C1, options:#C6}
+ #C8 = <core::int*>[#C3, #C3, #C3]
+ #C9 = "vm:entry-point"
+ #C10 = null
+ #C11 = core::pragma {name:#C9, options:#C10}
+ #C12 = 2
+ #C13 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+ #C14 = 0
+ #C15 = <core::int*>[#C14, #C14, #C14]
+ #C16 = <core::int*>[#C12, #C12]
+ #C17 = all::_CallocAllocator {}
+ #C18 = 1
+}
+
+Extra constant evaluation status:
+Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:18:25 -> IntConstant(0)
+Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:19:8 -> IntConstant(1)
+Extra constant evaluation: evaluated: 110, effectively constant: 2
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.textual_outline.expect
new file mode 100644
index 0000000..677a6db
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+import 'dart:ffi';
+import "package:ffi/ffi.dart";
+
+class StructInlineArrayMultiDimensional extends Struct {
+ @Array(2, 2, 2)
+ external Array<Array<Array<Uint8>>> a0;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..b7c36aa
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+import "package:ffi/ffi.dart";
+import 'dart:ffi';
+
+class StructInlineArrayMultiDimensional extends Struct {
+ @Array(2, 2, 2)
+ external Array<Array<Array<Uint8>>> a0;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
new file mode 100644
index 0000000..4f90a79
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:ffi" as ffi;
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+class StructInlineArrayMultiDimensional extends ffi::Struct {
+ synthetic constructor •() → self::StructInlineArrayMultiDimensional
+ : super ffi::Struct::•()
+ ;
+ @#C3
+ external get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>;
+ @#C3
+ external set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void;
+}
+static method main() → dynamic {
+ final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = ffi::AllocatorAlloc|call<self::StructInlineArrayMultiDimensional>(#C4);
+ final self::StructInlineArrayMultiDimensional struct = ffi::StructPointer|get#ref<self::StructInlineArrayMultiDimensional>(pointer);
+ final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0};
+ final ffi::Array<ffi::Array<ffi::Uint8>> subArray = ffi::ArrayArray|[]<ffi::Array<ffi::Uint8>>(array, 0);
+ ffi::ArrayArray|[]=<ffi::Array<ffi::Uint8>>(array, 1, subArray);
+ (#C4).{ffi::Allocator::free}(pointer);
+}
+
+constants {
+ #C1 = 2
+ #C2 = null
+ #C3 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C1, dimension2:#C1, dimension3:#C1, dimension4:#C2, dimension5:#C2, dimensions:#C2}
+ #C4 = all::_CallocAllocator {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.outline.expect
new file mode 100644
index 0000000..4cd5816
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.outline.expect
@@ -0,0 +1,23 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:ffi" as ffi;
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+class StructInlineArrayMultiDimensional extends ffi::Struct {
+ synthetic constructor •() → self::StructInlineArrayMultiDimensional
+ ;
+ @ffi::_ArraySize::•<ffi::NativeType>(2, 2, 2)
+ external get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>;
+ @ffi::_ArraySize::•<ffi::NativeType>(2, 2, 2)
+ external set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void;
+}
+static method main() → dynamic
+ ;
+
+
+Extra constant evaluation status:
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType*>{_ArraySize.dimension1: 2, _ArraySize.dimension2: 2, _ArraySize.dimension3: 2, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType*>{_ArraySize.dimension1: 2, _ArraySize.dimension2: 2, _ArraySize.dimension3: 2, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
+Extra constant evaluation: evaluated: 2, effectively constant: 2
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
new file mode 100644
index 0000000..d65caa1
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:ffi" as ffi;
+import "dart:typed_data" as typ;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+@#C7
+class StructInlineArrayMultiDimensional extends ffi::Struct {
+ static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ synthetic constructor •() → self::StructInlineArrayMultiDimensional
+ : super ffi::Struct::•()
+ ;
+ @#C11
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
+ : super ffi::Struct::_fromPointer(#pointer)
+ ;
+ @#C13
+ get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
+ return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>>( block {
+ core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
+ core::int #offset = (#C15).{core::List::[]}(ffi::_abi());
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C12, #C16);
+ @#C13
+ set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
+ return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C8).{core::List::[]}(ffi::_abi()));
+}
+static method main() → dynamic {
+ final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C17).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
+ final self::StructInlineArrayMultiDimensional struct = new self::StructInlineArrayMultiDimensional::#fromTypedDataBase(pointer!);
+ final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0};
+ final ffi::Array<ffi::Array<ffi::Uint8>> subArray = block {
+ ffi::Array<dynamic> #array = array!;
+ core::int #index = 0!;
+ #array.{ffi::Array::_checkIndex}(#index);
+ core::int #singleElementSize = #C18;
+ core::int #elementSize = #singleElementSize.{core::num::*}(#array.{ffi::Array::_nestedDimensionsFlattened});
+ core::int #offset = #elementSize.{core::num::*}(#index);
+ } =>new ffi::Array::_<ffi::Array<ffi::Uint8>>( block {
+ core::Object #typedDataBase = #array.{ffi::Array::_typedDataBase};
+ core::int #offset = #offset;
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Uint8>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), #elementSize), #array.{ffi::Array::_nestedDimensionsFirst}, #array.{ffi::Array::_nestedDimensionsRest});
+ block {
+ ffi::Array<dynamic> #array = array!;
+ core::int #index = 1!;
+ #array.{ffi::Array::_checkIndex}(#index);
+ core::int #singleElementSize = #C18;
+ core::int #elementSize = #singleElementSize.{core::num::*}(#array.{ffi::Array::_nestedDimensionsFlattened});
+ core::int #offset = #elementSize.{core::num::*}(#index);
+ } =>ffi::_memCopy(#array.{ffi::Array::_typedDataBase}, #offset, subArray.{ffi::Array::_typedDataBase}, #C14, #elementSize);
+ (#C17).{ffi::Allocator::free}(pointer);
+}
+
+constants {
+ #C1 = "vm:ffi:struct-fields"
+ #C2 = TypeLiteralConstant(ffi::Uint8)
+ #C3 = 8
+ #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
+ #C5 = <core::Type>[#C4]
+ #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
+ #C7 = core::pragma {name:#C1, options:#C6}
+ #C8 = <core::int*>[#C3, #C3, #C3]
+ #C9 = "vm:entry-point"
+ #C10 = null
+ #C11 = core::pragma {name:#C9, options:#C10}
+ #C12 = 2
+ #C13 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+ #C14 = 0
+ #C15 = <core::int*>[#C14, #C14, #C14]
+ #C16 = <core::int*>[#C12, #C12]
+ #C17 = all::_CallocAllocator {}
+ #C18 = 1
+}
+
+Extra constant evaluation status:
+Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:18:25 -> IntConstant(0)
+Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:19:8 -> IntConstant(1)
+Extra constant evaluation: evaluated: 110, effectively constant: 2
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/_internal/vm/lib/ffi_patch.dart:156:9)
diff --git a/pkg/front_end/testcases/outline.status b/pkg/front_end/testcases/outline.status
index 63e873f..533b969 100644
--- a/pkg/front_end/testcases/outline.status
+++ b/pkg/front_end/testcases/outline.status
@@ -3,7 +3,10 @@
# BSD-style license that can be found in the LICENSE.md file.
extension_types/simple: ExpectationFileMismatchSerialized
+extension_types/simple_getter_resolution: ExpectationFileMismatchSerialized
extension_types/simple_method_resolution: ExpectationFileMismatchSerialized
+extension_types/simple_operator_resolution: ExpectationFileMismatchSerialized
+extension_types/simple_setter_resolution: ExpectationFileMismatchSerialized
general/abstract_members: TypeCheckError
general/bug30695: TypeCheckError
general/covariant_field: TypeCheckError
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 757cc84..c0d86e6 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -7,7 +7,10 @@
# strong-mode enabled.
extension_types/simple: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_getter_resolution: ExpectationFileMismatchSerialized # Expected.
extension_types/simple_method_resolution: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_operator_resolution: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_setter_resolution: ExpectationFileMismatchSerialized # Expected.
late_lowering/covariant_late_field: TypeCheckError
nnbd/covariant_late_field: TypeCheckError
nnbd/getter_vs_setter_type: TypeCheckError
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 3aabae1..2538568 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -7,7 +7,10 @@
# Kernel files are produced by compiling Dart code via Fasta.
extension_types/simple: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_getter_resolution: ExpectationFileMismatchSerialized # Expected.
extension_types/simple_method_resolution: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_operator_resolution: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_setter_resolution: ExpectationFileMismatchSerialized # Expected.
extensions/call_methods: TypeCheckError
extensions/extension_setter_error: TypeCheckError
extensions/instance_access_of_static: RuntimeError
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index 779ddea..31f6de1 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -11,7 +11,10 @@
# regress/utf_16_le_content.crash: Crash # semi fuzz fails but isn't currently enabled by default.
extension_types/simple: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_getter_resolution: ExpectationFileMismatchSerialized # Expected.
extension_types/simple_method_resolution: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_operator_resolution: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_setter_resolution: ExpectationFileMismatchSerialized # Expected.
extensions/call_methods: TypeCheckError
extensions/extension_setter_error: TypeCheckError
extensions/instance_access_of_static: RuntimeError
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index b19ad56..8c5f5a2 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
- UInt32 formatVersion = 57;
+ UInt32 formatVersion = 58;
Byte[10] shortSdkHash;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index a52a42c..a5bbdc5 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -348,6 +348,52 @@
}
return node as Extension;
}
+
+ bool get isConsistent {
+ NamedNode? node = _node;
+ if (node != null) {
+ if (node.reference != this &&
+ (node is! Field || node.setterReference != this)) {
+ // The reference of a [NamedNode] must point to this reference, or
+ // if the node is a [Field] the setter reference must point to this
+ // reference.
+ return false;
+ }
+ }
+ if (canonicalName != null && canonicalName!.reference != this) {
+ return false;
+ }
+ return true;
+ }
+
+ String getInconsistency() {
+ StringBuffer sb = new StringBuffer();
+ sb.write('Reference ${this} (${hashCode}):');
+ NamedNode? node = _node;
+ if (node != null) {
+ if (node is Field) {
+ if (node.getterReference != this && node.setterReference != this) {
+ sb.write(' _node=${node} (${node.runtimeType}:${node.hashCode})');
+ sb.write(' _node.getterReference='
+ '${node.getterReference} (${node.getterReference.hashCode})');
+ sb.write(' _node.setterReference='
+ '${node.setterReference} (${node.setterReference.hashCode})');
+ }
+ } else {
+ if (node.reference != this) {
+ sb.write(' _node=${node} (${node.runtimeType}:${node.hashCode})');
+ sb.write(' _node.reference='
+ '${node.reference} (${node.reference.hashCode})');
+ }
+ }
+ }
+ if (canonicalName != null && canonicalName!.reference != this) {
+ sb.write(' canonicalName=${canonicalName} (${canonicalName.hashCode})');
+ sb.write(' canonicalName.reference='
+ '${canonicalName!.reference} (${canonicalName!.reference.hashCode})');
+ }
+ return sb.toString();
+ }
}
// ------------------------------------------------------------------------
@@ -13140,10 +13186,7 @@
/// Returns the canonical name of [member], or throws an exception if the
/// member has not been assigned a canonical name yet.
-///
-/// Returns `null` if the member is `null`.
-CanonicalName? getCanonicalNameOfMemberGetter(Member? member) {
- if (member == null) return null;
+CanonicalName getCanonicalNameOfMemberGetter(Member member) {
CanonicalName? canonicalName;
if (member is Field) {
canonicalName = member.getterCanonicalName;
@@ -13158,10 +13201,7 @@
/// Returns the canonical name of [member], or throws an exception if the
/// member has not been assigned a canonical name yet.
-///
-/// Returns `null` if the member is `null`.
-CanonicalName? getCanonicalNameOfMemberSetter(Member? member) {
- if (member == null) return null;
+CanonicalName getCanonicalNameOfMemberSetter(Member member) {
CanonicalName? canonicalName;
if (member is Field) {
canonicalName = member.setterCanonicalName;
@@ -13176,38 +13216,29 @@
/// Returns the canonical name of [class_], or throws an exception if the
/// class has not been assigned a canonical name yet.
-///
-/// Returns `null` if the class is `null`.
-CanonicalName? getCanonicalNameOfClass(Class? class_) {
- if (class_ == null) return null;
+CanonicalName getCanonicalNameOfClass(Class class_) {
if (class_.canonicalName == null) {
throw '$class_ has no canonical name';
}
- return class_.canonicalName;
+ return class_.canonicalName!;
}
/// Returns the canonical name of [extension], or throws an exception if the
/// class has not been assigned a canonical name yet.
-///
-/// Returns `null` if the extension is `null`.
-CanonicalName? getCanonicalNameOfExtension(Extension? extension) {
- if (extension == null) return null;
+CanonicalName getCanonicalNameOfExtension(Extension extension) {
if (extension.canonicalName == null) {
throw '$extension has no canonical name';
}
- return extension.canonicalName;
+ return extension.canonicalName!;
}
/// Returns the canonical name of [library], or throws an exception if the
/// library has not been assigned a canonical name yet.
-///
-/// Returns `null` if the library is `null`.
-CanonicalName? getCanonicalNameOfLibrary(Library? library) {
- if (library == null) return null;
+CanonicalName getCanonicalNameOfLibrary(Library library) {
if (library.canonicalName == null) {
throw '$library has no canonical name';
}
- return library.canonicalName;
+ return library.canonicalName!;
}
/// Murmur-inspired hashing, with a fall-back to Jenkins-inspired hashing when
@@ -13343,14 +13374,11 @@
/// Returns the canonical name of [typedef_], or throws an exception if the
/// typedef has not been assigned a canonical name yet.
-///
-/// Returns `null` if the typedef is `null`.
-CanonicalName? getCanonicalNameOfTypedef(Typedef? typedef_) {
- if (typedef_ == null) return null;
+CanonicalName getCanonicalNameOfTypedef(Typedef typedef_) {
if (typedef_.canonicalName == null) {
throw '$typedef_ has no canonical name';
}
- return typedef_.canonicalName;
+ return typedef_.canonicalName!;
}
/// Annotation describing information which is not part of Dart semantics; in
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 5f9a62f..3260625 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -716,8 +716,6 @@
for (CanonicalName child in parentChildren) {
if (child.name != '@methods' &&
child.name != '@typedefs' &&
- child.name != '@fields' &&
- child.name != '@=fields' &&
child.name != '@getters' &&
child.name != '@setters' &&
child.name != '@factories' &&
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 2c431e2..c3de8d3 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
library kernel.ast_to_binary;
import 'dart:convert';
@@ -20,34 +18,34 @@
/// A [BinaryPrinter] can be used to write one file and must then be
/// discarded.
class BinaryPrinter implements Visitor<void>, BinarySink {
- VariableIndexer _variableIndexer;
- LabelIndexer _labelIndexer;
- SwitchCaseIndexer _switchCaseIndexer;
+ VariableIndexer? _variableIndexer;
+ LabelIndexer? _labelIndexer;
+ SwitchCaseIndexer? _switchCaseIndexer;
final TypeParameterIndexer _typeParameterIndexer = new TypeParameterIndexer();
final StringIndexer stringIndexer;
- ConstantIndexer _constantIndexer;
+ late ConstantIndexer _constantIndexer;
final UriIndexer _sourceUriIndexer = new UriIndexer();
bool _currentlyInNonimplementation = false;
final List<bool> _sourcesFromRealImplementation = <bool>[];
final List<bool> _sourcesUsedInLibrary = <bool>[];
Map<LibraryDependency, int> _libraryDependencyIndex =
<LibraryDependency, int>{};
- NonNullableByDefaultCompiledMode compilationMode;
+ NonNullableByDefaultCompiledMode? compilationMode;
- List<_MetadataSubsection> _metadataSubsections;
+ List<_MetadataSubsection>? _metadataSubsections;
final BufferedSink _mainSink;
final BufferedSink _metadataSink;
final BytesSink _constantsBytesSink;
- BufferedSink _constantsSink;
- BufferedSink _sink;
+ late BufferedSink _constantsSink;
+ late BufferedSink _sink;
final bool includeSources;
final bool includeOffsets;
- final LibraryFilter libraryFilter;
+ final LibraryFilter? libraryFilter;
- List<int> libraryOffsets;
- List<int> classOffsets;
- List<int> procedureOffsets;
+ late List<int> libraryOffsets;
+ late List<int> classOffsets;
+ late List<int> procedureOffsets;
int _binaryOffsetForSourceTable = -1;
int _binaryOffsetForLinkTable = -1;
int _binaryOffsetForMetadataPayloads = -1;
@@ -55,10 +53,10 @@
int _binaryOffsetForStringTable = -1;
int _binaryOffsetForConstantTable = -1;
- List<CanonicalName> _canonicalNameList;
+ late List<CanonicalName> _canonicalNameList;
Set<CanonicalName> _knownCanonicalNameNonRootTops = new Set<CanonicalName>();
- Library _currentLibrary;
+ Library? _currentLibrary;
/// Create a printer that writes to the given [sink].
///
@@ -66,7 +64,7 @@
/// one.
BinaryPrinter(Sink<List<int>> sink,
{this.libraryFilter,
- StringIndexer stringIndexer,
+ StringIndexer? stringIndexer,
this.includeSources = true,
this.includeOffsets = true})
: _mainSink = new BufferedSink(sink),
@@ -83,10 +81,9 @@
}
int _getVariableIndex(VariableDeclaration variable) {
- _variableIndexer ??= new VariableIndexer();
- int index = _variableIndexer[variable];
+ int? index = (_variableIndexer ??= new VariableIndexer())[variable];
assert(index != null, "No index found for ${variable}");
- return index;
+ return index!;
}
void writeByte(int byte) {
@@ -132,7 +129,7 @@
final List<Uint8List> data = <Uint8List>[];
int totalLength = 0;
const int minLength = 1 << 16;
- Uint8List buffer;
+ Uint8List? buffer;
int index = 0;
// Write the end offsets.
@@ -241,7 +238,7 @@
constant.typeArguments.forEach(writeDartType);
writeUInt30(constant.fieldValues.length);
constant.fieldValues.forEach((Reference fieldRef, Constant value) {
- writeNonNullCanonicalNameReference(fieldRef.canonicalName);
+ writeNonNullCanonicalNameReference(fieldRef.canonicalName!);
writeConstantReference(value);
});
} else if (constant is PartialInstantiationConstant) {
@@ -254,7 +251,7 @@
}
} else if (constant is TearOffConstant) {
writeByte(ConstantTag.TearOffConstant);
- writeNonNullCanonicalNameReference(constant.procedure.canonicalName);
+ writeNonNullCanonicalNameReference(constant.procedure.canonicalName!);
} else if (constant is TypeLiteralConstant) {
writeByte(ConstantTag.TypeLiteralConstant);
writeDartType(constant.type);
@@ -273,7 +270,7 @@
}
// Returns the new active file uri.
- void writeUriReference(Uri uri) {
+ void writeUriReference(Uri? uri) {
final int index = _sourceUriIndexer.put(uri);
writeUInt30(index);
if (!_currentlyInNonimplementation) {
@@ -478,7 +475,7 @@
node.accept(this);
}
- void writeOptionalNode(Node node) {
+ void writeOptionalNode(Node? node) {
if (node == null) {
writeByte(Tag.Nothing);
} else {
@@ -487,7 +484,7 @@
}
}
- void writeOptionalFunctionNode(FunctionNode node) {
+ void writeOptionalFunctionNode(FunctionNode? node) {
if (node == null) {
writeByte(Tag.Nothing);
} else {
@@ -505,9 +502,9 @@
_canonicalNameList = <CanonicalName>[];
for (int i = 0; i < component.libraries.length; ++i) {
Library library = component.libraries[i];
- if (libraryFilter == null || libraryFilter(library)) {
- _indexLinkTableInternal(library.canonicalName);
- _knownCanonicalNameNonRootTops.add(library.canonicalName);
+ if (libraryFilter == null || libraryFilter!(library)) {
+ _indexLinkTableInternal(library.canonicalName!);
+ _knownCanonicalNameNonRootTops.add(library.canonicalName!);
}
}
}
@@ -515,7 +512,7 @@
void _indexLinkTableInternal(CanonicalName node) {
node.index = _canonicalNameList.length;
_canonicalNameList.add(node);
- Iterable<CanonicalName> children = node.childrenOrNull;
+ Iterable<CanonicalName>? children = node.childrenOrNull;
if (children != null) {
for (CanonicalName child in children) {
_indexLinkTableInternal(child);
@@ -527,14 +524,15 @@
void computeCanonicalNames(Component component) {
for (int i = 0; i < component.libraries.length; ++i) {
Library library = component.libraries[i];
- if (libraryFilter == null || libraryFilter(library)) {
+ if (libraryFilter == null || libraryFilter!(library)) {
component.computeCanonicalNamesForLibrary(library);
}
}
}
void writeCanonicalNameEntry(CanonicalName node) {
- CanonicalName parent = node.parent;
+ assert(node.isConsistent, node.getInconsistency());
+ CanonicalName parent = node.parent!;
if (parent.isRoot) {
writeUInt30(0);
} else {
@@ -558,9 +556,9 @@
_writeNodeMetadataImpl(component, componentOffset);
}
libraryOffsets = <int>[];
- CanonicalName main = getCanonicalNameOfMemberGetter(component.mainMethod);
- if (main != null) {
- checkCanonicalName(main);
+ Procedure? mainMethod = component.mainMethod;
+ if (mainMethod != null) {
+ checkCanonicalName(getCanonicalNameOfMemberGetter(mainMethod));
}
writeLibraries(component);
writeUriToSource(component.uriToSource);
@@ -573,7 +571,7 @@
List<Library> librariesNew = <Library>[];
for (int i = 0; i < libraries.length; i++) {
Library library = libraries[i];
- if (libraryFilter(library)) librariesNew.add(library);
+ if (libraryFilter!(library)) librariesNew.add(library);
}
libraries = librariesNew;
}
@@ -583,7 +581,7 @@
});
}
- void writeListOfStrings(List<String> strings) {
+ void writeListOfStrings(List<String>? strings) {
writeUInt30(strings?.length ?? 0);
if (strings != null) {
for (int i = 0; i < strings.length; i++) {
@@ -613,9 +611,9 @@
}
void _writeNodeMetadataImpl(Node node, int nodeOffset) {
- for (_MetadataSubsection subsection in _metadataSubsections) {
- final MetadataRepository<Object> repository = subsection.repository;
- final Object value = repository.mapping[node];
+ for (_MetadataSubsection subsection in _metadataSubsections!) {
+ final MetadataRepository<Object?> repository = subsection.repository;
+ final Object? value = repository.mapping[node];
if (value == null) {
continue;
}
@@ -641,7 +639,7 @@
@override
void enterScope(
- {List<TypeParameter> typeParameters,
+ {List<TypeParameter>? typeParameters,
bool memberScope: false,
bool variableScope: false}) {
if (typeParameters != null) {
@@ -652,17 +650,17 @@
}
if (variableScope) {
_variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ _variableIndexer!.pushScope();
}
}
@override
void leaveScope(
- {List<TypeParameter> typeParameters,
+ {List<TypeParameter>? typeParameters,
bool memberScope: false,
bool variableScope: false}) {
if (variableScope) {
- _variableIndexer.popScope();
+ _variableIndexer!.popScope();
}
if (memberScope) {
_variableIndexer = null;
@@ -687,7 +685,7 @@
_metadataSubsections
?.removeWhere((_MetadataSubsection s) => s.metadataMapping.isEmpty);
- if (_metadataSubsections == null || _metadataSubsections.isEmpty) {
+ if (_metadataSubsections == null || _metadataSubsections!.isEmpty) {
_binaryOffsetForMetadataMappings = getBufferOffset();
writeUInt32(0); // Empty section.
return;
@@ -699,7 +697,7 @@
// RList<MetadataMapping> metadataMappings
_binaryOffsetForMetadataMappings = getBufferOffset();
- for (_MetadataSubsection subsection in _metadataSubsections) {
+ for (_MetadataSubsection subsection in _metadataSubsections!) {
// UInt32 tag
writeUInt32(stringIndexer.put(subsection.repository.tag));
@@ -711,14 +709,14 @@
}
writeUInt32(mappingLength ~/ 2);
}
- writeUInt32(_metadataSubsections.length);
+ writeUInt32(_metadataSubsections!.length);
}
/// Write all of some of the libraries of the [component].
void writeLibraries(Component component) {
for (int i = 0; i < component.libraries.length; ++i) {
Library library = component.libraries[i];
- if (libraryFilter == null || libraryFilter(library)) {
+ if (libraryFilter == null || libraryFilter!(library)) {
writeLibraryNode(library);
}
}
@@ -758,10 +756,11 @@
assert(_binaryOffsetForConstantTable >= 0);
writeUInt32(_binaryOffsetForConstantTable);
- CanonicalName main = getCanonicalNameOfMemberGetter(component.mainMethod);
- if (main == null) {
+ Procedure? mainMethod = component.mainMethod;
+ if (mainMethod == null) {
writeUInt32(0);
} else {
+ CanonicalName main = getCanonicalNameOfMemberGetter(mainMethod);
writeUInt32(main.index + 1);
}
assert(component.modeRaw != null, "Component mode not set.");
@@ -782,14 +781,17 @@
int length = _sourceUriIndexer.index.length;
writeUInt32(length);
- List<int> index = new List<int>.filled(length, null);
+ List<int> index = new List<int>.filled(
+ length,
+ // Dummy element value.
+ -1);
// Write data.
int i = 0;
Uint8List buffer = new Uint8List(1 << 16);
- for (Uri uri in _sourceUriIndexer.index.keys) {
+ for (Uri? uri in _sourceUriIndexer.index.keys) {
index[i] = getBufferOffset();
- Source source = uriToSource[uri];
+ Source? source = uriToSource[uri];
if (source == null ||
!(includeSources &&
_sourcesFromRealImplementation.length > i &&
@@ -804,7 +806,7 @@
writeByteList(source.source);
{
- List<int> lineStarts = source.lineStarts;
+ List<int> lineStarts = source.lineStarts!;
writeUInt30(lineStarts.length);
int previousLineStart = 0;
for (int j = 0; j < lineStarts.length; ++j) {
@@ -819,7 +821,7 @@
outputStringViaBuffer(importUriAsString, buffer);
{
- Set<Reference> coverage = source.constantCoverageConstructors;
+ Set<Reference>? coverage = source.constantCoverageConstructors;
if (coverage == null || coverage.isEmpty) {
writeUInt30(0);
} else {
@@ -853,7 +855,7 @@
}
void writeLibraryDependencyReference(LibraryDependency node) {
- int index = _libraryDependencyIndex[node];
+ int? index = _libraryDependencyIndex[node];
if (index == null) {
throw new ArgumentError(
'Reference to library dependency $node out of scope');
@@ -861,17 +863,18 @@
writeUInt30(index);
}
- void writeNullAllowedInstanceMemberReference(Reference reference) {
+ void writeNullAllowedInstanceMemberReference(Reference? reference) {
writeNullAllowedReference(reference);
writeNullAllowedReference(
- getMemberReferenceGetter(reference?.asMember?.memberSignatureOrigin));
+ getMemberReferenceGetter(reference?.asMember.memberSignatureOrigin));
}
- void writeNullAllowedReference(Reference reference) {
+ void writeNullAllowedReference(Reference? reference) {
if (reference == null) {
writeUInt30(0);
} else {
- CanonicalName name = reference.canonicalName;
+ assert(reference.isConsistent, reference.getInconsistency());
+ CanonicalName? name = reference.canonicalName;
if (name == null) {
throw new ArgumentError('Missing canonical name for $reference');
}
@@ -883,14 +886,16 @@
void writeNonNullInstanceMemberReference(Reference reference) {
writeNonNullReference(reference);
writeNullAllowedReference(
- getMemberReferenceGetter(reference.asMember?.memberSignatureOrigin));
+ getMemberReferenceGetter(reference.asMember.memberSignatureOrigin));
}
void writeNonNullReference(Reference reference) {
+ // ignore: unnecessary_null_comparison
if (reference == null) {
throw new ArgumentError('Got null reference');
} else {
- CanonicalName name = reference.canonicalName;
+ assert(reference.isConsistent, reference.getInconsistency());
+ CanonicalName? name = reference.canonicalName;
if (name == null) {
throw new ArgumentError('Missing canonical name for $reference');
}
@@ -901,7 +906,7 @@
void checkCanonicalName(CanonicalName node) {
if (_knownCanonicalNameNonRootTops.contains(node.nonRootTop)) return;
- if (node == null || node.isRoot) return;
+ if (node.isRoot) return;
if (node.index >= 0 && node.index < _canonicalNameList.length) {
CanonicalName claim = _canonicalNameList[node.index];
if (node == claim) {
@@ -909,12 +914,12 @@
return;
}
}
- checkCanonicalName(node.parent);
+ checkCanonicalName(node.parent!);
node.index = _canonicalNameList.length;
_canonicalNameList.add(node);
}
- void writeNullAllowedCanonicalNameReference(CanonicalName name) {
+ void writeNullAllowedCanonicalNameReference(CanonicalName? name) {
if (name == null) {
writeUInt30(0);
} else {
@@ -924,6 +929,7 @@
}
void writeNonNullCanonicalNameReference(CanonicalName name) {
+ // ignore: unnecessary_null_comparison
if (name == null) {
throw new ArgumentError(
'Expected a canonical name to be valid but was `null`.');
@@ -953,6 +959,7 @@
}
void writeClassReference(Class class_) {
+ // ignore: unnecessary_null_comparison
if (class_ == null) {
throw new ArgumentError(
'Expected a class reference to be valid but was `null`.');
@@ -968,7 +975,7 @@
// TODO: Consider a more compressed format for private names within the
// enclosing library.
if (node.isPrivate) {
- writeLibraryReference(node.library);
+ writeLibraryReference(node.library!);
}
}
@@ -1111,7 +1118,7 @@
enterScope(typeParameters: node.typeParameters, variableScope: true);
writeNodeList(node.typeParameters);
- writeNode(node.type);
+ writeNode(node.type!);
enterScope(typeParameters: node.typeParametersOfFunctionType);
writeNodeList(node.typeParametersOfFunctionType);
@@ -1153,7 +1160,7 @@
writeOffset(node.fileEndOffset);
writeByte(node.flags);
- writeStringReference(node.name ?? '');
+ writeStringReference(node.name);
enterScope(memberScope: true);
writeAnnotationList(node.annotations);
@@ -1200,12 +1207,12 @@
writeName(node.name ?? _emptyName);
writeAnnotationList(node.annotations);
- assert(node.function.typeParameters.isEmpty);
- writeFunctionNode(node.function);
+ assert(node.function!.typeParameters.isEmpty);
+ writeFunctionNode(node.function!);
// Parameters are in scope in the initializers.
_variableIndexer ??= new VariableIndexer();
- _variableIndexer.restoreScope(node.function.positionalParameters.length +
- node.function.namedParameters.length);
+ _variableIndexer!.restoreScope(node.function!.positionalParameters.length +
+ node.function!.namedParameters.length);
writeNodeList(node.initializers);
leaveScope(memberScope: true);
@@ -1229,6 +1236,22 @@
if (node.canonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
+ if (node.reference.node != node) {
+ throw new ArgumentError(
+ 'Trying to serialize orphaned procedure reference.\n'
+ 'Orphaned procedure ${node} (${node.runtimeType}:${node.hashCode})\n'
+ 'Linked node ${node.reference.node} '
+ '(${node.reference.node.runtimeType}:'
+ '${node.reference.node.hashCode})');
+ }
+ if (node.canonicalName!.reference?.node != node) {
+ throw new ArgumentError(
+ 'Trying to serialize orphaned procedure canonical name.\n'
+ 'Orphaned procedure ${node} (${node.runtimeType}:${node.hashCode})\n'
+ 'Linked node ${node.canonicalName!.reference?.node} '
+ '(${node.canonicalName!.reference?.node.runtimeType}:'
+ '${node.canonicalName!.reference?.node.hashCode})');
+ }
final bool currentlyInNonimplementationSaved =
_currentlyInNonimplementation;
@@ -1255,7 +1278,7 @@
_currentlyInNonimplementation = currentlyInNonimplementationSaved;
assert(
(node.concreteForwardingStubTarget != null) ||
- !(node.isForwardingStub && node.function.body != null),
+ !(node.isForwardingStub && node.function!.body != null),
"Invalid forwarding stub $node.");
}
@@ -1264,8 +1287,42 @@
if (node.getterCanonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
- if (node.hasSetter && node.setterCanonicalName == null) {
- throw new ArgumentError('Missing canonical name for $node');
+ if (node.getterReference.node != node) {
+ throw new ArgumentError('Trying to serialize orphaned getter reference.\n'
+ 'Orphaned getter ${node} (${node.runtimeType}:${node.hashCode})\n'
+ 'Linked node ${node.getterReference.node} '
+ '(${node.getterReference.node.runtimeType}:'
+ '${node.getterReference.node.hashCode})');
+ }
+ if (node.getterCanonicalName!.reference?.node != node) {
+ throw new ArgumentError(
+ 'Trying to serialize orphaned getter canonical name.\n'
+ 'Orphaned getter ${node} '
+ '(${node.runtimeType}:${node.hashCode})\n'
+ 'Linked node ${node.getterCanonicalName!.reference?.node} '
+ '(${node.getterCanonicalName!.reference?.node.runtimeType}:'
+ '${node.getterCanonicalName!.reference?.node.hashCode})');
+ }
+ if (node.hasSetter) {
+ if (node.setterCanonicalName == null) {
+ throw new ArgumentError('Missing canonical name for $node');
+ }
+ if (node.setterReference?.node != node) {
+ throw new ArgumentError(
+ 'Trying to serialize orphaned setter reference.\n'
+ 'Orphaned setter ${node} (${node.runtimeType}:${node.hashCode})\n'
+ 'Linked node ${node.setterReference?.node}'
+ '(${node.setterReference?.node.runtimeType}:'
+ '${node.setterReference?.node.hashCode})');
+ }
+ if (node.setterCanonicalName!.reference?.node != node) {
+ throw new ArgumentError(
+ 'Trying to serialize orphaned setter canonical name.\n'
+ 'Orphaned setter ${node} (${node.runtimeType}:${node.hashCode})\n'
+ 'Linked node ${node.setterCanonicalName!.reference?.node} '
+ '(${node.setterCanonicalName!.reference?.node.runtimeType}:'
+ '${node.setterCanonicalName!.reference?.node.hashCode})');
+ }
}
enterScope(memberScope: true);
writeByte(Tag.Field);
@@ -1279,7 +1336,7 @@
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeUInt30(node.flags);
- writeName(node.name);
+ writeName(node.name!);
writeAnnotationList(node.annotations);
writeNode(node.type);
writeOptionalNode(node.initializer);
@@ -1301,10 +1358,10 @@
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
- writeName(node.name);
+ writeName(node.name!);
writeAnnotationList(node.annotations);
- writeNonNullReference(node.targetReference);
+ writeNonNullReference(node.targetReference!);
writeNodeList(node.typeArguments);
writeNodeList(node.typeParameters);
writeUInt30(node.positionalParameters.length + node.namedParameters.length);
@@ -1368,9 +1425,9 @@
void visitFunctionNode(FunctionNode node) {
writeByte(Tag.FunctionNode);
enterScope(typeParameters: node.typeParameters, variableScope: true);
- LabelIndexer oldLabels = _labelIndexer;
+ LabelIndexer? oldLabels = _labelIndexer;
_labelIndexer = null;
- SwitchCaseIndexer oldCases = _switchCaseIndexer;
+ SwitchCaseIndexer? oldCases = _switchCaseIndexer;
_switchCaseIndexer = null;
// Note: FunctionNode has no tag.
writeOffset(node.fileOffset);
@@ -1688,7 +1745,6 @@
case LogicalExpressionOperator.OR:
return 1;
}
- throw new ArgumentError('Not a logical operator: $operator');
}
@override
@@ -1911,21 +1967,23 @@
void visitLet(Let node) {
writeByte(Tag.Let);
writeOffset(node.fileOffset);
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeVariableDeclaration(node.variable);
writeNode(node.body);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
void visitBlockExpression(BlockExpression node) {
writeByte(Tag.BlockExpression);
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeNodeList(node.body.statements);
writeNode(node.value);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
@@ -1947,7 +2005,7 @@
writeLibraryDependencyReference(node.import);
}
- writeStatementOrEmpty(Statement node) {
+ writeStatementOrEmpty(Statement? node) {
if (node == null) {
writeByte(Tag.EmptyStatement);
} else {
@@ -1963,22 +2021,24 @@
@override
void visitBlock(Block node) {
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeByte(Tag.Block);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeNodeList(node.statements);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
void visitAssertBlock(AssertBlock node) {
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeByte(Tag.AssertBlock);
writeNodeList(node.statements);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
@@ -1997,13 +2057,11 @@
@override
void visitLabeledStatement(LabeledStatement node) {
- if (_labelIndexer == null) {
- _labelIndexer = new LabelIndexer();
- }
- _labelIndexer.enter(node);
+ LabelIndexer labelIndexer = _labelIndexer ??= new LabelIndexer();
+ labelIndexer.enter(node);
writeByte(Tag.LabeledStatement);
writeNode(node.body);
- _labelIndexer.exit();
+ labelIndexer.exit();
}
@override
@@ -2018,7 +2076,7 @@
void visitBreakStatement(BreakStatement node) {
writeByte(Tag.BreakStatement);
writeOffset(node.fileOffset);
- writeUInt30(_labelIndexer[node.target]);
+ writeUInt30(_labelIndexer![node.target]!);
}
@override
@@ -2039,41 +2097,42 @@
@override
void visitForStatement(ForStatement node) {
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeByte(Tag.ForStatement);
writeOffset(node.fileOffset);
writeVariableDeclarationList(node.variables);
writeOptionalNode(node.condition);
writeNodeList(node.updates);
writeNode(node.body);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
void visitForInStatement(ForInStatement node) {
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeByte(node.isAsync ? Tag.AsyncForInStatement : Tag.ForInStatement);
writeOffset(node.fileOffset);
writeOffset(node.bodyOffset);
writeVariableDeclaration(node.variable);
writeNode(node.iterable);
writeNode(node.body);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
void visitSwitchStatement(SwitchStatement node) {
- if (_switchCaseIndexer == null) {
- _switchCaseIndexer = new SwitchCaseIndexer();
- }
- _switchCaseIndexer.enter(node);
+ SwitchCaseIndexer switchCaseIndexer =
+ _switchCaseIndexer ??= new SwitchCaseIndexer();
+ switchCaseIndexer.enter(node);
writeByte(Tag.SwitchStatement);
writeOffset(node.fileOffset);
writeNode(node.expression);
writeSwitchCaseNodeList(node.cases);
- _switchCaseIndexer.exit(node);
+ switchCaseIndexer.exit(node);
}
@override
@@ -2093,7 +2152,7 @@
void visitContinueSwitchStatement(ContinueSwitchStatement node) {
writeByte(Tag.ContinueSwitchStatement);
writeOffset(node.fileOffset);
- writeUInt30(_switchCaseIndexer[node.target]);
+ writeUInt30(_switchCaseIndexer![node.target]!);
}
@override
@@ -2128,14 +2187,15 @@
@override
void visitCatch(Catch node) {
// Note: there is no tag on Catch.
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.pushScope();
+ VariableIndexer variableIndexer =
+ _variableIndexer ??= new VariableIndexer();
+ variableIndexer.pushScope();
writeOffset(node.fileOffset);
writeNode(node.guard);
writeOptionalVariableDeclaration(node.exception);
writeOptionalVariableDeclaration(node.stackTrace);
writeNode(node.body);
- _variableIndexer.popScope();
+ variableIndexer.popScope();
}
@override
@@ -2173,15 +2233,14 @@
writeOptionalNode(node.initializer);
// Declare the variable after its initializer. It is not in scope in its
// own initializer.
- _variableIndexer ??= new VariableIndexer();
- _variableIndexer.declare(node);
+ (_variableIndexer ??= new VariableIndexer()).declare(node);
}
void writeVariableDeclarationList(List<VariableDeclaration> nodes) {
writeList(nodes, writeVariableDeclaration);
}
- void writeOptionalVariableDeclaration(VariableDeclaration node) {
+ void writeOptionalVariableDeclaration(VariableDeclaration? node) {
if (node == null) {
writeByte(Tag.Nothing);
} else {
@@ -2195,7 +2254,7 @@
writeByte(Tag.FunctionDeclaration);
writeOffset(node.fileOffset);
writeVariableDeclaration(node.variable);
- writeFunctionNode(node.function);
+ writeFunctionNode(node.function!);
}
@override
@@ -2237,11 +2296,10 @@
void visitFutureOrType(FutureOrType node) {
// TODO(dmitryas): Remove special treatment of FutureOr when the VM supports
// the new encoding: just write the tag.
- assert(_knownCanonicalNameNonRootTops != null &&
- _knownCanonicalNameNonRootTops.isNotEmpty);
+ assert(_knownCanonicalNameNonRootTops.isNotEmpty);
CanonicalName root = _knownCanonicalNameNonRootTops.first;
while (!root.isRoot) {
- root = root.parent;
+ root = root.parent!;
}
CanonicalName canonicalNameOfFutureOr =
root.getChild("dart:async").getChild("FutureOr");
@@ -2257,11 +2315,10 @@
void visitNullType(NullType node) {
// TODO(dmitryas): Remove special treatment of Null when the VM supports the
// new encoding: just write the tag.
- assert(_knownCanonicalNameNonRootTops != null &&
- _knownCanonicalNameNonRootTops.isNotEmpty);
+ assert(_knownCanonicalNameNonRootTops.isNotEmpty);
CanonicalName root = _knownCanonicalNameNonRootTops.first;
while (!root.isRoot) {
- root = root.parent;
+ root = root.parent!;
}
CanonicalName canonicalNameOfNull =
root.getChild("dart:core").getChild("Null");
@@ -2279,11 +2336,11 @@
// requires the nullability byte.
if (node.typeArguments.isEmpty) {
writeByte(Tag.SimpleInterfaceType);
- writeByte(_currentLibrary.nonNullable.index);
+ writeByte(_currentLibrary!.nonNullable.index);
writeNonNullReference(node.className);
} else {
writeByte(Tag.InterfaceType);
- writeByte(_currentLibrary.nonNullable.index);
+ writeByte(_currentLibrary!.nonNullable.index);
writeNonNullReference(node.className);
writeNodeList(node.typeArguments);
}
@@ -2349,7 +2406,8 @@
writeByte(node.variance);
}
writeStringReference(node.name ?? '');
- writeNode(node.bound);
+ writeNode(node.bound!);
+ // TODO(johnniwinther): Make this non-optional.
writeOptionalNode(node.defaultType);
}
@@ -2360,7 +2418,7 @@
}
writeByte(Tag.Extension);
writeNonNullCanonicalNameReference(getCanonicalNameOfExtension(node));
- writeStringReference(node.name ?? '');
+ writeStringReference(node.name);
writeUriReference(node.fileUri);
writeOffset(node.fileOffset);
@@ -2376,7 +2434,7 @@
writeName(descriptor.name);
writeByte(descriptor.kind.index);
writeByte(descriptor.flags);
- writeNonNullCanonicalNameReference(descriptor.member.canonicalName);
+ writeNonNullCanonicalNameReference(descriptor.member.canonicalName!);
}
}
@@ -2657,30 +2715,28 @@
typedef bool LibraryFilter(Library _);
class VariableIndexer {
- Map<VariableDeclaration, int> index;
- List<int> scopes;
+ Map<VariableDeclaration, int>? index;
+ List<int>? scopes;
int stackHeight = 0;
void declare(VariableDeclaration node) {
- index ??= <VariableDeclaration, int>{};
- index[node] = stackHeight++;
+ (index ??= <VariableDeclaration, int>{})[node] = stackHeight++;
}
void pushScope() {
- scopes ??= <int>[];
- scopes.add(stackHeight);
+ (scopes ??= <int>[]).add(stackHeight);
}
void popScope() {
- stackHeight = scopes.removeLast();
+ stackHeight = scopes!.removeLast();
}
void restoreScope(int numberOfVariables) {
stackHeight += numberOfVariables;
}
- int operator [](VariableDeclaration node) {
- return index == null ? null : index[node];
+ int? operator [](VariableDeclaration node) {
+ return index == null ? null : index![node];
}
}
@@ -2696,7 +2752,7 @@
--stackHeight;
}
- int operator [](LabeledStatement node) => index[node];
+ int? operator [](LabeledStatement node) => index[node];
}
class SwitchCaseIndexer {
@@ -2713,7 +2769,7 @@
stackHeight -= node.cases.length;
}
- int operator [](SwitchCase node) => index[node];
+ int? operator [](SwitchCase node) => index[node];
}
class ConstantIndexer extends RecursiveResultVisitor {
@@ -2728,7 +2784,7 @@
ConstantIndexer(this.stringIndexer, this._printer);
int put(Constant constant) {
- final int oldOffset = offsets[constant];
+ final int? oldOffset = offsets[constant];
if (oldOffset != null) return oldOffset;
// Traverse DAG in post-order to ensure children have their offsets assigned
@@ -2758,7 +2814,7 @@
put(node);
}
- int operator [](Constant node) => offsets[node];
+ int? operator [](Constant node) => offsets[node];
}
class TypeParameterIndexer {
@@ -2794,7 +2850,7 @@
}
int put(String string) {
- int result = index[string];
+ int? result = index[string];
if (result == null) {
result = index.length;
index[string] = result;
@@ -2802,19 +2858,19 @@
return result;
}
- int operator [](String string) => index[string];
+ int? operator [](String string) => index[string];
}
class UriIndexer {
// Note that the iteration order is important.
- final Map<Uri, int> index = new Map<Uri, int>();
+ final Map<Uri?, int> index = new Map<Uri?, int>();
UriIndexer() {
put(null);
}
- int put(Uri uri) {
- int result = index[uri];
+ int put(Uri? uri) {
+ int? result = index[uri];
if (result == null) {
result = index.length;
index[uri] = result;
@@ -2834,19 +2890,20 @@
int flushedLength = 0;
Float64List _doubleBuffer = new Float64List(1);
- Uint8List _doubleBufferUint8;
+ Uint8List? _doubleBufferUint8;
int get offset => length + flushedLength;
BufferedSink(this._sink);
void addDouble(double d) {
- _doubleBufferUint8 ??= _doubleBuffer.buffer.asUint8List();
+ Uint8List doubleBufferUint8 =
+ _doubleBufferUint8 ??= _doubleBuffer.buffer.asUint8List();
_doubleBuffer[0] = d;
- addByte4(_doubleBufferUint8[0], _doubleBufferUint8[1],
- _doubleBufferUint8[2], _doubleBufferUint8[3]);
- addByte4(_doubleBufferUint8[4], _doubleBufferUint8[5],
- _doubleBufferUint8[6], _doubleBufferUint8[7]);
+ addByte4(doubleBufferUint8[0], doubleBufferUint8[1], doubleBufferUint8[2],
+ doubleBufferUint8[3]);
+ addByte4(doubleBufferUint8[4], doubleBufferUint8[5], doubleBufferUint8[6],
+ doubleBufferUint8[7]);
}
@pragma("vm:prefer-inline")
@@ -2926,7 +2983,7 @@
/// Non-empty metadata subsection.
class _MetadataSubsection {
- final MetadataRepository<Object> repository;
+ final MetadataRepository<Object?> repository;
/// List of (nodeOffset, metadataOffset) pairs.
/// Gradually filled by the writer as writing progresses, which by
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 3b8d1ee..7047f59 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -174,7 +174,7 @@
/// Internal version of kernel binary format.
/// Bump it when making incompatible changes in kernel binaries.
/// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
- static const int BinaryFormatVersion = 57;
+ static const int BinaryFormatVersion = 58;
}
abstract class ConstantTag {
diff --git a/pkg/kernel/lib/canonical_name.dart b/pkg/kernel/lib/canonical_name.dart
index af7344e..cfaa67f 100644
--- a/pkg/kernel/lib/canonical_name.dart
+++ b/pkg/kernel/lib/canonical_name.dart
@@ -30,9 +30,14 @@
/// "@constructors"
/// Qualified name
///
-/// Field:
+/// Field or the implicit getter of a field:
/// Canonical name of enclosing class or library
-/// "@fields"
+/// "@getters"
+/// Qualified name
+///
+/// Implicit setter of a field:
+/// Canonical name of enclosing class or library
+/// "@setters"
/// Qualified name
///
/// Typedef:
@@ -132,11 +137,11 @@
}
CanonicalName getChildFromField(Field field) {
- return getChild('@fields').getChildFromQualifiedName(field.name!);
+ return getChild('@getters').getChildFromQualifiedName(field.name!);
}
CanonicalName getChildFromFieldSetter(Field field) {
- return getChild('@=fields').getChildFromQualifiedName(field.name!);
+ return getChild('@setters').getChildFromQualifiedName(field.name!);
}
CanonicalName getChildFromConstructor(Constructor constructor) {
@@ -151,11 +156,11 @@
}
CanonicalName getChildFromFieldWithName(Name name) {
- return getChild('@fields').getChildFromQualifiedName(name);
+ return getChild('@getters').getChildFromQualifiedName(name);
}
CanonicalName getChildFromFieldSetterWithName(Name name) {
- return getChild('@=fields').getChildFromQualifiedName(name);
+ return getChild('@setters').getChildFromQualifiedName(name);
}
CanonicalName getChildFromTypedef(Typedef typedef_) {
@@ -256,6 +261,22 @@
return reference ??= (new Reference()..canonicalName = this);
}
+ bool get isConsistent {
+ if (reference != null && !reference!.isConsistent) {
+ return false;
+ }
+ return true;
+ }
+
+ String getInconsistency() {
+ StringBuffer sb = new StringBuffer();
+ sb.write('CanonicalName ${this} (${hashCode}):');
+ if (reference != null) {
+ sb.write(' ${reference!.getInconsistency()}');
+ }
+ return sb.toString();
+ }
+
static String getProcedureQualifier(Procedure procedure) {
if (procedure.isGetter) return '@getters';
if (procedure.isSetter) return '@setters';
diff --git a/pkg/kernel/test/convert_field_to_setter_getter.dart b/pkg/kernel/test/convert_field_to_setter_getter.dart
index 8cc17a4..1ae22d8 100644
--- a/pkg/kernel/test/convert_field_to_setter_getter.dart
+++ b/pkg/kernel/test/convert_field_to_setter_getter.dart
@@ -37,12 +37,14 @@
List<int> writtenBytesFieldOriginal = serialize(lib1, lib2);
// Canonical names are now set: Verify that the field is marked as such,
// canonical-name-wise.
- if (field.getterCanonicalName.parent.name != "@fields") {
- throw "Expected @fields parent, but had "
+ String getterCanonicalName = '${field.getterCanonicalName}';
+ if (field.getterCanonicalName.parent.name != "@getters") {
+ throw "Expected @getters parent, but had "
"${field.getterCanonicalName.parent.name}";
}
- if (field.setterCanonicalName.parent.name != "@=fields") {
- throw "Expected @=fields parent, but had "
+ String setterCanonicalName = '${field.setterCanonicalName}';
+ if (field.setterCanonicalName.parent.name != "@setters") {
+ throw "Expected @setters parent, but had "
"${field.setterCanonicalName.parent.name}";
}
@@ -80,10 +82,18 @@
throw "Expected @getters parent, but had "
"${getter.canonicalName.parent.name}";
}
+ if ('${getter.canonicalName}' != getterCanonicalName) {
+ throw "Unexpected getter canonical name. "
+ "Expected $getterCanonicalName, actual ${getter.canonicalName}.";
+ }
if (setter.canonicalName.parent.name != "@setters") {
throw "Expected @setters parent, but had "
"${setter.canonicalName.parent.name}";
}
+ if ('${setter.canonicalName}' != setterCanonicalName) {
+ throw "Unexpected setter canonical name. "
+ "Expected $setterCanonicalName, actual ${setter.canonicalName}.";
+ }
// Replace getter/setter with field.
lib1.procedures.remove(getter);
@@ -101,12 +111,12 @@
List<int> writtenBytesFieldNew = serialize(lib1, lib2);
// Canonical names are now set: Verify that the field is marked as such,
// canonical-name-wise.
- if (fieldReplacement.getterCanonicalName.parent.name != "@fields") {
- throw "Expected @fields parent, but had "
+ if (fieldReplacement.getterCanonicalName.parent.name != "@getters") {
+ throw "Expected @getters parent, but had "
"${fieldReplacement.getterCanonicalName.parent.name}";
}
- if (fieldReplacement.setterCanonicalName.parent.name != "@=fields") {
- throw "Expected @=fields parent, but had "
+ if (fieldReplacement.setterCanonicalName.parent.name != "@setters") {
+ throw "Expected @setters parent, but had "
"${fieldReplacement.setterCanonicalName.parent.name}";
}
diff --git a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
index 0c1c2e4a..df7b9ff 100644
--- a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
+++ b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
@@ -133,7 +133,7 @@
name: '/* suppose top-level: dynamic field; */ field;',
node: new ExpressionStatement(new StaticGet(field)),
expectation: ''
- '(expr (get-static "package:foo/bar.dart::@fields::field"))',
+ '(expr (get-static "package:foo/bar.dart::@getters::field"))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
@@ -151,7 +151,7 @@
name: '/* suppose top-level: dynamic field; */ field;',
node: new ExpressionStatement(new StaticGet(field)),
expectation: ''
- '(expr (get-static "package:foo/bar.dart::@fields::field"))',
+ '(expr (get-static "package:foo/bar.dart::@getters::field"))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
@@ -171,7 +171,7 @@
new ExpressionStatement(new StaticSet(field, new IntLiteral(1))),
expectation: ''
'(expr'
- ' (set-static "package:foo/bar.dart::@=fields::field" (int 1)))',
+ ' (set-static "package:foo/bar.dart::@setters::field" (int 1)))',
makeSerializationState: () => new SerializationState(null),
makeDeserializationState: () =>
new DeserializationState(null, component.root),
diff --git a/pkg/vm/lib/metadata/inferred_type.dart b/pkg/vm/lib/metadata/inferred_type.dart
index 5f0c9af..e88f513 100644
--- a/pkg/vm/lib/metadata/inferred_type.dart
+++ b/pkg/vm/lib/metadata/inferred_type.dart
@@ -114,8 +114,9 @@
void writeToBinary(InferredType metadata, Node node, BinarySink sink) {
// TODO(sjindel/tfa): Implement serialization of type arguments when can use
// them for optimizations.
- sink.writeNullAllowedCanonicalNameReference(
- getCanonicalNameOfClass(metadata.concreteClass));
+ sink.writeNullAllowedCanonicalNameReference(metadata.concreteClass != null
+ ? getCanonicalNameOfClass(metadata.concreteClass)
+ : null);
sink.writeByte(metadata._flags);
if (metadata.constantValue != null) {
sink.writeConstantReference(metadata.constantValue);
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 449b5b0..7f1d707 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -7,13 +7,14 @@
library vm.transformations.ffi;
-import 'package:kernel/ast.dart';
+import 'package:kernel/ast.dart' hide MapEntry;
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart';
import 'package:kernel/library_index.dart' show LibraryIndex;
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
-import 'package:kernel/type_environment.dart' show TypeEnvironment;
+import 'package:kernel/type_environment.dart'
+ show TypeEnvironment, SubtypeCheckMode;
/// Represents the (instantiated) ffi.NativeType.
enum NativeType {
@@ -212,8 +213,14 @@
final Class allocatorClass;
final Class nativeFunctionClass;
final Class opaqueClass;
- final Class cArrayClass;
- final Class cArraySizeClass;
+ final Class arrayClass;
+ final Class arraySizeClass;
+ final Field arraySizeDimension1Field;
+ final Field arraySizeDimension2Field;
+ final Field arraySizeDimension3Field;
+ final Field arraySizeDimension4Field;
+ final Field arraySizeDimension5Field;
+ final Field arraySizeDimensionsField;
final Class pointerClass;
final Class structClass;
final Class ffiStructLayoutClass;
@@ -230,15 +237,23 @@
final Procedure structPointerRef;
final Procedure structPointerElemAt;
final Procedure structArrayElemAt;
+ final Procedure arrayArrayElemAt;
+ final Procedure arrayArrayAssignAt;
final Procedure asFunctionMethod;
final Procedure asFunctionInternal;
final Procedure sizeOfMethod;
final Procedure lookupFunctionMethod;
final Procedure fromFunctionMethod;
final Field addressOfField;
- final Field cArrayTypedDataBaseField;
+ final Field arrayTypedDataBaseField;
+ final Field arraySizeField;
+ final Field arrayNestedDimensionsField;
+ final Procedure arrayCheckIndex;
+ final Field arrayNestedDimensionsFlattened;
+ final Field arrayNestedDimensionsFirst;
+ final Field arrayNestedDimensionsRest;
final Constructor structFromPointer;
- final Constructor cArrayConstructor;
+ final Constructor arrayConstructor;
final Procedure fromAddressInternal;
final Procedure libraryLookupMethod;
final Procedure abiMethod;
@@ -285,8 +300,20 @@
allocatorClass = index.getClass('dart:ffi', 'Allocator'),
nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'),
opaqueClass = index.getClass('dart:ffi', 'Opaque'),
- cArrayClass = index.getClass('dart:ffi', 'Array'),
- cArraySizeClass = index.getClass('dart:ffi', '_ArraySize'),
+ arrayClass = index.getClass('dart:ffi', 'Array'),
+ arraySizeClass = index.getClass('dart:ffi', '_ArraySize'),
+ arraySizeDimension1Field =
+ index.getMember('dart:ffi', '_ArraySize', 'dimension1'),
+ arraySizeDimension2Field =
+ index.getMember('dart:ffi', '_ArraySize', 'dimension2'),
+ arraySizeDimension3Field =
+ index.getMember('dart:ffi', '_ArraySize', 'dimension3'),
+ arraySizeDimension4Field =
+ index.getMember('dart:ffi', '_ArraySize', 'dimension4'),
+ arraySizeDimension5Field =
+ index.getMember('dart:ffi', '_ArraySize', 'dimension5'),
+ arraySizeDimensionsField =
+ index.getMember('dart:ffi', '_ArraySize', 'dimensions'),
pointerClass = index.getClass('dart:ffi', 'Pointer'),
structClass = index.getClass('dart:ffi', 'Struct'),
ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'),
@@ -305,11 +332,21 @@
elementAtMethod = index.getMember('dart:ffi', 'Pointer', 'elementAt'),
addressGetter = index.getMember('dart:ffi', 'Pointer', 'get:address'),
addressOfField = index.getMember('dart:ffi', 'Struct', '_addressOf'),
- cArrayTypedDataBaseField =
+ arrayTypedDataBaseField =
index.getMember('dart:ffi', 'Array', '_typedDataBase'),
+ arraySizeField = index.getMember('dart:ffi', 'Array', '_size'),
+ arrayNestedDimensionsField =
+ index.getMember('dart:ffi', 'Array', '_nestedDimensions'),
+ arrayCheckIndex = index.getMember('dart:ffi', 'Array', '_checkIndex'),
+ arrayNestedDimensionsFlattened =
+ index.getMember('dart:ffi', 'Array', '_nestedDimensionsFlattened'),
+ arrayNestedDimensionsFirst =
+ index.getMember('dart:ffi', 'Array', '_nestedDimensionsFirst'),
+ arrayNestedDimensionsRest =
+ index.getMember('dart:ffi', 'Array', '_nestedDimensionsRest'),
structFromPointer =
index.getMember('dart:ffi', 'Struct', '_fromPointer'),
- cArrayConstructor = index.getMember('dart:ffi', 'Array', '_'),
+ arrayConstructor = index.getMember('dart:ffi', 'Array', '_'),
fromAddressInternal =
index.getTopLevelMember('dart:ffi', '_fromAddress'),
structPointerRef =
@@ -317,6 +354,8 @@
structPointerElemAt =
index.getMember('dart:ffi', 'StructPointer', '[]'),
structArrayElemAt = index.getMember('dart:ffi', 'StructArray', '[]'),
+ arrayArrayElemAt = index.getMember('dart:ffi', 'ArrayArray', '[]'),
+ arrayArrayAssignAt = index.getMember('dart:ffi', 'ArrayArray', '[]='),
asFunctionMethod =
index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'),
asFunctionInternal =
@@ -391,7 +430,8 @@
DartType convertNativeTypeToDartType(DartType nativeType,
{bool allowStructs = false,
bool allowStructItself = false,
- bool allowHandle = false}) {
+ bool allowHandle = false,
+ bool allowInlineArray = false}) {
if (nativeType is! InterfaceType) {
return null;
}
@@ -399,6 +439,12 @@
final Class nativeClass = native.classNode;
final NativeType nativeType_ = getType(nativeClass);
+ if (nativeClass == arrayClass) {
+ if (!allowInlineArray) {
+ return null;
+ }
+ return nativeType;
+ }
if (hierarchy.isSubclassOf(nativeClass, structClass)) {
if (structClass == nativeClass) {
return allowStructItself ? nativeType : null;
@@ -457,18 +503,22 @@
return NativeType.values[index];
}
+ ConstantExpression intListConstantExpression(List<int> values) =>
+ ConstantExpression(
+ ListConstant(InterfaceType(intClass, Nullability.legacy),
+ [for (var v in values) IntConstant(v)]),
+ InterfaceType(listClass, Nullability.legacy,
+ [InterfaceType(intClass, Nullability.legacy)]));
+
/// Expression that queries VM internals at runtime to figure out on which ABI
/// we are.
Expression runtimeBranchOnLayout(Map<Abi, int> values) {
return MethodInvocation(
- ConstantExpression(
- ListConstant(InterfaceType(intClass, Nullability.legacy), [
- IntConstant(values[Abi.wordSize64]),
- IntConstant(values[Abi.wordSize32Align32]),
- IntConstant(values[Abi.wordSize32Align64])
- ]),
- InterfaceType(listClass, Nullability.legacy,
- [InterfaceType(intClass, Nullability.legacy)])),
+ intListConstantExpression([
+ values[Abi.wordSize64],
+ values[Abi.wordSize32Align32],
+ values[Abi.wordSize32Align64]
+ ]),
Name("[]"),
Arguments([StaticInvocation(abiMethod, Arguments([]))]),
listElementAt);
@@ -578,6 +628,102 @@
fileOffset),
coreTypes.objectNonNullableRawType));
}
+
+ bool isPrimitiveType(DartType type) {
+ if (type is InvalidType) {
+ return false;
+ }
+ if (type is NullType) {
+ return false;
+ }
+ if (!env.isSubtypeOf(
+ type,
+ InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
+ Nullability.legacy),
+ SubtypeCheckMode.ignoringNullabilities)) {
+ return false;
+ }
+ if (isPointerType(type)) {
+ return false;
+ }
+ if (type is InterfaceType) {
+ final nativeType = getType(type.classNode);
+ return nativeType != null;
+ }
+ return false;
+ }
+
+ bool isPointerType(DartType type) {
+ if (type is InvalidType) {
+ return false;
+ }
+ if (type is NullType) {
+ return false;
+ }
+ return env.isSubtypeOf(
+ type,
+ InterfaceType(pointerClass, Nullability.legacy, [
+ InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
+ Nullability.legacy)
+ ]),
+ SubtypeCheckMode.ignoringNullabilities);
+ }
+
+ bool isArrayType(DartType type) {
+ if (type is InvalidType) {
+ return false;
+ }
+ if (type is NullType) {
+ return false;
+ }
+ return env.isSubtypeOf(
+ type,
+ InterfaceType(arrayClass, Nullability.legacy, [
+ InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
+ Nullability.legacy)
+ ]),
+ SubtypeCheckMode.ignoringNullabilities);
+ }
+
+ /// Returns the single element type nested type argument of `Array`.
+ ///
+ /// `Array<Array<Array<Int8>>>` -> `Int8`.
+ DartType arraySingleElementType(DartType dartType) {
+ InterfaceType elementType = dartType as InterfaceType;
+ while (elementType.classNode == arrayClass) {
+ elementType = elementType.typeArguments[0] as InterfaceType;
+ }
+ return elementType;
+ }
+
+ /// Returns the number of dimensions of `Array`.
+ ///
+ /// `Array<Array<Array<Int8>>>` -> 3.
+ int arrayDimensions(DartType dartType) {
+ InterfaceType elementType = dartType as InterfaceType;
+ int dimensions = 0;
+ while (elementType.classNode == arrayClass) {
+ elementType = elementType.typeArguments[0] as InterfaceType;
+ dimensions++;
+ }
+ return dimensions;
+ }
+
+ bool isStructSubtype(DartType type) {
+ if (type is InvalidType) {
+ return false;
+ }
+ if (type is NullType) {
+ return false;
+ }
+ if (type is InterfaceType) {
+ if (type.classNode == structClass) {
+ return false;
+ }
+ }
+ return env.isSubtypeOf(type, InterfaceType(structClass, Nullability.legacy),
+ SubtypeCheckMode.ignoringNullabilities);
+ }
}
/// Contains all information collected by _FfiDefinitionTransformer that is
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index 1311f82..4569be7 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -13,10 +13,10 @@
templateFfiFieldNull,
templateFfiFieldCyclic,
templateFfiFieldNoAnnotation,
- templateFfiTypeInvalid,
templateFfiTypeMismatch,
templateFfiFieldInitializer,
templateFfiSizeAnnotation,
+ templateFfiSizeAnnotationDimensions,
templateFfiStructGeneric;
import 'package:kernel/ast.dart' hide MapEntry;
@@ -213,48 +213,6 @@
}
}
- bool _isPointerType(DartType type) {
- if (type is InvalidType) {
- return false;
- }
- return env.isSubtypeOf(
- type,
- InterfaceType(pointerClass, Nullability.legacy, [
- InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
- Nullability.legacy)
- ]),
- SubtypeCheckMode.ignoringNullabilities);
- }
-
- bool _isArrayType(DartType type) {
- if (type is InvalidType) {
- return false;
- }
- return env.isSubtypeOf(
- type,
- InterfaceType(cArrayClass, Nullability.legacy, [
- InterfaceType(nativeTypesClasses[NativeType.kNativeType.index],
- Nullability.legacy)
- ]),
- SubtypeCheckMode.ignoringNullabilities);
- }
-
- bool _isStructSubtype(DartType type) {
- if (type is InvalidType) {
- return false;
- }
- if (type is NullType) {
- return false;
- }
- if (type is InterfaceType) {
- if (type.classNode == structClass) {
- return false;
- }
- }
- return env.isSubtypeOf(type, InterfaceType(structClass, Nullability.legacy),
- SubtypeCheckMode.ignoringNullabilities);
- }
-
/// Returns members of [node] that correspond to struct fields.
///
/// Note that getters and setters that originate from an external field have
@@ -311,9 +269,9 @@
f.fileUri);
// This class is invalid, but continue reporting other errors on it.
success = false;
- } else if (_isPointerType(type) ||
- _isStructSubtype(type) ||
- _isArrayType(type)) {
+ } else if (isPointerType(type) ||
+ isStructSubtype(type) ||
+ isArrayType(type)) {
if (nativeTypeAnnos.length != 0) {
diagnosticReporter.report(
templateFfiFieldNoAnnotation.withArguments(f.name.text),
@@ -323,24 +281,24 @@
// This class is invalid, but continue reporting other errors on it.
success = false;
}
- if (_isStructSubtype(type)) {
+ if (isStructSubtype(type)) {
final clazz = (type as InterfaceType).classNode;
structClassDependencies[node].add(clazz);
- } else if (_isArrayType(type)) {
+ } else if (isArrayType(type)) {
final sizeAnnotations = _getArraySizeAnnotations(f);
if (sizeAnnotations.length == 1) {
- final typeArgument = (type as InterfaceType).typeArguments.single;
- if (_isStructSubtype(typeArgument)) {
- final clazz = (typeArgument as InterfaceType).classNode;
+ final singleElementType = arraySingleElementType(type);
+ if (isStructSubtype(singleElementType)) {
+ final clazz = (singleElementType as InterfaceType).classNode;
structClassDependencies[node].add(clazz);
- } else if (_isArrayType(typeArgument)) {
+ }
+ if (arrayDimensions(type) != sizeAnnotations.single.length) {
diagnosticReporter.report(
- templateFfiTypeInvalid.withArguments(
- typeArgument, currentLibrary.isNonNullableByDefault),
+ templateFfiSizeAnnotationDimensions
+ .withArguments(f.name.text),
f.fileOffset,
f.name.text.length,
f.fileUri);
- success = false;
}
} else {
diagnosticReporter.report(
@@ -446,38 +404,17 @@
final dartType = _structFieldMemberType(m);
NativeTypeCfe type;
- if (_isPointerType(dartType)) {
- type = PointerNativeTypeCfe();
- } else if (_isStructSubtype(dartType)) {
- final clazz = (dartType as InterfaceType).classNode;
- type = structCache[clazz];
- if (emptyStructs.contains(clazz)) {
- diagnosticReporter.report(
- templateFfiEmptyStruct.withArguments(clazz.name),
- m.fileOffset,
- 1,
- m.location.file);
- }
- } else if (_isArrayType(dartType)) {
+ if (isArrayType(dartType)) {
final sizeAnnotations = _getArraySizeAnnotations(m).toList();
if (sizeAnnotations.length == 1) {
- final elementClass =
- ((dartType as InterfaceType).typeArguments[0] as InterfaceType)
- .classNode;
- final elementNativeType =
- _getFieldType(elementClass) ?? NativeType.kStruct;
- final arraySize = sizeAnnotations.single;
- if (elementNativeType == NativeType.kStruct) {
- type = ArrayNativeTypeCfe(structCache[elementClass], arraySize);
- } else if (elementNativeType == NativeType.kPointer) {
- type = ArrayNativeTypeCfe(PointerNativeTypeCfe(), arraySize);
- } else {
- type = ArrayNativeTypeCfe(
- PrimitiveNativeTypeCfe(elementNativeType, elementClass),
- arraySize);
- }
+ final arrayDimensions = sizeAnnotations.single;
+ type = NativeTypeCfe(this, dartType,
+ structCache: structCache, arrayDimensions: arrayDimensions);
}
+ } else if (isPointerType(dartType) || isStructSubtype(dartType)) {
+ type = NativeTypeCfe(this, dartType, structCache: structCache);
} else {
+ // The C type is in the annotation, not the field type itself.
final nativeTypeAnnos = _getNativeTypeAnnotations(m).toList();
if (nativeTypeAnnos.length == 1) {
final clazz = nativeTypeAnnos.first;
@@ -570,17 +507,29 @@
List<Procedure> _generateMethodsForField(Field field, NativeTypeCfe type,
Map<Abi, int> offsets, IndexedClass indexedClass) {
+ // TODO(johnniwinther): Avoid passing [indexedClass]. When compiling
+ // incrementally, [field] should already carry the references from
+ // [indexedClass].
final getterStatement = type.generateGetterStatement(
field.type, field.fileOffset, offsets, this);
+ Reference getterReference =
+ indexedClass?.lookupGetterReference(field.name) ??
+ field.getterReference;
+ assert(getterReference == field.getterReference,
+ "Unexpected getter reference for ${field}, found $getterReference.");
final Procedure getter = Procedure(field.name, ProcedureKind.Getter,
FunctionNode(getterStatement, returnType: field.type),
- fileUri: field.fileUri,
- reference: indexedClass?.lookupGetterReference(field.name))
+ fileUri: field.fileUri, reference: getterReference)
..fileOffset = field.fileOffset
..isNonNullableByDefault = field.isNonNullableByDefault;
Procedure setter = null;
if (!field.isFinal) {
+ Reference setterReference =
+ indexedClass?.lookupSetterReference(field.name) ??
+ field.setterReference;
+ assert(setterReference == field.setterReference,
+ "Unexpected setter reference for ${field}, found $setterReference.");
final VariableDeclaration argument =
VariableDeclaration('#v', type: field.type)
..fileOffset = field.fileOffset;
@@ -592,7 +541,7 @@
FunctionNode(setterStatement,
returnType: VoidType(), positionalParameters: [argument]),
fileUri: field.fileUri,
- reference: indexedClass?.lookupSetterReference(field.name))
+ reference: setterReference)
..fileOffset = field.fileOffset
..isNonNullableByDefault = field.isNonNullableByDefault;
}
@@ -648,13 +597,41 @@
.where((klass) => _getFieldType(klass) != null);
}
- Iterable<int> _getArraySizeAnnotations(Member node) {
+ Iterable<List<int>> _getArraySizeAnnotations(Member node) {
return node.annotations
.whereType<ConstantExpression>()
.map((e) => e.constant)
.whereType<InstanceConstant>()
- .where((e) => e.classNode == cArraySizeClass)
- .map((e) => (e.fieldValues.values.single as IntConstant).value);
+ .where((e) => e.classNode == arraySizeClass)
+ .map(_arraySize);
+ }
+
+ List<int> _arraySize(InstanceConstant constant) {
+ final dimensions =
+ constant.fieldValues[arraySizeDimensionsField.getterReference];
+ if (dimensions != null) {
+ if (dimensions is ListConstant) {
+ final result = dimensions.entries
+ .whereType<IntConstant>()
+ .map((e) => e.value)
+ .toList();
+ assert(result.length > 0);
+ return result;
+ }
+ }
+ final dimensionFields = [
+ arraySizeDimension1Field,
+ arraySizeDimension2Field,
+ arraySizeDimension3Field,
+ arraySizeDimension4Field,
+ arraySizeDimension5Field
+ ];
+ final result = dimensionFields
+ .map((f) => constant.fieldValues[f.getterReference])
+ .whereType<IntConstant>()
+ .map((c) => c.value)
+ .toList();
+ return result;
}
}
@@ -677,6 +654,37 @@
/// This algebraic data structure does not stand on its own but refers
/// intimately to AST nodes such as [Class].
abstract class NativeTypeCfe {
+ factory NativeTypeCfe(FfiTransformer transformer, DartType dartType,
+ {List<int> arrayDimensions,
+ Map<Class, StructNativeTypeCfe> structCache = const {}}) {
+ if (transformer.isPrimitiveType(dartType)) {
+ final clazz = (dartType as InterfaceType).classNode;
+ final nativeType = transformer.getType(clazz);
+ return PrimitiveNativeTypeCfe(nativeType, clazz);
+ }
+ if (transformer.isPointerType(dartType)) {
+ return PointerNativeTypeCfe();
+ }
+ if (transformer.isStructSubtype(dartType)) {
+ final clazz = (dartType as InterfaceType).classNode;
+ if (structCache.containsKey(clazz)) {
+ return structCache[clazz];
+ } else {
+ throw "$clazz not found in structCache";
+ }
+ }
+ if (transformer.isArrayType(dartType)) {
+ if (arrayDimensions == null) {
+ throw "Must have array dimensions for ArrayType";
+ }
+ final elementType = transformer.arraySingleElementType(dartType);
+ final elementCfeType =
+ NativeTypeCfe(transformer, elementType, structCache: structCache);
+ return ArrayNativeTypeCfe.multi(elementCfeType, arrayDimensions);
+ }
+ throw "Invalid type $dartType";
+ }
+
/// The size in bytes per [Abi].
Map<Abi, int> get size;
@@ -947,6 +955,37 @@
ArrayNativeTypeCfe(this.elementType, this.length);
+ factory ArrayNativeTypeCfe.multi(
+ NativeTypeCfe elementType, List<int> dimensions) {
+ if (dimensions.length == 1) {
+ return ArrayNativeTypeCfe(elementType, dimensions.single);
+ }
+ return ArrayNativeTypeCfe(
+ ArrayNativeTypeCfe.multi(elementType, dimensions.sublist(1)),
+ dimensions.first);
+ }
+
+ List<int> get dimensions {
+ final elementType = this.elementType;
+ if (elementType is ArrayNativeTypeCfe) {
+ return [length, ...elementType.dimensions];
+ }
+ return [length];
+ }
+
+ List<int> get nestedDimensions => dimensions.sublist(1);
+
+ int get dimensionsFlattened =>
+ dimensions.fold(1, (accumulator, element) => accumulator * element);
+
+ NativeTypeCfe get singleElementType {
+ final elementType = this.elementType;
+ if (elementType is ArrayNativeTypeCfe) {
+ return elementType.singleElementType;
+ }
+ return elementType;
+ }
+
@override
Map<Abi, int> get size =>
elementType.size.map((abi, size) => MapEntry(abi, size * length));
@@ -954,13 +993,14 @@
@override
Map<Abi, int> get alignment => elementType.alignment;
+ // Note that we flatten multi dimensional arrays.
@override
Constant generateConstant(FfiTransformer transformer) =>
InstanceConstant(transformer.ffiInlineArrayClass.reference, [], {
transformer.ffiInlineArrayElementTypeField.getterReference:
- elementType.generateConstant(transformer),
+ singleElementType.generateConstant(transformer),
transformer.ffiInlineArrayLengthField.getterReference:
- IntConstant(length)
+ IntConstant(dimensionsFlattened)
});
/// Sample output for `Array<Int8> get x =>`:
@@ -976,7 +1016,7 @@
InterfaceType typeArgument =
(dartType as InterfaceType).typeArguments.single as InterfaceType;
return ReturnStatement(ConstructorInvocation(
- transformer.cArrayConstructor,
+ transformer.arrayConstructor,
Arguments([
transformer.typedDataBaseOffset(
PropertyGet(ThisExpression(), transformer.addressOfField.name,
@@ -986,7 +1026,8 @@
transformer.runtimeBranchOnLayout(size),
typeArgument,
fileOffset),
- ConstantExpression(IntConstant(length))
+ ConstantExpression(IntConstant(length)),
+ transformer.intListConstantExpression(nestedDimensions)
], types: [
dartType
]))
@@ -1014,8 +1055,8 @@
transformer.runtimeBranchOnLayout(offsets),
PropertyGet(
VariableGet(argument),
- transformer.cArrayTypedDataBaseField.name,
- transformer.cArrayTypedDataBaseField)
+ transformer.arrayTypedDataBaseField.name,
+ transformer.arrayTypedDataBaseField)
..fileOffset = fileOffset,
ConstantExpression(IntConstant(0)),
transformer.runtimeBranchOnLayout(size),
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 377721f..aad89d8 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -188,6 +188,20 @@
_ensureNativeTypeValid(nativeType, node, allowStructItself: false);
return _replaceRefArray(node);
+ } else if (target == arrayArrayElemAt) {
+ final DartType nativeType = node.arguments.types[0];
+
+ _ensureNativeTypeValid(nativeType, node,
+ allowStructItself: false, allowInlineArray: true);
+
+ return _replaceArrayArrayElemAt(node);
+ } else if (target == arrayArrayAssignAt) {
+ final DartType nativeType = node.arguments.types[0];
+
+ _ensureNativeTypeValid(nativeType, node,
+ allowStructItself: false, allowInlineArray: true);
+
+ return _replaceArrayArrayElemAt(node, setter: true);
} else if (target == sizeOfMethod) {
final DartType nativeType = node.arguments.types[0];
@@ -469,7 +483,7 @@
final typedDataBasePrime = typedDataBaseOffset(
PropertyGet(NullCheck(node.arguments.positional[0]),
- cArrayTypedDataBaseField.name, cArrayTypedDataBaseField),
+ arrayTypedDataBaseField.name, arrayTypedDataBaseField),
MethodInvocation(node.arguments.positional[1], numMultiplication.name,
Arguments([StaticGet(clazz.fields.single)]), numMultiplication),
StaticGet(clazz.fields.single),
@@ -479,6 +493,146 @@
return ConstructorInvocation(constructor, Arguments([typedDataBasePrime]));
}
+ /// Generates an expression that returns a new `Array<dartType>`.
+ ///
+ /// Sample input getter:
+ /// ```
+ /// this<Array<T>>[index]
+ /// ```
+ ///
+ /// Sample output getter:
+ ///
+ /// ```
+ /// Array #array = this!;
+ /// int #index = index!;
+ /// #array._checkIndex(#index);
+ /// int #singleElementSize = _inlineSizeOf<innermost(T)>();
+ /// int #elementSize = #array.nestedDimensionsFlattened * #singleElementSize;
+ /// int #offset = #elementSize * #index;
+ ///
+ /// new Array<T>._(
+ /// typedDataBaseOffset(#array._typedDataBase, #offset, #elementSize),
+ /// #array.nestedDimensionsFirst,
+ /// #array.nestedDimensionsRest
+ /// )
+ /// ```
+ ///
+ /// Sample input setter:
+ /// ```
+ /// this<Array<T>>[index] = value
+ /// ```
+ ///
+ /// Sample output setter:
+ ///
+ /// ```
+ /// Array #array = this!;
+ /// int #index = index!;
+ /// #array._checkIndex(#index);
+ /// int #singleElementSize = _inlineSizeOf<innermost(T)>();
+ /// int #elementSize = #array.nestedDimensionsFlattened * #singleElementSize;
+ /// int #offset = #elementSize * #index;
+ ///
+ /// _memCopy(
+ /// #array._typedDataBase, #offset, value._typedDataBase, 0, #elementSize)
+ /// ```
+ Expression _replaceArrayArrayElemAt(StaticInvocation node,
+ {bool setter: false}) {
+ final dartType = node.arguments.types[0];
+ final elementType = arraySingleElementType(dartType as InterfaceType);
+
+ final arrayVar = VariableDeclaration("#array",
+ initializer: NullCheck(node.arguments.positional[0]),
+ type: InterfaceType(arrayClass, Nullability.nonNullable))
+ ..fileOffset = node.fileOffset;
+ final indexVar = VariableDeclaration("#index",
+ initializer: NullCheck(node.arguments.positional[1]),
+ type: coreTypes.intNonNullableRawType)
+ ..fileOffset = node.fileOffset;
+ final singleElementSizeVar = VariableDeclaration("#singleElementSize",
+ initializer: _inlineSizeOf(elementType),
+ type: coreTypes.intNonNullableRawType)
+ ..fileOffset = node.fileOffset;
+ final elementSizeVar = VariableDeclaration("#elementSize",
+ initializer: MethodInvocation(
+ VariableGet(singleElementSizeVar),
+ numMultiplication.name,
+ Arguments([
+ PropertyGet(
+ VariableGet(arrayVar),
+ arrayNestedDimensionsFlattened.name,
+ arrayNestedDimensionsFlattened)
+ ]),
+ numMultiplication),
+ type: coreTypes.intNonNullableRawType)
+ ..fileOffset = node.fileOffset;
+ final offsetVar = VariableDeclaration("#offset",
+ initializer: MethodInvocation(
+ VariableGet(elementSizeVar),
+ numMultiplication.name,
+ Arguments([
+ VariableGet(indexVar),
+ ]),
+ numMultiplication),
+ type: coreTypes.intNonNullableRawType)
+ ..fileOffset = node.fileOffset;
+
+ final checkIndexAndLocalVars = Block([
+ arrayVar,
+ indexVar,
+ ExpressionStatement(MethodInvocation(
+ VariableGet(arrayVar),
+ arrayCheckIndex.name,
+ Arguments([VariableGet(indexVar)]),
+ arrayCheckIndex)),
+ singleElementSizeVar,
+ elementSizeVar,
+ offsetVar
+ ]);
+
+ if (!setter) {
+ // `[]`
+ return BlockExpression(
+ checkIndexAndLocalVars,
+ ConstructorInvocation(
+ arrayConstructor,
+ Arguments([
+ typedDataBaseOffset(
+ PropertyGet(VariableGet(arrayVar),
+ arrayTypedDataBaseField.name, arrayTypedDataBaseField),
+ VariableGet(offsetVar),
+ VariableGet(elementSizeVar),
+ dartType,
+ node.fileOffset),
+ PropertyGet(
+ VariableGet(arrayVar),
+ arrayNestedDimensionsFirst.name,
+ arrayNestedDimensionsFirst),
+ PropertyGet(VariableGet(arrayVar),
+ arrayNestedDimensionsRest.name, arrayNestedDimensionsRest)
+ ], types: [
+ dartType
+ ])));
+ }
+
+ // `[]=`
+ return BlockExpression(
+ checkIndexAndLocalVars,
+ StaticInvocation(
+ memCopy,
+ Arguments([
+ PropertyGet(VariableGet(arrayVar), arrayTypedDataBaseField.name,
+ arrayTypedDataBaseField)
+ ..fileOffset = node.fileOffset,
+ VariableGet(offsetVar),
+ PropertyGet(node.arguments.positional[2],
+ arrayTypedDataBaseField.name, arrayTypedDataBaseField)
+ ..fileOffset = node.fileOffset,
+ ConstantExpression(IntConstant(0)),
+ VariableGet(elementSizeVar),
+ ]))
+ ..fileOffset = node.fileOffset);
+ }
+
@override
visitMethodInvocation(MethodInvocation node) {
super.visitMethodInvocation(node);
@@ -543,11 +697,14 @@
}
void _ensureNativeTypeValid(DartType nativeType, Expression node,
- {bool allowHandle: false, bool allowStructItself = true}) {
+ {bool allowHandle: false,
+ bool allowStructItself = true,
+ bool allowInlineArray = false}) {
if (!_nativeTypeValid(nativeType,
allowStructs: true,
allowStructItself: allowStructItself,
- allowHandle: allowHandle)) {
+ allowHandle: allowHandle,
+ allowInlineArray: allowInlineArray)) {
diagnosticReporter.report(
templateFfiTypeInvalid.withArguments(
nativeType, currentLibrary.isNonNullableByDefault),
@@ -586,11 +743,13 @@
bool _nativeTypeValid(DartType nativeType,
{bool allowStructs: false,
bool allowStructItself = false,
- bool allowHandle = false}) {
+ bool allowHandle = false,
+ bool allowInlineArray = false}) {
return convertNativeTypeToDartType(nativeType,
allowStructs: allowStructs,
allowStructItself: allowStructItself,
- allowHandle: allowHandle) !=
+ allowHandle: allowHandle,
+ allowInlineArray: allowInlineArray) !=
null;
}
@@ -621,7 +780,7 @@
: null;
}
- if (!nativeTypesClasses.contains(klass) && klass != cArrayClass) {
+ if (!nativeTypesClasses.contains(klass) && klass != arrayClass) {
for (final parent in nativeTypesClasses) {
if (hierarchy.isSubtypeOf(klass, parent)) {
return parent;
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index ac88a41..ff602a2 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -554,7 +554,17 @@
isFieldInitializerReachable(f) &&
mayHaveSideEffects(f.initializer)) ||
(f.isLate && f.isFinal)) ||
- isMemberReferencedFromNativeCode(f);
+ isMemberReferencedFromNativeCode(f) ||
+ _isInstanceFieldOfAllocatedEnum(f);
+
+ /// Preserve instance fields of allocated enums as VM relies on their
+ /// existence. Non-allocated enums are converted into ordinary classes during
+ /// the 2nd pass.
+ bool _isInstanceFieldOfAllocatedEnum(Field node) =>
+ !node.isStatic &&
+ node.enclosingClass != null &&
+ node.enclosingClass.isEnum &&
+ isClassAllocated(node.enclosingClass);
void addClassUsedInType(Class c) {
if (_classesUsedInType.add(c)) {
@@ -687,14 +697,17 @@
positionalParameters: [parameter], returnType: const VoidType())
..fileOffset = field.fileOffset,
isAbstract: isAbstract,
- fileUri: field.fileUri);
+ fileUri: field.fileUri,
+ reference: field.setterReference);
if (!isAbstract) {
_extraMembersWithReachableBody.add(accessor);
}
} else {
accessor = new Procedure(field.name, ProcedureKind.Getter,
new FunctionNode(null, returnType: field.type),
- isAbstract: true, fileUri: field.fileUri);
+ isAbstract: true,
+ fileUri: field.fileUri,
+ reference: field.getterReference);
}
accessor.fileOffset = field.fileOffset;
field.enclosingClass.addProcedure(accessor);
@@ -739,6 +752,12 @@
}
return _removedFields[target] ?? target;
}
+
+ bool isGetterReferenceReused(Field node) =>
+ _gettersForRemovedFields.containsKey(node);
+
+ bool isSetterReferenceReused(Field node) =>
+ _settersForRemovedFields.containsKey(node);
}
/// Visits Dart types and collects all classes and typedefs used in types.
@@ -1246,7 +1265,7 @@
for (Source source in component.uriToSource.values) {
source?.constantCoverageConstructors?.removeWhere((Reference reference) {
Member node = reference.asMember;
- return !shaker.isMemberUsed(node) && !_preserveSpecialMember(node);
+ return !shaker.isMemberUsed(node);
});
}
}
@@ -1282,6 +1301,10 @@
debugPrint('Dropped class ${node.name}');
// Ensure that kernel file writer will not be able to
// write a dangling reference to the deleted class.
+ assert(
+ node.reference.node == node,
+ "Trying to remove canonical name from reference on $node which has "
+ "been repurposed for ${node.reference.node}.");
node.reference.canonicalName = null;
Statistics.classesDropped++;
return removalSentinel; // Remove the class.
@@ -1297,6 +1320,7 @@
node.implementedTypes.clear();
node.typeParameters.clear();
node.isAbstract = true;
+ node.isEnum = false;
// Mixin applications cannot have static members.
assert(node.mixedInType == null);
node.annotations = const <Expression>[];
@@ -1305,6 +1329,7 @@
if (!shaker.isClassAllocated(node)) {
debugPrint('Class ${node.name} converted to abstract');
node.isAbstract = true;
+ node.isEnum = false;
}
node.transformOrRemoveChildren(this);
@@ -1312,19 +1337,41 @@
return node;
}
- /// Preserve instance fields of enums as VM relies on their existence.
- bool _preserveSpecialMember(Member node) =>
- node is Field &&
- !node.isStatic &&
- node.enclosingClass != null &&
- node.enclosingClass.isEnum;
-
@override
Member defaultMember(Member node, TreeNode removalSentinel) {
- if (!shaker.isMemberUsed(node) && !_preserveSpecialMember(node)) {
+ if (!shaker.isMemberUsed(node)) {
// Ensure that kernel file writer will not be able to
// write a dangling reference to the deleted member.
- node.reference.canonicalName = null;
+ if (node is Field) {
+ if (!shaker.fieldMorpher.isGetterReferenceReused(node)) {
+ // The getter reference hasn't be repurposed for another node so we
+ // reset the canonical name to ensure that the getter reference
+ // cannot be serialized.
+ assert(
+ node.getterReference.node == node,
+ "Trying to remove canonical name from getter reference on $node "
+ "which has been repurposed for ${node.getterReference.node}.");
+ node.getterReference.canonicalName = null;
+ }
+ if (node.hasSetter) {
+ if (!shaker.fieldMorpher.isSetterReferenceReused(node)) {
+ // The setter reference hasn't be repurposed for another node so we
+ // reset the canonical name to ensure that the setter reference
+ // cannot be serialized.
+ assert(
+ node.setterReference.node == node,
+ "Trying to remove canonical name from reference on $node which "
+ "has been repurposed for ${node.setterReference.node}.");
+ node.setterReference.canonicalName = null;
+ }
+ }
+ } else {
+ assert(
+ node.reference.node == node,
+ "Trying to remove canonical name from reference on $node which has "
+ "been repurposed for ${node.reference.node}.");
+ node.reference.canonicalName = null;
+ }
Statistics.membersDropped++;
return removalSentinel;
}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/enum_used_as_type.dart b/pkg/vm/testcases/transformations/type_flow/transformer/enum_used_as_type.dart
new file mode 100644
index 0000000..da8fb17
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/enum_used_as_type.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+enum Enum { a }
+
+class Class {
+ int method(Enum e) => e.index;
+}
+
+main() {
+ List list = [];
+ if (list.isNotEmpty) {
+ new Class().method(null as dynamic);
+ }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/enum_used_as_type.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/enum_used_as_type.dart.expect
new file mode 100644
index 0000000..89d9a31
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/enum_used_as_type.dart.expect
@@ -0,0 +1,21 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+abstract class Enum extends core::Object {
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] abstract get index() → core::int*;
+}
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method method() → core::int*
+ return [@vm.inferred-type.metadata=!](#C1).{self::Enum::index};
+}
+static method main() → dynamic {
+ core::List<dynamic>* list = [@vm.inferred-type.metadata=dart.core::_GrowableList<dynamic>] core::_GrowableList::•<dynamic>(0);
+ if([@vm.direct-call.metadata=dart.core::_GrowableList.isNotEmpty] [@vm.inferred-type.metadata=dart.core::bool] list.{core::Iterable::isNotEmpty}) {
+ let final self::Class* #t1 = new self::Class::•() in let final self::Enum* #t2 = _in::unsafeCast<self::Enum*>(_in::unsafeCast<dynamic>(null)) in [@vm.direct-call.metadata=#lib::Class.method] [@vm.inferred-type.metadata=!? (skip check)] #t1.{self::Class::method}();
+ }
+}
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index 220b5c1..a352c54 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -451,6 +451,22 @@
int16_t a2[2];
};
+struct Struct8BytesInlineArrayMultiDimensionalInt {
+ uint8_t a0[2][2][2];
+};
+
+struct Struct32BytesInlineArrayMultiDimensionalInt {
+ uint8_t a0[2][2][2][2][2];
+};
+
+struct Struct64BytesInlineArrayMultiDimensionalInt {
+ uint8_t a0[2][2][2][2][2][2];
+};
+
+struct Struct4BytesInlineArrayMultiDimensionalInt {
+ Struct1ByteInt a0[2][2];
+};
+
// Used for testing structs by value.
// Smallest struct with data.
// 10 struct arguments will exhaust available registers.
@@ -3826,6 +3842,160 @@
}
// Used for testing structs by value.
+// Test multi dimensional inline array struct as argument.
+DART_EXPORT uint32_t PassUint8Struct32BytesInlineArrayMultiDimensionalI(
+ uint8_t a0,
+ Struct32BytesInlineArrayMultiDimensionalInt a1,
+ uint8_t a2,
+ Struct8BytesInlineArrayMultiDimensionalInt a3,
+ uint8_t a4,
+ Struct8BytesInlineArrayMultiDimensionalInt a5,
+ uint8_t a6) {
+ std::cout << "PassUint8Struct32BytesInlineArrayMultiDimensionalI"
+ << "(" << static_cast<int>(a0) << ", ([[[[["
+ << static_cast<int>(a1.a0[0][0][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][0][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[0][0][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][0][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][1][1][1]) << "]]], [[["
+ << static_cast<int>(a1.a0[0][1][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][1][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[0][1][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][1][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][1][1][1]) << "]]]], [[[["
+ << static_cast<int>(a1.a0[1][0][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][0][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[1][0][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][0][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][1][1][1]) << "]]], [[["
+ << static_cast<int>(a1.a0[1][1][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][1][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[1][1][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][1][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][1][1][1]) << "]]]]]), "
+ << static_cast<int>(a2) << ", ([[["
+ << static_cast<int>(a3.a0[0][0][0]) << ", "
+ << static_cast<int>(a3.a0[0][0][1]) << "], ["
+ << static_cast<int>(a3.a0[0][1][0]) << ", "
+ << static_cast<int>(a3.a0[0][1][1]) << "]], [["
+ << static_cast<int>(a3.a0[1][0][0]) << ", "
+ << static_cast<int>(a3.a0[1][0][1]) << "], ["
+ << static_cast<int>(a3.a0[1][1][0]) << ", "
+ << static_cast<int>(a3.a0[1][1][1]) << "]]]), "
+ << static_cast<int>(a4) << ", ([[["
+ << static_cast<int>(a5.a0[0][0][0]) << ", "
+ << static_cast<int>(a5.a0[0][0][1]) << "], ["
+ << static_cast<int>(a5.a0[0][1][0]) << ", "
+ << static_cast<int>(a5.a0[0][1][1]) << "]], [["
+ << static_cast<int>(a5.a0[1][0][0]) << ", "
+ << static_cast<int>(a5.a0[1][0][1]) << "], ["
+ << static_cast<int>(a5.a0[1][1][0]) << ", "
+ << static_cast<int>(a5.a0[1][1][1]) << "]]]), "
+ << static_cast<int>(a6) << ")"
+ << "\n";
+
+ uint32_t result = 0;
+
+ result += a0;
+ result += a1.a0[0][0][0][0][0];
+ result += a1.a0[0][0][0][0][1];
+ result += a1.a0[0][0][0][1][0];
+ result += a1.a0[0][0][0][1][1];
+ result += a1.a0[0][0][1][0][0];
+ result += a1.a0[0][0][1][0][1];
+ result += a1.a0[0][0][1][1][0];
+ result += a1.a0[0][0][1][1][1];
+ result += a1.a0[0][1][0][0][0];
+ result += a1.a0[0][1][0][0][1];
+ result += a1.a0[0][1][0][1][0];
+ result += a1.a0[0][1][0][1][1];
+ result += a1.a0[0][1][1][0][0];
+ result += a1.a0[0][1][1][0][1];
+ result += a1.a0[0][1][1][1][0];
+ result += a1.a0[0][1][1][1][1];
+ result += a1.a0[1][0][0][0][0];
+ result += a1.a0[1][0][0][0][1];
+ result += a1.a0[1][0][0][1][0];
+ result += a1.a0[1][0][0][1][1];
+ result += a1.a0[1][0][1][0][0];
+ result += a1.a0[1][0][1][0][1];
+ result += a1.a0[1][0][1][1][0];
+ result += a1.a0[1][0][1][1][1];
+ result += a1.a0[1][1][0][0][0];
+ result += a1.a0[1][1][0][0][1];
+ result += a1.a0[1][1][0][1][0];
+ result += a1.a0[1][1][0][1][1];
+ result += a1.a0[1][1][1][0][0];
+ result += a1.a0[1][1][1][0][1];
+ result += a1.a0[1][1][1][1][0];
+ result += a1.a0[1][1][1][1][1];
+ result += a2;
+ result += a3.a0[0][0][0];
+ result += a3.a0[0][0][1];
+ result += a3.a0[0][1][0];
+ result += a3.a0[0][1][1];
+ result += a3.a0[1][0][0];
+ result += a3.a0[1][0][1];
+ result += a3.a0[1][1][0];
+ result += a3.a0[1][1][1];
+ result += a4;
+ result += a5.a0[0][0][0];
+ result += a5.a0[0][0][1];
+ result += a5.a0[0][1][0];
+ result += a5.a0[0][1][1];
+ result += a5.a0[1][0][0];
+ result += a5.a0[1][0][1];
+ result += a5.a0[1][1][0];
+ result += a5.a0[1][1][1];
+ result += a6;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test struct in multi dimensional inline array.
+DART_EXPORT uint32_t PassUint8Struct4BytesInlineArrayMultiDimensionalIn(
+ uint8_t a0,
+ Struct4BytesInlineArrayMultiDimensionalInt a1,
+ uint8_t a2) {
+ std::cout << "PassUint8Struct4BytesInlineArrayMultiDimensionalIn"
+ << "(" << static_cast<int>(a0) << ", ([[("
+ << static_cast<int>(a1.a0[0][0].a0) << "), ("
+ << static_cast<int>(a1.a0[0][1].a0) << ")], [("
+ << static_cast<int>(a1.a0[1][0].a0) << "), ("
+ << static_cast<int>(a1.a0[1][1].a0) << ")]]), "
+ << static_cast<int>(a2) << ")"
+ << "\n";
+
+ uint32_t result = 0;
+
+ result += a0;
+ result += a1.a0[0][0].a0;
+ result += a1.a0[0][1].a0;
+ result += a1.a0[1][0].a0;
+ result += a1.a0[1][1].a0;
+ result += a2;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
// Smallest struct with data.
DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
std::cout << "ReturnStruct1ByteInt"
@@ -9997,6 +10167,206 @@
}
// Used for testing structs by value.
+// Test multi dimensional inline array struct as argument.
+DART_EXPORT intptr_t TestPassUint8Struct32BytesInlineArrayMultiDimensionalI(
+ // NOLINTNEXTLINE(whitespace/parens)
+ uint32_t (*f)(uint8_t a0,
+ Struct32BytesInlineArrayMultiDimensionalInt a1,
+ uint8_t a2,
+ Struct8BytesInlineArrayMultiDimensionalInt a3,
+ uint8_t a4,
+ Struct8BytesInlineArrayMultiDimensionalInt a5,
+ uint8_t a6)) {
+ uint8_t a0;
+ Struct32BytesInlineArrayMultiDimensionalInt a1;
+ uint8_t a2;
+ Struct8BytesInlineArrayMultiDimensionalInt a3;
+ uint8_t a4;
+ Struct8BytesInlineArrayMultiDimensionalInt a5;
+ uint8_t a6;
+
+ a0 = 1;
+ a1.a0[0][0][0][0][0] = 2;
+ a1.a0[0][0][0][0][1] = 3;
+ a1.a0[0][0][0][1][0] = 4;
+ a1.a0[0][0][0][1][1] = 5;
+ a1.a0[0][0][1][0][0] = 6;
+ a1.a0[0][0][1][0][1] = 7;
+ a1.a0[0][0][1][1][0] = 8;
+ a1.a0[0][0][1][1][1] = 9;
+ a1.a0[0][1][0][0][0] = 10;
+ a1.a0[0][1][0][0][1] = 11;
+ a1.a0[0][1][0][1][0] = 12;
+ a1.a0[0][1][0][1][1] = 13;
+ a1.a0[0][1][1][0][0] = 14;
+ a1.a0[0][1][1][0][1] = 15;
+ a1.a0[0][1][1][1][0] = 16;
+ a1.a0[0][1][1][1][1] = 17;
+ a1.a0[1][0][0][0][0] = 18;
+ a1.a0[1][0][0][0][1] = 19;
+ a1.a0[1][0][0][1][0] = 20;
+ a1.a0[1][0][0][1][1] = 21;
+ a1.a0[1][0][1][0][0] = 22;
+ a1.a0[1][0][1][0][1] = 23;
+ a1.a0[1][0][1][1][0] = 24;
+ a1.a0[1][0][1][1][1] = 25;
+ a1.a0[1][1][0][0][0] = 26;
+ a1.a0[1][1][0][0][1] = 27;
+ a1.a0[1][1][0][1][0] = 28;
+ a1.a0[1][1][0][1][1] = 29;
+ a1.a0[1][1][1][0][0] = 30;
+ a1.a0[1][1][1][0][1] = 31;
+ a1.a0[1][1][1][1][0] = 32;
+ a1.a0[1][1][1][1][1] = 33;
+ a2 = 34;
+ a3.a0[0][0][0] = 35;
+ a3.a0[0][0][1] = 36;
+ a3.a0[0][1][0] = 37;
+ a3.a0[0][1][1] = 38;
+ a3.a0[1][0][0] = 39;
+ a3.a0[1][0][1] = 40;
+ a3.a0[1][1][0] = 41;
+ a3.a0[1][1][1] = 42;
+ a4 = 43;
+ a5.a0[0][0][0] = 44;
+ a5.a0[0][0][1] = 45;
+ a5.a0[0][1][0] = 46;
+ a5.a0[0][1][1] = 47;
+ a5.a0[1][0][0] = 48;
+ a5.a0[1][0][1] = 49;
+ a5.a0[1][1][0] = 50;
+ a5.a0[1][1][1] = 51;
+ a6 = 52;
+
+ std::cout << "Calling TestPassUint8Struct32BytesInlineArrayMultiDimensionalI("
+ << "(" << static_cast<int>(a0) << ", ([[[[["
+ << static_cast<int>(a1.a0[0][0][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][0][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[0][0][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][0][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][0][1][1][1]) << "]]], [[["
+ << static_cast<int>(a1.a0[0][1][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][1][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[0][1][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[0][1][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[0][1][1][1][1]) << "]]]], [[[["
+ << static_cast<int>(a1.a0[1][0][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][0][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[1][0][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][0][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][0][1][1][1]) << "]]], [[["
+ << static_cast<int>(a1.a0[1][1][0][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][0][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][1][0][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][0][1][1]) << "]], [["
+ << static_cast<int>(a1.a0[1][1][1][0][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][1][0][1]) << "], ["
+ << static_cast<int>(a1.a0[1][1][1][1][0]) << ", "
+ << static_cast<int>(a1.a0[1][1][1][1][1]) << "]]]]]), "
+ << static_cast<int>(a2) << ", ([[["
+ << static_cast<int>(a3.a0[0][0][0]) << ", "
+ << static_cast<int>(a3.a0[0][0][1]) << "], ["
+ << static_cast<int>(a3.a0[0][1][0]) << ", "
+ << static_cast<int>(a3.a0[0][1][1]) << "]], [["
+ << static_cast<int>(a3.a0[1][0][0]) << ", "
+ << static_cast<int>(a3.a0[1][0][1]) << "], ["
+ << static_cast<int>(a3.a0[1][1][0]) << ", "
+ << static_cast<int>(a3.a0[1][1][1]) << "]]]), "
+ << static_cast<int>(a4) << ", ([[["
+ << static_cast<int>(a5.a0[0][0][0]) << ", "
+ << static_cast<int>(a5.a0[0][0][1]) << "], ["
+ << static_cast<int>(a5.a0[0][1][0]) << ", "
+ << static_cast<int>(a5.a0[0][1][1]) << "]], [["
+ << static_cast<int>(a5.a0[1][0][0]) << ", "
+ << static_cast<int>(a5.a0[1][0][1]) << "], ["
+ << static_cast<int>(a5.a0[1][1][0]) << ", "
+ << static_cast<int>(a5.a0[1][1][1]) << "]]]), "
+ << static_cast<int>(a6) << ")"
+ << ")\n";
+
+ uint32_t result = f(a0, a1, a2, a3, a4, a5, a6);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(1378, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test struct in multi dimensional inline array.
+DART_EXPORT intptr_t TestPassUint8Struct4BytesInlineArrayMultiDimensionalIn(
+ // NOLINTNEXTLINE(whitespace/parens)
+ uint32_t (*f)(uint8_t a0,
+ Struct4BytesInlineArrayMultiDimensionalInt a1,
+ uint8_t a2)) {
+ uint8_t a0;
+ Struct4BytesInlineArrayMultiDimensionalInt a1;
+ uint8_t a2;
+
+ a0 = 1;
+ a1.a0[0][0].a0 = 2;
+ a1.a0[0][1].a0 = -3;
+ a1.a0[1][0].a0 = 4;
+ a1.a0[1][1].a0 = -5;
+ a2 = 6;
+
+ std::cout << "Calling TestPassUint8Struct4BytesInlineArrayMultiDimensionalIn("
+ << "(" << static_cast<int>(a0) << ", ([[("
+ << static_cast<int>(a1.a0[0][0].a0) << "), ("
+ << static_cast<int>(a1.a0[0][1].a0) << ")], [("
+ << static_cast<int>(a1.a0[1][0].a0) << "), ("
+ << static_cast<int>(a1.a0[1][1].a0) << ")]]), "
+ << static_cast<int>(a2) << ")"
+ << ")\n";
+
+ uint32_t result = f(a0, a1, a2);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(5, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0 = 42;
+
+ result = f(a0, a1, a2);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0 = 84;
+
+ result = f(a0, a1, a2);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
// Smallest struct with data.
DART_EXPORT intptr_t TestReturnStruct1ByteInt(
// NOLINTNEXTLINE(whitespace/parens)
diff --git a/runtime/tests/vm/dart/regress_45207_test.dart b/runtime/tests/vm/dart/regress_45207_test.dart
new file mode 100644
index 0000000..9f01648
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45207_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=--deterministic --optimization-counter-threshold=100 --deoptimize-on-runtime-call-name-filter=TypeCheck --deoptimize-on-runtime-call-every=1 --max-subtype-cache-entries=0
+
+main() {
+ void nop() {}
+ for (int i = 0; i < 1000; ++i) {
+ if (assertAssignable(nop) != 1) {
+ throw 'broken';
+ }
+ }
+}
+
+@pragma('vm:never-inline')
+int assertAssignable(dynamic a0) {
+ return ensureValidExpressionStack(1, a0 as void Function());
+}
+
+@pragma('vm:never-inline')
+int ensureValidExpressionStack(int b, void Function() a) => b;
diff --git a/runtime/tests/vm/dart_2/regress_45207_test.dart b/runtime/tests/vm/dart_2/regress_45207_test.dart
new file mode 100644
index 0000000..9f01648
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_45207_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=--deterministic --optimization-counter-threshold=100 --deoptimize-on-runtime-call-name-filter=TypeCheck --deoptimize-on-runtime-call-every=1 --max-subtype-cache-entries=0
+
+main() {
+ void nop() {}
+ for (int i = 0; i < 1000; ++i) {
+ if (assertAssignable(nop) != 1) {
+ throw 'broken';
+ }
+ }
+}
+
+@pragma('vm:never-inline')
+int assertAssignable(dynamic a0) {
+ return ensureValidExpressionStack(1, a0 as void Function());
+}
+
+@pragma('vm:never-inline')
+int ensureValidExpressionStack(int b, void Function() a) => b;
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index f5423f3c..20440a1 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -508,7 +508,7 @@
// deoptimization point in optimized code, after call.
const intptr_t deopt_id_after = DeoptId::ToDeoptAfter(deopt_id);
if (is_optimizing()) {
- AddDeoptIndexAtCall(deopt_id_after);
+ AddDeoptIndexAtCall(deopt_id_after, env);
} else {
// Add deoptimization continuation point after the call and before the
// arguments are removed.
@@ -902,14 +902,18 @@
dispatch_table_call_targets_.Add(selector);
}
-CompilerDeoptInfo* FlowGraphCompiler::AddDeoptIndexAtCall(intptr_t deopt_id) {
+CompilerDeoptInfo* FlowGraphCompiler::AddDeoptIndexAtCall(intptr_t deopt_id,
+ Environment* env) {
ASSERT(is_optimizing());
ASSERT(!intrinsic_mode());
ASSERT(!FLAG_precompiled_mode);
+ if (env == nullptr) {
+ env = pending_deoptimization_env_;
+ }
CompilerDeoptInfo* info =
new (zone()) CompilerDeoptInfo(deopt_id, ICData::kDeoptAtCall,
0, // No flags.
- pending_deoptimization_env_);
+ env);
info->set_pc_offset(assembler()->CodeSize());
deopt_infos_.Add(info);
return info;
@@ -2859,7 +2863,8 @@
const InstructionSource& source,
intptr_t deopt_id,
const String& dst_name,
- LocationSummary* locs) {
+ LocationSummary* locs,
+ bool was_licm_hoisted) {
ASSERT(!source.token_pos.IsClassifying());
ASSERT(CheckAssertAssignableTypeTestingABILocations(*locs));
@@ -2890,7 +2895,8 @@
}
}
- GenerateTTSCall(source, deopt_id, type_reg, dst_type, dst_name, locs);
+ GenerateTTSCall(source, deopt_id, type_reg, dst_type, dst_name, locs,
+ was_licm_hoisted);
__ Bind(&done);
}
@@ -2902,7 +2908,8 @@
Register reg_with_type,
const AbstractType& dst_type,
const String& dst_name,
- LocationSummary* locs) {
+ LocationSummary* locs,
+ bool was_licm_hoisted) {
ASSERT(!dst_name.IsNull());
// We use 2 consecutive entries in the pool for the subtype cache and the
// destination name. The second entry, namely [dst_name] seems to be unused,
@@ -2929,7 +2936,22 @@
} else {
GenerateIndirectTTSCall(assembler(), reg_with_type, sub_type_cache_index);
}
- EmitCallsiteMetadata(source, deopt_id, UntaggedPcDescriptors::kOther, locs);
+
+ // Lazy deopt to after the call should not have the inputs to AssertAssignable
+ // because those are poped before doing the call.
+ auto pruned_env = pending_deoptimization_env_;
+ if (pruned_env != nullptr) {
+ // If the AssertAssignable was licm hoisted, a lazy-deopt will not continue
+ // after the TTS call inside the assert assignable. Rather the lazy-deopt
+ // will continue at the instruction it was hoisted above (e.g. continue at a
+ // Goto branch) and will later on re-do the AssertAssignable (again).
+ if (!was_licm_hoisted) {
+ pruned_env = pruned_env->DeepCopy(
+ zone(), pruned_env->Length() - AssertAssignableInstr::kNumInputs);
+ }
+ }
+ EmitCallsiteMetadata(source, deopt_id, UntaggedPcDescriptors::kOther, locs,
+ pruned_env);
}
// Optimize assignable type check by adding inlined tests for:
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index f3f422e..d3420af 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -592,7 +592,8 @@
const InstructionSource& source,
intptr_t deopt_id,
const String& dst_name,
- LocationSummary* locs);
+ LocationSummary* locs,
+ bool was_licm_hoisted);
#if !defined(TARGET_ARCH_IA32)
void GenerateCallerChecksForAssertAssignable(CompileType* receiver_type,
@@ -604,7 +605,8 @@
Register reg_with_type,
const AbstractType& dst_type,
const String& dst_name,
- LocationSummary* locs);
+ LocationSummary* locs,
+ bool was_licm_hoisted);
static void GenerateIndirectTTSCall(compiler::Assembler* assembler,
Register reg_with_type,
@@ -862,7 +864,8 @@
ICData::DeoptReasonId reason,
uint32_t flags = 0);
- CompilerDeoptInfo* AddDeoptIndexAtCall(intptr_t deopt_id);
+ CompilerDeoptInfo* AddDeoptIndexAtCall(intptr_t deopt_id,
+ Environment* env = nullptr);
CompilerDeoptInfo* AddSlowPathDeoptInfo(intptr_t deopt_id, Environment* env);
void AddSlowPathCode(SlowPathCode* slow_path);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index d89ec9a..a4a2c64 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -339,7 +339,8 @@
const InstructionSource& source,
intptr_t deopt_id,
const String& dst_name,
- LocationSummary* locs) {
+ LocationSummary* locs,
+ bool was_licm_hoisted) {
ASSERT(!source.token_pos.IsClassifying());
ASSERT(CheckAssertAssignableTypeTestingABILocations(*locs));
@@ -395,26 +396,33 @@
}
__ Bind(&runtime_call);
- __ PushObject(Object::null_object()); // Make room for the result.
- __ pushl(TypeTestABI::kInstanceReg); // Push the source object.
- // Push the type of the destination.
+
+ // We push the inputs of [AssertAssignable] in the same order as they lie on
+ // the stack in unoptimized code.
+ // That will make the deopt environment we emit as metadata correct and
+ // doesn't need pruning (as in other architectures).
+
+ static_assert(AssertAssignableInstr::kNumInputs == 4,
+ "Expected AssertAssignable to have 4 inputs");
+
+ __ PushRegister(TypeTestABI::kInstanceReg);
if (!dst_type.IsNull()) {
__ PushObject(dst_type);
} else {
- __ pushl(TypeTestABI::kDstTypeReg);
+ __ PushRegister(TypeTestABI::kDstTypeReg);
}
- __ pushl(TypeTestABI::kInstantiatorTypeArgumentsReg);
- __ pushl(TypeTestABI::kFunctionTypeArgumentsReg);
- __ PushObject(dst_name); // Push the name of the destination.
- // Can reuse kInstanceReg as scratch here since it was pushed above.
- __ LoadObject(TypeTestABI::kInstanceReg, test_cache);
- __ pushl(TypeTestABI::kInstanceReg);
- __ PushObject(Smi::ZoneHandle(zone(), Smi::New(kTypeCheckFromInline)));
- GenerateRuntimeCall(source, deopt_id, kTypeCheckRuntimeEntry, 7, locs);
- // Pop the parameters supplied to the runtime entry. The result of the
- // type check runtime call is the checked value.
- __ Drop(7);
- __ popl(TypeTestABI::kInstanceReg);
+ __ PushRegister(TypeTestABI::kInstantiatorTypeArgumentsReg);
+ __ PushRegister(TypeTestABI::kFunctionTypeArgumentsReg);
+
+ // Pass destination name and subtype test reg as register arguments.
+ __ LoadObject(AssertAssignableStubABI::kDstNameReg, dst_name);
+ __ LoadObject(AssertAssignableStubABI::kSubtypeTestReg, test_cache);
+
+ GenerateStubCall(source, StubCode::AssertAssignable(),
+ UntaggedPcDescriptors::kOther, locs, deopt_id);
+
+ __ Drop(AssertAssignableInstr::kNumInputs - 1);
+ __ PopRegister(TypeTestABI::kInstanceReg);
__ Bind(&is_assignable);
}
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 441120c..2759abd 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5387,7 +5387,7 @@
void AssertAssignableInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
compiler->GenerateAssertAssignable(value()->Type(), source(), deopt_id(),
- dst_name(), locs());
+ dst_name(), locs(), licm_hoisted());
ASSERT(locs()->in(kInstancePos).reg() == locs()->out(0).reg());
}
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index a09d32c..38006b2 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -3701,6 +3701,7 @@
kDstTypePos = 1,
kInstantiatorTAVPos = 2,
kFunctionTAVPos = 3,
+ kNumInputs = 4,
};
AssertAssignableInstr(const InstructionSource& source,
@@ -3752,6 +3753,9 @@
virtual bool AttributesEqual(Instruction* other) const { return true; }
+ void set_licm_hoisted(bool value) { licm_hoisted_ = value; }
+ bool licm_hoisted() const { return licm_hoisted_; }
+
virtual Value* RedefinedValue() const;
PRINT_OPERANDS_TO_SUPPORT
@@ -3761,6 +3765,7 @@
const TokenPosition token_pos_;
const String& dst_name_;
const Kind kind_;
+ bool licm_hoisted_ = false;
DISALLOW_COPY_AND_ASSIGN(AssertAssignableInstr);
};
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index d63933e..6ecbbf7 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1336,20 +1336,22 @@
void LICM::Hoist(ForwardInstructionIterator* it,
BlockEntryInstr* pre_header,
Instruction* current) {
- if (current->IsCheckClass()) {
- current->AsCheckClass()->set_licm_hoisted(true);
- } else if (current->IsCheckSmi()) {
- current->AsCheckSmi()->set_licm_hoisted(true);
- } else if (current->IsCheckEitherNonSmi()) {
- current->AsCheckEitherNonSmi()->set_licm_hoisted(true);
- } else if (current->IsCheckArrayBound()) {
+ if (auto check = current->AsCheckClass()) {
+ check->set_licm_hoisted(true);
+ } else if (auto check = current->AsCheckSmi()) {
+ check->set_licm_hoisted(true);
+ } else if (auto check = current->AsCheckEitherNonSmi()) {
+ check->set_licm_hoisted(true);
+ } else if (auto check = current->AsCheckArrayBound()) {
ASSERT(!CompilerState::Current().is_aot()); // speculative in JIT only
- current->AsCheckArrayBound()->set_licm_hoisted(true);
- } else if (current->IsGenericCheckBound()) {
+ check->set_licm_hoisted(true);
+ } else if (auto check = current->AsGenericCheckBound()) {
ASSERT(CompilerState::Current().is_aot()); // non-speculative in AOT only
// Does not deopt, so no need for licm_hoisted flag.
- } else if (current->IsTestCids()) {
- current->AsTestCids()->set_licm_hoisted(true);
+ } else if (auto check = current->AsTestCids()) {
+ check->set_licm_hoisted(true);
+ } else if (auto check = current->AsAssertAssignable()) {
+ check->set_licm_hoisted(true);
}
if (FLAG_trace_optimization) {
THR_Print("Hoisting instruction %s:%" Pd " from B%" Pd " to B%" Pd "\n",
diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc
index f968c45..7c0d3ad 100644
--- a/runtime/vm/compiler/frontend/constant_reader.cc
+++ b/runtime/vm/compiler/frontend/constant_reader.cc
@@ -264,7 +264,8 @@
Field& field = Field::Handle(Z);
Instance& constant = Instance::Handle(Z);
for (intptr_t j = 0; j < number_of_fields; ++j) {
- field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference());
+ field = H.LookupFieldByKernelGetterOrSetter(
+ reader.ReadCanonicalNameReference());
// Recurse into lazily evaluating all "sub" constants
// needed to evaluate the current constant.
const intptr_t entry_offset = reader.ReadUInt();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index e3708c8..d90c132 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -238,7 +238,7 @@
ReadBool();
const NameIndex field_name = ReadCanonicalNameReference();
const Field& field =
- Field::Handle(Z, H.LookupFieldByKernelField(field_name));
+ Field::Handle(Z, H.LookupFieldByKernelGetterOrSetter(field_name));
initializer_fields[i] = &field;
SkipExpression();
continue;
@@ -2181,8 +2181,7 @@
const Function* tearoff_interface_target = &Function::null_function();
const NameIndex itarget_name =
ReadInterfaceMemberNameReference(); // read interface_target_reference.
- if (!H.IsRoot(itarget_name) &&
- (H.IsGetter(itarget_name) || H.IsField(itarget_name))) {
+ if (!H.IsRoot(itarget_name) && H.IsGetter(itarget_name)) {
interface_target = &Function::ZoneHandle(
Z,
H.LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name)));
@@ -2550,10 +2549,11 @@
inferred_type_metadata_helper_.GetInferredType(offset);
NameIndex target = ReadCanonicalNameReference(); // read target_reference.
+ ASSERT(H.IsGetter(target));
- if (H.IsField(target)) {
- const Field& field =
- Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
+ const Field& field = Field::ZoneHandle(
+ Z, H.LookupFieldByKernelGetterOrSetter(target, /*required=*/false));
+ if (!field.IsNull()) {
if (field.is_const()) {
// Since the CFE inlines all references to const variables and fields,
// it never emits a StaticGet of a const field.
@@ -2606,44 +2606,39 @@
if (p != NULL) *p = position;
NameIndex target = ReadCanonicalNameReference(); // read target_reference.
+ ASSERT(H.IsSetter(target));
- if (H.IsField(target)) {
- const Field& field =
- Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
- const Class& owner = Class::Handle(Z, field.Owner());
- const String& setter_name = H.DartSetterName(target);
- const Function& setter =
- Function::ZoneHandle(Z, owner.LookupStaticFunction(setter_name));
- Fragment instructions = BuildExpression(); // read expression.
- if (NeedsDebugStepCheck(stack(), position)) {
- instructions = DebugStepCheck(position) + instructions;
- }
- LocalVariable* variable = MakeTemporary();
- instructions += LoadLocal(variable);
- if (!setter.IsNull() && field.NeedsSetter()) {
- instructions += StaticCall(position, setter, 1, ICData::kStatic);
- instructions += Drop();
- } else {
- instructions += StoreStaticField(position, field);
- }
- return instructions;
- } else {
- ASSERT(H.IsProcedure(target));
+ // Evaluate the expression on the right hand side.
+ Fragment instructions = BuildExpression(); // read expression.
- // Evaluate the expression on the right hand side.
- Fragment instructions = BuildExpression(); // read expression.
+ // Look up the target as a setter first and, if not present, as a field
+ // second. This order is needed to avoid looking up a final field as the
+ // target.
+ const Function& function = Function::ZoneHandle(
+ Z, H.LookupStaticMethodByKernelProcedure(target, /*required=*/false));
+
+ if (!function.IsNull()) {
LocalVariable* variable = MakeTemporary();
// Prepare argument.
instructions += LoadLocal(variable);
// Invoke the setter function.
- const Function& function =
- Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
instructions += StaticCall(position, function, 1, ICData::kStatic);
// Drop the unused result & leave the stored value on the stack.
return instructions + Drop();
+ } else {
+ const Field& field =
+ Field::ZoneHandle(Z, H.LookupFieldByKernelGetterOrSetter(target));
+ ASSERT(!field.NeedsSetter());
+ if (NeedsDebugStepCheck(stack(), position)) {
+ instructions = DebugStepCheck(position) + instructions;
+ }
+ LocalVariable* variable = MakeTemporary();
+ instructions += LoadLocal(variable);
+ instructions += StoreStaticField(position, field);
+ return instructions;
}
}
@@ -2771,8 +2766,7 @@
// TODO(dartbug.com/34497): Once front-end desugars calls via
// fields/getters, filtering of field and getter interface targets here
// can be turned into assertions.
- if (!H.IsRoot(itarget_name) && !H.IsField(itarget_name) &&
- !H.IsGetter(itarget_name)) {
+ if (!H.IsRoot(itarget_name) && !H.IsGetter(itarget_name)) {
interface_target = &Function::ZoneHandle(
Z, H.LookupMethodByMember(itarget_name,
H.DartProcedureName(itarget_name)));
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index d8cacea..ca9ebbf 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -322,7 +322,7 @@
void KernelFingerprintHelper::CalculateGetterNameFingerprint() {
const NameIndex name = ReadCanonicalNameReference();
- if (!H.IsRoot(name) && (H.IsGetter(name) || H.IsField(name))) {
+ if (!H.IsRoot(name) && H.IsGetter(name)) {
BuildHash(H.DartGetterName(name).Hash());
}
ReadCanonicalNameReference(); // read interface_target_origin_reference
@@ -339,7 +339,7 @@
void KernelFingerprintHelper::CalculateMethodNameFingerprint() {
const NameIndex name =
ReadCanonicalNameReference(); // read interface_target_reference.
- if (!H.IsRoot(name) && !H.IsField(name)) {
+ if (!H.IsRoot(name)) {
BuildHash(H.DartProcedureName(name).Hash());
}
ReadCanonicalNameReference(); // read interface_target_origin_reference
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 7324e81..5078854 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -235,22 +235,7 @@
}
bool TranslationHelper::IsMember(NameIndex name) {
- return IsConstructor(name) || IsField(name) || IsProcedure(name);
-}
-
-bool TranslationHelper::IsField(NameIndex name) {
- // Fields with private names have the import URI of the library where they are
- // visible as the parent and the string "@fields" as the parent's parent.
- // Fields with non-private names have the string "@fields' as the parent.
- if (IsRoot(name)) {
- return false;
- }
- NameIndex kind = CanonicalNameParent(name);
- if (IsPrivate(name)) {
- kind = CanonicalNameParent(kind);
- }
- return StringEquals(CanonicalNameString(kind), "@fields") ||
- StringEquals(CanonicalNameString(kind), "@=fields");
+ return IsConstructor(name) || IsProcedure(name);
}
bool TranslationHelper::IsConstructor(NameIndex name) {
@@ -330,7 +315,7 @@
}
NameIndex TranslationHelper::EnclosingName(NameIndex name) {
- ASSERT(IsField(name) || IsConstructor(name) || IsProcedure(name));
+ ASSERT(IsConstructor(name) || IsProcedure(name));
NameIndex enclosing = CanonicalNameParent(CanonicalNameParent(name));
if (IsPrivate(name)) {
enclosing = CanonicalNameParent(enclosing);
@@ -587,8 +572,10 @@
return info_.InsertClass(thread_, name_index_handle_, klass);
}
-FieldPtr TranslationHelper::LookupFieldByKernelField(NameIndex kernel_field) {
- ASSERT(IsField(kernel_field));
+FieldPtr TranslationHelper::LookupFieldByKernelGetterOrSetter(
+ NameIndex kernel_field,
+ bool required) {
+ ASSERT(IsGetter(kernel_field) || IsSetter(kernel_field));
NameIndex enclosing = EnclosingName(kernel_field);
Class& klass = Class::Handle(Z);
@@ -604,12 +591,15 @@
Field& field = Field::Handle(
Z, klass.LookupFieldAllowPrivate(
DartSymbolObfuscate(CanonicalNameString(kernel_field))));
- CheckStaticLookup(field);
+ if (required) {
+ CheckStaticLookup(field);
+ }
return field.ptr();
}
FunctionPtr TranslationHelper::LookupStaticMethodByKernelProcedure(
- NameIndex procedure) {
+ NameIndex procedure,
+ bool required) {
const String& procedure_name = DartProcedureName(procedure);
// The parent is either a library or a class (in which case the procedure is a
@@ -620,7 +610,9 @@
Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing));
Function& function =
Function::Handle(Z, library.LookupFunctionAllowPrivate(procedure_name));
- CheckStaticLookup(function);
+ if (required) {
+ CheckStaticLookup(function);
+ }
return function.ptr();
} else {
ASSERT(IsClass(enclosing));
@@ -629,7 +621,9 @@
ASSERT(error == Error::null());
Function& function = Function::ZoneHandle(
Z, klass.LookupFunctionAllowPrivate(procedure_name));
- CheckStaticLookup(function);
+ if (required) {
+ CheckStaticLookup(function);
+ }
return function.ptr();
}
}
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index b76603a..d5d39fe 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -101,7 +101,6 @@
bool IsLibrary(NameIndex name);
bool IsClass(NameIndex name);
bool IsMember(NameIndex name);
- bool IsField(NameIndex name);
bool IsConstructor(NameIndex name);
bool IsProcedure(NameIndex name);
bool IsMethod(NameIndex name);
@@ -164,8 +163,10 @@
virtual LibraryPtr LookupLibraryByKernelLibrary(NameIndex library);
virtual ClassPtr LookupClassByKernelClass(NameIndex klass);
- FieldPtr LookupFieldByKernelField(NameIndex field);
- FunctionPtr LookupStaticMethodByKernelProcedure(NameIndex procedure);
+ FieldPtr LookupFieldByKernelGetterOrSetter(NameIndex field,
+ bool required = true);
+ FunctionPtr LookupStaticMethodByKernelProcedure(NameIndex procedure,
+ bool required = true);
FunctionPtr LookupConstructorByKernelConstructor(NameIndex constructor);
FunctionPtr LookupConstructorByKernelConstructor(const Class& owner,
NameIndex constructor);
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index c4ed124..3478028 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -179,6 +179,31 @@
__ Ret();
}
+void StubCodeCompiler::GenerateAssertAssignableStub(Assembler* assembler) {
+#if !defined(TARGET_ARCH_IA32)
+ __ Breakpoint();
+#else
+ __ EnterStubFrame();
+ __ PushObject(Object::null_object()); // Make room for the result.
+ __ pushl(Address(
+ EBP, target::kWordSize * AssertAssignableStubABI::kInstanceSlotFromFp));
+ __ pushl(Address(
+ EBP, target::kWordSize * AssertAssignableStubABI::kDstTypeSlotFromFp));
+ __ pushl(Address(
+ EBP,
+ target::kWordSize * AssertAssignableStubABI::kInstantiatorTAVSlotFromFp));
+ __ pushl(Address(EBP, target::kWordSize *
+ AssertAssignableStubABI::kFunctionTAVSlotFromFp));
+ __ PushRegister(AssertAssignableStubABI::kDstNameReg);
+ __ PushRegister(AssertAssignableStubABI::kSubtypeTestReg);
+ __ PushObject(Smi::ZoneHandle(Smi::New(kTypeCheckFromInline)));
+ __ CallRuntime(kTypeCheckRuntimeEntry, /*argument_count=*/7);
+ __ Drop(8);
+ __ LeaveStubFrame();
+ __ Ret();
+#endif
+}
+
void StubCodeCompiler::GenerateInstanceOfStub(Assembler* assembler) {
__ EnterStubFrame();
__ PushObject(NullObject()); // Make room for the result.
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index da4d058..ca05271 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -138,6 +138,17 @@
// (throws if the subtype check fails).
};
+// For calling the ia32-specific AssertAssignableStub
+struct AssertAssignableStubABI {
+ static const Register kDstNameReg = EBX;
+ static const Register kSubtypeTestReg = ECX;
+
+ static const intptr_t kInstanceSlotFromFp = 2 + 3;
+ static const intptr_t kDstTypeSlotFromFp = 2 + 2;
+ static const intptr_t kInstantiatorTAVSlotFromFp = 2 + 1;
+ static const intptr_t kFunctionTAVSlotFromFp = 2 + 0;
+};
+
// ABI for InitStaticFieldStub.
struct InitStaticFieldABI {
static const Register kFieldReg = EAX;
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index c118e0f..59226e1 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -110,6 +110,8 @@
"Deoptimizes we are about to return to Dart code from native entries.") \
C(deoptimize_every, 0, 0, int, 0, \
"Deoptimize on every N stack overflow checks") \
+ P(deoptimize_on_runtime_call_every, int, 0, \
+ "Deoptimize functions on every runtime call.") \
R(disable_alloc_stubs_after_gc, false, bool, false, "Stress testing flag.") \
R(dump_megamorphic_stats, false, bool, false, \
"Dump megamorphic cache statistics") \
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 1c29a5b..6a4d1b1 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
// Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 57;
-static const uint32_t kMaxSupportedKernelFormatVersion = 57;
+static const uint32_t kMinSupportedKernelFormatVersion = 58;
+static const uint32_t kMaxSupportedKernelFormatVersion = 58;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 8fb8e14..4d42361 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -10180,7 +10180,9 @@
// This assertion ensures that the cid seen by the background compiler is
// consistent. So the assertion passes if the field is a clone. It also
// passes if the field is static, because we don't use field guards on
- // static fields.
+ // static fields. It also passes if we're compiling unoptimized
+ // code (in which case the caller might get different answers if it obtains
+ // the guarded cid multiple times).
Thread* thread = Thread::Current();
ASSERT(!thread->IsInsideCompiler() ||
#if !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index ce1222e..f38ceba 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -3653,7 +3653,7 @@
#define DEFINE_ACCESSORS(name, accessor_name) \
void set_##accessor_name(bool value) const { \
- untag()->kind_tag_.UpdateBool<name##Bit>(value); \
+ untag()->kind_tag_.UpdateUnsynchronized<name##Bit>(value); \
} \
bool accessor_name() const { return untag()->kind_tag_.Read<name##Bit>(); }
FOR_EACH_FUNCTION_KIND_BIT(DEFINE_ACCESSORS)
@@ -3661,7 +3661,7 @@
#define DEFINE_ACCESSORS(name, accessor_name) \
void set_##accessor_name(bool value) const { \
- untag()->kind_tag_.UpdateUnsynchronized<name##Bit>(value); \
+ untag()->kind_tag_.UpdateBool<name##Bit>(value); \
} \
bool accessor_name() const { return untag()->kind_tag_.Read<name##Bit>(); }
FOR_EACH_FUNCTION_VOLATILE_KIND_BIT(DEFINE_ACCESSORS)
@@ -4103,7 +4103,8 @@
set_guarded_cid_unsafe(cid);
}
void set_guarded_cid_unsafe(intptr_t cid) const {
- StoreNonPointer(&untag()->guarded_cid_, cid);
+ StoreNonPointer<ClassIdTagType, ClassIdTagType, std::memory_order_relaxed>(
+ &untag()->guarded_cid_, cid);
}
static intptr_t guarded_cid_offset() {
return OFFSET_OF(UntaggedField, guarded_cid_);
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index b3325b0..39d4e2d 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -80,6 +80,10 @@
deoptimize_filter,
NULL,
"Deoptimize in named function on stack overflow checks");
+DEFINE_FLAG(charp,
+ deoptimize_on_runtime_call_name_filter,
+ NULL,
+ "Runtime call name filter for --deoptimize-on-runtime-call-every.");
DEFINE_FLAG(bool,
unopt_monomorphic_calls,
@@ -3205,6 +3209,29 @@
UNREACHABLE();
}
+void OnEveryRuntimeEntryCall(Thread* thread, const char* runtime_call_name) {
+ ASSERT(FLAG_deoptimize_on_runtime_call_every > 0);
+ if (FLAG_precompiled_mode) {
+ return;
+ }
+ if (IsolateGroup::IsSystemIsolateGroup(thread->isolate_group())) {
+ return;
+ }
+ const bool is_deopt_related = strstr(runtime_call_name, "Deoptimize") != 0;
+ if (is_deopt_related) {
+ return;
+ }
+ if (FLAG_deoptimize_on_runtime_call_name_filter != nullptr &&
+ strstr(runtime_call_name, FLAG_deoptimize_on_runtime_call_name_filter) ==
+ 0) {
+ return;
+ }
+ const uint32_t count = thread->IncrementAndGetRuntimeCallCount();
+ if ((count % FLAG_deoptimize_on_runtime_call_every) == 0) {
+ DeoptimizeFunctionsOnStack();
+ }
+}
+
double DartModulo(double left, double right) {
double remainder = fmod_ieee(left, right);
if (remainder == 0.0) {
diff --git a/runtime/vm/runtime_entry.h b/runtime/vm/runtime_entry.h
index c3468ba..3120254 100644
--- a/runtime/vm/runtime_entry.h
+++ b/runtime/vm/runtime_entry.h
@@ -114,6 +114,9 @@
StackZone zone(thread); \
HANDLESCOPE(thread); \
CHECK_SIMULATOR_STACK_OVERFLOW(); \
+ if (FLAG_deoptimize_on_runtime_call_every > 0) { \
+ OnEveryRuntimeEntryCall(thread, "" #name); \
+ } \
DRT_Helper##name(isolate, thread, zone.GetZone(), arguments); \
} \
} \
@@ -160,6 +163,8 @@
const char* DeoptReasonToCString(ICData::DeoptReasonId deopt_reason);
+void OnEveryRuntimeEntryCall(Thread* thread, const char* runtime_call_name);
+
void DeoptimizeAt(Thread* mutator_thread,
const Code& optimized_code,
StackFrame* frame);
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index d99a661..ea8d907 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -86,6 +86,7 @@
V(OneArgUnoptimizedStaticCall) \
V(TwoArgsUnoptimizedStaticCall) \
V(AssertSubtype) \
+ V(AssertAssignable) \
V(TypeIsTopTypeForSubtyping) \
V(TypeIsTopTypeForSubtypingNullSafe) \
V(NullIsAssignableToType) \
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 5aee930..c76464f 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -340,6 +340,8 @@
return ++stack_overflow_count_;
}
+ uint32_t IncrementAndGetRuntimeCallCount() { return ++runtime_call_count_; }
+
static uword stack_overflow_shared_stub_entry_point_offset(bool fpu_regs) {
return fpu_regs
? stack_overflow_shared_with_fpu_regs_entry_point_offset()
@@ -1014,6 +1016,7 @@
uint16_t deferred_interrupts_mask_;
uint16_t deferred_interrupts_;
int32_t stack_overflow_count_;
+ uint32_t runtime_call_count_ = 0;
// Deoptimization of stack frames.
PendingDeopts pending_deopts_;
diff --git a/sdk/lib/_internal/js_runtime/lib/js_number.dart b/sdk/lib/_internal/js_runtime/lib/js_number.dart
index 1d3ad24..d0f0c71 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_number.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_number.dart
@@ -404,9 +404,6 @@
return _shrOtherPositive(other);
}
- num operator >>>(num other) =>
- throw UnimplementedError('int.>>> is not implemented yet');
-
num _shrOtherPositive(num other) {
return JS('num', '#', this) > 0
? _shrBothPositive(other)
@@ -434,6 +431,17 @@
: JS('JSUInt32', r'# >>> #', this, other);
}
+ num operator >>>(num other) {
+ if (other is! num) throw argumentErrorValue(other);
+ if (other < 0) throw argumentErrorValue(other);
+ return _shruOtherPositive(other);
+ }
+
+ num _shruOtherPositive(num other) {
+ if (other > 31) return 0;
+ return JS('JSUInt32', r'# >>> #', this, other);
+ }
+
num operator &(num other) {
if (other is! num) throw argumentErrorValue(other);
return JS('JSUInt32', r'(# & #) >>> 0', this, other);
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index 38dbcda..f9612e6 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -115,7 +115,17 @@
final int _size;
@pragma("vm:entry-point")
- Array._(this._typedDataBase, this._size);
+ final List<int> _nestedDimensions;
+
+ @pragma("vm:entry-point")
+ Array._(this._typedDataBase, this._size, this._nestedDimensions);
+
+ late final int _nestedDimensionsFlattened = _nestedDimensions.fold(
+ 1, (accumulator, element) => accumulator * element);
+
+ late final int _nestedDimensionsFirst = _nestedDimensions.first;
+
+ late final List<int> _nestedDimensionsRest = _nestedDimensions.sublist(1);
_checkIndex(int index) {
if (index < 0 || index >= _size) {
@@ -124,13 +134,35 @@
}
@patch
- const factory Array(int dimension1) = _ArraySize<T>;
+ const factory Array(int dimension1,
+ [int dimension2,
+ int dimension3,
+ int dimension4,
+ int dimension5]) = _ArraySize<T>;
+
+ @patch
+ const factory Array.multi(List<int> dimensions) = _ArraySize<T>.multi;
}
class _ArraySize<T extends NativeType> implements Array<T> {
- final int dimension1;
+ final int? dimension1;
+ final int? dimension2;
+ final int? dimension3;
+ final int? dimension4;
+ final int? dimension5;
- const _ArraySize(this.dimension1);
+ final List<int>? dimensions;
+
+ const _ArraySize(this.dimension1,
+ [this.dimension2, this.dimension3, this.dimension4, this.dimension5])
+ : dimensions = null;
+
+ const _ArraySize.multi(this.dimensions)
+ : dimension1 = null,
+ dimension2 = null,
+ dimension3 = null,
+ dimension4 = null,
+ dimension5 = null;
}
/// Returns an integer encoding the ABI used for size and alignment
@@ -706,6 +738,16 @@
_storePointer(this, _intPtrSize * index, value);
}
+extension ArrayArray<T extends NativeType> on Array<Array<T>> {
+ @patch
+ Array<T> operator [](int index) =>
+ throw "UNREACHABLE: This case should have been rewritten in the CFE.";
+
+ @patch
+ void operator []=(int index, Array<T> value) =>
+ throw "UNREACHABLE: This case should have been rewritten in the CFE.";
+}
+
extension StructArray<T extends Struct> on Array<T> {
@patch
T operator [](int index) {
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 8510065..c1db8ab 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -93,11 +93,30 @@
/// class MyStruct extends Struct {
/// @Array(8)
/// external Array<Uint8> inlineArray;
+ ///
+ /// @Array(2, 2, 2)
+ /// external Array<Array<Array<Uint8>>> threeDimensionalInlineArray;
/// }
/// ```
///
/// Do not invoke in normal code.
- external const factory Array(int dimension1);
+ external const factory Array(int dimension1,
+ [int dimension2, int dimension3, int dimension4, int dimension5]);
+
+ /// Const constructor to specify [Array] dimensions in [Struct]s.
+ ///
+ /// ```
+ /// class MyStruct extends Struct {
+ /// @Array.multi([2, 2, 2])
+ /// external Array<Array<Array<Uint8>>> threeDimensionalInlineArray;
+ ///
+ /// @Array.multi([2, 2, 2, 2, 2, 2, 2, 2])
+ /// external Array<Array<Array<Array<Array<Array<Array<Array<Uint8>>>>>>>> eightDimensionalInlineArray;
+ /// }
+ /// ```
+ ///
+ /// Do not invoke in normal code.
+ external const factory Array.multi(List<int> dimensions);
}
/// Extension on [Pointer] specialized for the type argument [NativeFunction].
@@ -662,6 +681,13 @@
external T operator [](int index);
}
+/// Bounds checking indexing methods on [Array]s of [Array].
+extension ArrayArray<T extends NativeType> on Array<Array<T>> {
+ external Array<T> operator [](int index);
+
+ external void operator []=(int index, Array<T> value);
+}
+
/// Extension to retrieve the native `Dart_Port` from a [SendPort].
extension NativePort on SendPort {
/// The native port of this [SendPort].
diff --git a/sdk/lib/io/process.dart b/sdk/lib/io/process.dart
index a1ccce6..ccb9b44 100644
--- a/sdk/lib/io/process.dart
+++ b/sdk/lib/io/process.dart
@@ -177,7 +177,7 @@
/// main() async {
/// // List all files in the current directory in UNIX-like systems.
/// var result = await Process.run('ls', ['-l']);
-/// print(results.stdout);
+/// print(result.stdout);
/// }
/// ```
/// ## Start a process with the start method
diff --git a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
index 025b2d6..4cdd74b 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -273,6 +273,18 @@
passStructStruct16BytesMixed3x10, 0.0),
passStructStruct16BytesMixed3x10AfterCallback),
CallbackTest.withCheck(
+ "PassUint8Struct32BytesInlineArrayMultiDimensionalI",
+ Pointer.fromFunction<
+ PassUint8Struct32BytesInlineArrayMultiDimensionalIType>(
+ passUint8Struct32BytesInlineArrayMultiDimensionalI, 0),
+ passUint8Struct32BytesInlineArrayMultiDimensionalIAfterCallback),
+ CallbackTest.withCheck(
+ "PassUint8Struct4BytesInlineArrayMultiDimensionalIn",
+ Pointer.fromFunction<
+ PassUint8Struct4BytesInlineArrayMultiDimensionalInType>(
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn, 0),
+ passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -5967,6 +5979,237 @@
Expect.approxEquals(30.0, result);
}
+typedef PassUint8Struct32BytesInlineArrayMultiDimensionalIType
+ = Uint32 Function(
+ Uint8,
+ Struct32BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a0 = 0;
+Struct32BytesInlineArrayMultiDimensionalInt
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1 =
+ Struct32BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a2 = 0;
+Struct8BytesInlineArrayMultiDimensionalInt
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a3 =
+ Struct8BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a4 = 0;
+Struct8BytesInlineArrayMultiDimensionalInt
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a5 =
+ Struct8BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a6 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Struct32BytesInlineArrayMultiDimensionalIResult = 0;
+
+int passUint8Struct32BytesInlineArrayMultiDimensionalICalculateResult() {
+ int result = 0;
+
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a0;
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a2;
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a4;
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a6;
+
+ passUint8Struct32BytesInlineArrayMultiDimensionalIResult = result;
+
+ return result;
+}
+
+/// Test multi dimensional inline array struct as argument.
+int passUint8Struct32BytesInlineArrayMultiDimensionalI(
+ int a0,
+ Struct32BytesInlineArrayMultiDimensionalInt a1,
+ int a2,
+ Struct8BytesInlineArrayMultiDimensionalInt a3,
+ int a4,
+ Struct8BytesInlineArrayMultiDimensionalInt a5,
+ int a6) {
+ print(
+ "passUint8Struct32BytesInlineArrayMultiDimensionalI(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassUint8Struct32BytesInlineArrayMultiDimensionalI throwing on purpose!");
+ }
+
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a0 = a0;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1 = a1;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a2 = a2;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a3 = a3;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a4 = a4;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a5 = a5;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a6 = a6;
+
+ final result =
+ passUint8Struct32BytesInlineArrayMultiDimensionalICalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passUint8Struct32BytesInlineArrayMultiDimensionalIAfterCallback() {
+ final result =
+ passUint8Struct32BytesInlineArrayMultiDimensionalICalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(1378, result);
+}
+
+typedef PassUint8Struct4BytesInlineArrayMultiDimensionalInType = Uint32
+ Function(Uint8, Struct4BytesInlineArrayMultiDimensionalInt, Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Struct4BytesInlineArrayMultiDimensionalIn_a0 = 0;
+Struct4BytesInlineArrayMultiDimensionalInt
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1 =
+ Struct4BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct4BytesInlineArrayMultiDimensionalIn_a2 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Struct4BytesInlineArrayMultiDimensionalInResult = 0;
+
+int passUint8Struct4BytesInlineArrayMultiDimensionalInCalculateResult() {
+ int result = 0;
+
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[0][0].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[0][1].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[1][0].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[1][1].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a2;
+
+ passUint8Struct4BytesInlineArrayMultiDimensionalInResult = result;
+
+ return result;
+}
+
+/// Test struct in multi dimensional inline array.
+int passUint8Struct4BytesInlineArrayMultiDimensionalIn(
+ int a0, Struct4BytesInlineArrayMultiDimensionalInt a1, int a2) {
+ print(
+ "passUint8Struct4BytesInlineArrayMultiDimensionalIn(${a0}, ${a1}, ${a2})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassUint8Struct4BytesInlineArrayMultiDimensionalIn throwing on purpose!");
+ }
+
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a0 = a0;
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1 = a1;
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a2 = a2;
+
+ final result =
+ passUint8Struct4BytesInlineArrayMultiDimensionalInCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback() {
+ final result =
+ passUint8Struct4BytesInlineArrayMultiDimensionalInCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(5, result);
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index e909559..ef59018 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -68,6 +68,8 @@
testPassStructStruct16BytesHomogeneousFloat2x5();
testPassStructStruct32BytesHomogeneousDouble2x5();
testPassStructStruct16BytesMixed3x10();
+ testPassUint8Struct32BytesInlineArrayMultiDimensionalI();
+ testPassUint8Struct4BytesInlineArrayMultiDimensionalIn();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@@ -1043,7 +1045,7 @@
@Array(8)
external Array<Uint8> a0;
- String toString() => "(${[for (var i = 0; i < 8; i += 1) a0[i]]})";
+ String toString() => "(${[for (var i0 = 0; i0 < 8; i0 += 1) a0[i0]]})";
}
class StructInlineArrayIrregular extends Struct {
@@ -1053,14 +1055,14 @@
@Uint8()
external int a1;
- String toString() => "(${[for (var i = 0; i < 2; i += 1) a0[i]]}, ${a1})";
+ String toString() => "(${[for (var i0 = 0; i0 < 2; i0 += 1) a0[i0]]}, ${a1})";
}
class StructInlineArray100Bytes extends Struct {
@Array(100)
external Array<Uint8> a0;
- String toString() => "(${[for (var i = 0; i < 100; i += 1) a0[i]]})";
+ String toString() => "(${[for (var i0 = 0; i0 < 100; i0 += 1) a0[i0]]})";
}
class StructInlineArrayBig extends Struct {
@@ -1074,7 +1076,7 @@
external Array<Uint8> a2;
String toString() =>
- "(${a0}, ${a1}, ${[for (var i = 0; i < 4000; i += 1) a2[i]]})";
+ "(${a0}, ${a1}, ${[for (var i0 = 0; i0 < 4000; i0 += 1) a2[i0]]})";
}
class StructStruct16BytesHomogeneousFloat2 extends Struct {
@@ -1087,7 +1089,7 @@
external double a2;
String toString() =>
- "(${a0}, ${[for (var i = 0; i < 2; i += 1) a1[i]]}, ${a2})";
+ "(${a0}, ${[for (var i0 = 0; i0 < 2; i0 += 1) a1[i0]]}, ${a2})";
}
class StructStruct32BytesHomogeneousDouble2 extends Struct {
@@ -1100,7 +1102,7 @@
external double a2;
String toString() =>
- "(${a0}, ${[for (var i = 0; i < 2; i += 1) a1[i]]}, ${a2})";
+ "(${a0}, ${[for (var i0 = 0; i0 < 2; i0 += 1) a1[i0]]}, ${a2})";
}
class StructStruct16BytesMixed3 extends Struct {
@@ -1112,8 +1114,75 @@
@Array(2)
external Array<Int16> a2;
- String toString() => "(${a0}, ${[for (var i = 0; i < 1; i += 1) a1[i]]}, ${[
- for (var i = 0; i < 2; i += 1) a2[i]
+ String toString() => "(${a0}, ${[
+ for (var i0 = 0; i0 < 1; i0 += 1) a1[i0]
+ ]}, ${[for (var i0 = 0; i0 < 2; i0 += 1) a2[i0]]})";
+}
+
+class Struct8BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array(2, 2, 2)
+ external Array<Array<Array<Uint8>>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [
+ for (var i1 = 0; i1 < 2; i1 += 1)
+ [for (var i2 = 0; i2 < 2; i2 += 1) a0[i0][i1][i2]]
+ ]
+ ]})";
+}
+
+class Struct32BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array(2, 2, 2, 2, 2)
+ external Array<Array<Array<Array<Array<Uint8>>>>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [
+ for (var i1 = 0; i1 < 2; i1 += 1)
+ [
+ for (var i2 = 0; i2 < 2; i2 += 1)
+ [
+ for (var i3 = 0; i3 < 2; i3 += 1)
+ [for (var i4 = 0; i4 < 2; i4 += 1) a0[i0][i1][i2][i3][i4]]
+ ]
+ ]
+ ]
+ ]})";
+}
+
+class Struct64BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array.multi([2, 2, 2, 2, 2, 2])
+ external Array<Array<Array<Array<Array<Array<Uint8>>>>>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [
+ for (var i1 = 0; i1 < 2; i1 += 1)
+ [
+ for (var i2 = 0; i2 < 2; i2 += 1)
+ [
+ for (var i3 = 0; i3 < 2; i3 += 1)
+ [
+ for (var i4 = 0; i4 < 2; i4 += 1)
+ [
+ for (var i5 = 0; i5 < 2; i5 += 1)
+ a0[i0][i1][i2][i3][i4][i5]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]})";
+}
+
+class Struct4BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array(2, 2)
+ external Array<Array<Struct1ByteInt>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [for (var i1 = 0; i1 < 2; i1 += 1) a0[i0][i1]]
]})";
}
@@ -5227,6 +5296,133 @@
calloc.free(a9Pointer);
}
+final passUint8Struct32BytesInlineArrayMultiDimensionalI =
+ ffiTestFunctions.lookupFunction<
+ Uint32 Function(
+ Uint8,
+ Struct32BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8),
+ int Function(
+ int,
+ Struct32BytesInlineArrayMultiDimensionalInt,
+ int,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ int,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ int)>("PassUint8Struct32BytesInlineArrayMultiDimensionalI");
+
+/// Test multi dimensional inline array struct as argument.
+void testPassUint8Struct32BytesInlineArrayMultiDimensionalI() {
+ int a0;
+ final a1Pointer = calloc<Struct32BytesInlineArrayMultiDimensionalInt>();
+ final Struct32BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref;
+ int a2;
+ final a3Pointer = calloc<Struct8BytesInlineArrayMultiDimensionalInt>();
+ final Struct8BytesInlineArrayMultiDimensionalInt a3 = a3Pointer.ref;
+ int a4;
+ final a5Pointer = calloc<Struct8BytesInlineArrayMultiDimensionalInt>();
+ final Struct8BytesInlineArrayMultiDimensionalInt a5 = a5Pointer.ref;
+ int a6;
+
+ a0 = 1;
+ a1.a0[0][0][0][0][0] = 2;
+ a1.a0[0][0][0][0][1] = 3;
+ a1.a0[0][0][0][1][0] = 4;
+ a1.a0[0][0][0][1][1] = 5;
+ a1.a0[0][0][1][0][0] = 6;
+ a1.a0[0][0][1][0][1] = 7;
+ a1.a0[0][0][1][1][0] = 8;
+ a1.a0[0][0][1][1][1] = 9;
+ a1.a0[0][1][0][0][0] = 10;
+ a1.a0[0][1][0][0][1] = 11;
+ a1.a0[0][1][0][1][0] = 12;
+ a1.a0[0][1][0][1][1] = 13;
+ a1.a0[0][1][1][0][0] = 14;
+ a1.a0[0][1][1][0][1] = 15;
+ a1.a0[0][1][1][1][0] = 16;
+ a1.a0[0][1][1][1][1] = 17;
+ a1.a0[1][0][0][0][0] = 18;
+ a1.a0[1][0][0][0][1] = 19;
+ a1.a0[1][0][0][1][0] = 20;
+ a1.a0[1][0][0][1][1] = 21;
+ a1.a0[1][0][1][0][0] = 22;
+ a1.a0[1][0][1][0][1] = 23;
+ a1.a0[1][0][1][1][0] = 24;
+ a1.a0[1][0][1][1][1] = 25;
+ a1.a0[1][1][0][0][0] = 26;
+ a1.a0[1][1][0][0][1] = 27;
+ a1.a0[1][1][0][1][0] = 28;
+ a1.a0[1][1][0][1][1] = 29;
+ a1.a0[1][1][1][0][0] = 30;
+ a1.a0[1][1][1][0][1] = 31;
+ a1.a0[1][1][1][1][0] = 32;
+ a1.a0[1][1][1][1][1] = 33;
+ a2 = 34;
+ a3.a0[0][0][0] = 35;
+ a3.a0[0][0][1] = 36;
+ a3.a0[0][1][0] = 37;
+ a3.a0[0][1][1] = 38;
+ a3.a0[1][0][0] = 39;
+ a3.a0[1][0][1] = 40;
+ a3.a0[1][1][0] = 41;
+ a3.a0[1][1][1] = 42;
+ a4 = 43;
+ a5.a0[0][0][0] = 44;
+ a5.a0[0][0][1] = 45;
+ a5.a0[0][1][0] = 46;
+ a5.a0[0][1][1] = 47;
+ a5.a0[1][0][0] = 48;
+ a5.a0[1][0][1] = 49;
+ a5.a0[1][1][0] = 50;
+ a5.a0[1][1][1] = 51;
+ a6 = 52;
+
+ final result = passUint8Struct32BytesInlineArrayMultiDimensionalI(
+ a0, a1, a2, a3, a4, a5, a6);
+
+ print("result = $result");
+
+ Expect.equals(1378, result);
+
+ calloc.free(a1Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a5Pointer);
+}
+
+final passUint8Struct4BytesInlineArrayMultiDimensionalIn =
+ ffiTestFunctions.lookupFunction<
+ Uint32 Function(
+ Uint8, Struct4BytesInlineArrayMultiDimensionalInt, Uint8),
+ int Function(int, Struct4BytesInlineArrayMultiDimensionalInt,
+ int)>("PassUint8Struct4BytesInlineArrayMultiDimensionalIn");
+
+/// Test struct in multi dimensional inline array.
+void testPassUint8Struct4BytesInlineArrayMultiDimensionalIn() {
+ int a0;
+ final a1Pointer = calloc<Struct4BytesInlineArrayMultiDimensionalInt>();
+ final Struct4BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref;
+ int a2;
+
+ a0 = 1;
+ a1.a0[0][0].a0 = 2;
+ a1.a0[0][1].a0 = -3;
+ a1.a0[1][0].a0 = 4;
+ a1.a0[1][1].a0 = -5;
+ a2 = 6;
+
+ final result = passUint8Struct4BytesInlineArrayMultiDimensionalIn(a0, a1, a2);
+
+ print("result = $result");
+
+ Expect.equals(5, result);
+
+ calloc.free(a1Pointer);
+}
+
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index 70fbfe1..e94b35b 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -133,8 +133,8 @@
String get cStructField {
String postFix = "";
if (type is FixedLengthArrayType) {
- final length = (type as FixedLengthArrayType).length;
- postFix = "[$length]";
+ final dimensions = (type as FixedLengthArrayType).dimensions;
+ postFix = "[${dimensions.join("][")}]";
}
return "${type.cType} $name$postFix;";
}
@@ -193,6 +193,12 @@
bool get hasInlineArrays =>
members.map((e) => e.type is FixedLengthArrayType).contains(true);
+ bool get hasMultiDimensionalInlineArrays => members
+ .map((e) => e.type)
+ .whereType<FixedLengthArrayType>()
+ .where((e) => e.isMulti)
+ .isNotEmpty;
+
/// All members have the same type.
bool get isHomogeneous => memberTypes.toSet().length == 1;
@@ -216,6 +222,9 @@
}
if (hasInlineArrays) {
result += "InlineArray";
+ if (hasMultiDimensionalInlineArrays) {
+ result += "MultiDimensional";
+ }
}
if (members.length == 0) {
// No suffix.
@@ -241,10 +250,37 @@
FixedLengthArrayType(this.elementType, this.length);
+ factory FixedLengthArrayType.multi(CType elementType, List<int> dimensions) {
+ if (dimensions.length == 1) {
+ return FixedLengthArrayType(elementType, dimensions.single);
+ }
+
+ final remainingDimensions = dimensions.sublist(1);
+ final nestedArray =
+ FixedLengthArrayType.multi(elementType, remainingDimensions);
+ return FixedLengthArrayType(nestedArray, dimensions.first);
+ }
+
String get cType => elementType.cType;
- String get dartCType => "Array<${elementType.dartType}>";
+ String get dartCType => "Array<${elementType.dartCType}>";
String get dartType => "Array<${elementType.dartCType}>";
- String get dartStructFieldAnnotation => "@Array($length)";
+
+ String get dartStructFieldAnnotation {
+ if (dimensions.length > 5) {
+ return "@Array.multi([${dimensions.join(", ")}])";
+ }
+ return "@Array(${dimensions.join(", ")})";
+ }
+
+ List<int> get dimensions {
+ final elementType = this.elementType;
+ if (elementType is FixedLengthArrayType) {
+ return [length, ...elementType.dimensions];
+ }
+ return [length];
+ }
+
+ bool get isMulti => elementType is FixedLengthArrayType;
bool get hasSize => elementType.hasSize;
int get size => elementType.size * length;
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index c681f8a..ebc6a94 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -310,6 +310,24 @@
On x64, it will exhaust the integer registers with the 6th argument.
The rest goes on the stack.
On arm, arguments are 4 byte aligned."""),
+ FunctionType(
+ [
+ uint8,
+ struct32bytesInlineArrayMultiDimesional,
+ uint8,
+ struct8bytesInlineArrayMultiDimesional,
+ uint8,
+ struct8bytesInlineArrayMultiDimesional,
+ uint8
+ ],
+ uint32,
+ """
+Test multi dimensional inline array struct as argument."""),
+ FunctionType(
+ [uint8, structMultiDimensionalStruct, uint8],
+ uint32,
+ """
+Test struct in multi dimensional inline array."""),
FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
Smallest struct with data."""),
FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -507,6 +525,10 @@
struct16bytesFloatInlineNested,
struct32bytesDoubleInlineNested,
struct16bytesMixedInlineNested,
+ struct8bytesInlineArrayMultiDimesional,
+ struct32bytesInlineArrayMultiDimesional,
+ struct64bytesInlineArrayMultiDimesional,
+ structMultiDimensionalStruct,
];
final struct1byteInt = StructType([int8]);
@@ -634,3 +656,19 @@
FixedLengthArrayType(StructType([float, int16, int16]), 1),
FixedLengthArrayType(int16, 2),
], "Struct16BytesMixed3");
+
+final struct8bytesInlineArrayMultiDimesional = StructType([
+ FixedLengthArrayType.multi(uint8, [2, 2, 2])
+]);
+
+final struct32bytesInlineArrayMultiDimesional = StructType([
+ FixedLengthArrayType.multi(uint8, [2, 2, 2, 2, 2])
+]);
+
+final struct64bytesInlineArrayMultiDimesional = StructType([
+ FixedLengthArrayType.multi(uint8, [2, 2, 2, 2, 2, 2])
+]);
+
+final structMultiDimensionalStruct = StructType([
+ FixedLengthArrayType.multi(struct1byteInt, [2, 2])
+]);
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index 7ebb535..51f51ab 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -332,7 +332,7 @@
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
return """
-for(int i = 0; i < ${this_.length}; i++){
+for (int i = 0; i < ${this_.length}; i++){
${this_.elementType.dartExpectsStatements("$expected[i]", "$actual[i]")}
}
""";
@@ -370,7 +370,7 @@
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
return """
-for(intptr_t i = 0; i < ${this_.length}; i++){
+for (intptr_t i = 0; i < ${this_.length}; i++){
${this_.elementType.cExpectsStatements("$expected[i]", "$actual[i]")}
}
""";
@@ -397,7 +397,7 @@
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
return """
-for(intptr_t i = 0; i < ${this_.length}; i++){
+for (intptr_t i = 0; i < ${this_.length}; i++){
${this_.elementType.cExpectsZeroStatements("$actual[i]")}
}
""";
@@ -461,8 +461,18 @@
}
String toStringBody = members.map((m) {
if (m.type is FixedLengthArrayType) {
- final length = (m.type as FixedLengthArrayType).length;
- return "\$\{[for (var i = 0; i < $length; i += 1) ${m.name}[i]]\}";
+ int dimensionNumber = 0;
+ String inlineFor = "";
+ String read = m.name;
+ String closing = "";
+ for (final dimension in (m.type as FixedLengthArrayType).dimensions) {
+ final i = "i$dimensionNumber";
+ inlineFor += "[for (var $i = 0; $i < $dimension; $i += 1)";
+ read += "[$i]";
+ closing += "]";
+ dimensionNumber++;
+ }
+ return "\$\{$inlineFor $read $closing\}";
}
return "\$\{${m.name}\}";
}).join(", ");
diff --git a/tests/ffi/inline_array_multi_dimensional_test.dart b/tests/ffi/inline_array_multi_dimensional_test.dart
new file mode 100644
index 0000000..15d2059
--- /dev/null
+++ b/tests/ffi/inline_array_multi_dimensional_test.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import 'package:ffi/ffi.dart';
+
+// Reuse struct definitions.
+import 'function_structs_by_value_generated_test.dart';
+
+void main() {
+ testSizeOf();
+ testLoad();
+ testLoadMultiAnnotation();
+ testStore();
+ testToString();
+ testRange();
+}
+
+void testSizeOf() {
+ Expect.equals(32, sizeOf<Struct32BytesInlineArrayMultiDimensionalInt>());
+ Expect.equals(64, sizeOf<Struct64BytesInlineArrayMultiDimensionalInt>());
+}
+
+/// Tests the load of nested `Array`s.
+///
+/// Only stores into arrays which do not have nested arrays.
+void testLoad() {
+ final Pointer<Struct32BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ array[i][j][k][l][m] = i + j + k + l + m;
+ }
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ Expect.equals(i + j + k + l + m, array[i][j][k][l][m]);
+ }
+ }
+ }
+ }
+ }
+ calloc.free(pointer);
+}
+
+/// Tests the load of nested `Array`s.
+///
+/// Only stores into arrays which do not have nested arrays.
+void testLoadMultiAnnotation() {
+ final Pointer<Struct64BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ for (int o = 0; o < 2; o++) {
+ array[i][j][k][l][m][o] = i + j + k + l + m + o;
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ for (int o = 0; o < 2; o++) {
+ Expect.equals(i + j + k + l + m + o, array[i][j][k][l][m][o]);
+ }
+ }
+ }
+ }
+ }
+ }
+ calloc.free(pointer);
+}
+
+void testStore() {
+ final Pointer<Struct32BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ array[i][j][k][l][m] = i + j + k + l + m;
+ }
+ }
+ }
+ }
+ }
+ array[0] = array[1]; // Copy many things.
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ Expect.equals(array[1][j][k][l][m], array[0][j][k][l][m]);
+ }
+ }
+ }
+ }
+ calloc.free(pointer);
+}
+
+// // Tests the toString of the test generator.
+void testToString() {
+ final Pointer<Struct32BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ array[i][j][k][l][m] = 16 * i + 8 * j + 4 * k + 2 * l + m;
+ }
+ }
+ }
+ }
+ }
+ Expect.equals(
+ "([[[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]], [[[[16, 17], [18, 19]], [[20, 21], [22, 23]]], [[[24, 25], [26, 27]], [[28, 29], [30, 31]]]]])",
+ struct.toString());
+ calloc.free(pointer);
+}
+
+void testRange() {
+ final pointer = calloc<Struct32BytesInlineArrayMultiDimensionalInt>();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ array[0];
+ array[1];
+ Expect.throws(() => array[-1]);
+ Expect.throws(() => array[-1] = array[1]);
+ Expect.throws(() => array[2]);
+ Expect.throws(() => array[2] = array[1]);
+ array[0][0];
+ array[0][1];
+ Expect.throws(() => array[0][-1]);
+ Expect.throws(() => array[0][-1] = array[0][1]);
+ Expect.throws(() => array[0][2]);
+ Expect.throws(() => array[0][2] = array[0][1]);
+ calloc.free(pointer);
+}
diff --git a/tests/ffi/inline_array_test.dart b/tests/ffi/inline_array_test.dart
index 518d62e..868d117 100644
--- a/tests/ffi/inline_array_test.dart
+++ b/tests/ffi/inline_array_test.dart
@@ -30,11 +30,11 @@
void testLoad() {
final pointer = calloc<Struct8BytesInlineArrayInt>();
final struct = pointer.ref;
- final cArray = struct.a0;
+ final array = struct.a0;
pointer.cast<Uint8>()[0] = 42;
pointer.cast<Uint8>()[7] = 3;
- Expect.equals(42, cArray[0]);
- Expect.equals(3, cArray[7]);
+ Expect.equals(42, array[0]);
+ Expect.equals(3, array[7]);
calloc.free(pointer);
}
@@ -55,9 +55,9 @@
void testToString() {
final pointer = calloc<Struct8BytesInlineArrayInt>();
final struct = pointer.ref;
- final cArray = struct.a0;
+ final array = struct.a0;
for (var i = 0; i < 8; i++) {
- cArray[i] = i;
+ array[i] = i;
}
Expect.equals("([0, 1, 2, 3, 4, 5, 6, 7])", struct.toString());
calloc.free(pointer);
@@ -77,14 +77,14 @@
void testRange() {
final pointer = calloc<Struct8BytesInlineArrayInt>();
final struct = pointer.ref;
- final cArray = struct.a0;
- cArray[0] = 1;
- Expect.equals(1, cArray[0]);
- cArray[7] = 7;
- Expect.equals(7, cArray[7]);
- Expect.throws(() => cArray[-1]);
- Expect.throws(() => cArray[-1] = 0);
- Expect.throws(() => cArray[8]);
- Expect.throws(() => cArray[8] = 0);
+ final array = struct.a0;
+ array[0] = 1;
+ Expect.equals(1, array[0]);
+ array[7] = 7;
+ Expect.equals(7, array[7]);
+ Expect.throws(() => array[-1]);
+ Expect.throws(() => array[-1] = 0);
+ Expect.throws(() => array[8]);
+ Expect.throws(() => array[8] = 0);
calloc.free(pointer);
}
diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart
index 3ec5ac0..746b238 100644
--- a/tests/ffi/vmspecific_static_checks_test.dart
+++ b/tests/ffi/vmspecific_static_checks_test.dart
@@ -677,8 +677,29 @@
}
class TestStruct1402 extends Struct {
- @Array(8) //# 1402: compile-time error
+ @Array(8, 8, 8) //# 1402: compile-time error
external Array<Array<Uint8>> a0; //# 1402: compile-time error
external Pointer<Uint8> notEmpty;
}
+
+class TestStruct1403 extends Struct {
+ @Array(8, 8) //# 1403: compile-time error
+ external Array<Array<Array<Uint8>>> a0; //# 1403: compile-time error
+
+ external Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1404 extends Struct {
+ @Array.multi([8, 8, 8]) //# 1404: compile-time error
+ external Array<Array<Uint8>> a0; //# 1404: compile-time error
+
+ external Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1405 extends Struct {
+ @Array.multi([8, 8]) //# 1405: compile-time error
+ external Array<Array<Array<Uint8>>> a0; //# 1405: compile-time error
+
+ external Pointer<Uint8> notEmpty;
+}
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
index ec212c5..d4650fe 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
@@ -273,6 +273,18 @@
passStructStruct16BytesMixed3x10, 0.0),
passStructStruct16BytesMixed3x10AfterCallback),
CallbackTest.withCheck(
+ "PassUint8Struct32BytesInlineArrayMultiDimensionalI",
+ Pointer.fromFunction<
+ PassUint8Struct32BytesInlineArrayMultiDimensionalIType>(
+ passUint8Struct32BytesInlineArrayMultiDimensionalI, 0),
+ passUint8Struct32BytesInlineArrayMultiDimensionalIAfterCallback),
+ CallbackTest.withCheck(
+ "PassUint8Struct4BytesInlineArrayMultiDimensionalIn",
+ Pointer.fromFunction<
+ PassUint8Struct4BytesInlineArrayMultiDimensionalInType>(
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn, 0),
+ passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -6159,6 +6171,245 @@
Expect.approxEquals(30.0, result);
}
+typedef PassUint8Struct32BytesInlineArrayMultiDimensionalIType
+ = Uint32 Function(
+ Uint8,
+ Struct32BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a0 = 0;
+Struct32BytesInlineArrayMultiDimensionalInt
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1 =
+ Struct32BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a2 = 0;
+Struct8BytesInlineArrayMultiDimensionalInt
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a3 =
+ Struct8BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a4 = 0;
+Struct8BytesInlineArrayMultiDimensionalInt
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a5 =
+ Struct8BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct32BytesInlineArrayMultiDimensionalI_a6 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Struct32BytesInlineArrayMultiDimensionalIResult = 0;
+
+int passUint8Struct32BytesInlineArrayMultiDimensionalICalculateResult() {
+ int result = 0;
+
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a0;
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][0][1][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[0][1][1][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][0][1][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][0][1][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][0][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][0][1];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][1][0];
+ result +=
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1.a0[1][1][1][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a2;
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[0][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a3.a0[1][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a4;
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[0][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][0][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][0][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][1][0];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a5.a0[1][1][1];
+ result += passUint8Struct32BytesInlineArrayMultiDimensionalI_a6;
+
+ passUint8Struct32BytesInlineArrayMultiDimensionalIResult = result;
+
+ return result;
+}
+
+/// Test multi dimensional inline array struct as argument.
+int passUint8Struct32BytesInlineArrayMultiDimensionalI(
+ int a0,
+ Struct32BytesInlineArrayMultiDimensionalInt a1,
+ int a2,
+ Struct8BytesInlineArrayMultiDimensionalInt a3,
+ int a4,
+ Struct8BytesInlineArrayMultiDimensionalInt a5,
+ int a6) {
+ print(
+ "passUint8Struct32BytesInlineArrayMultiDimensionalI(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6})");
+
+ // In legacy mode, possibly return null.
+ if (a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassUint8Struct32BytesInlineArrayMultiDimensionalI throwing on purpose!");
+ }
+
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a0 = a0;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a1 = a1;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a2 = a2;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a3 = a3;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a4 = a4;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a5 = a5;
+ passUint8Struct32BytesInlineArrayMultiDimensionalI_a6 = a6;
+
+ final result =
+ passUint8Struct32BytesInlineArrayMultiDimensionalICalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passUint8Struct32BytesInlineArrayMultiDimensionalIAfterCallback() {
+ final result =
+ passUint8Struct32BytesInlineArrayMultiDimensionalICalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(1378, result);
+}
+
+typedef PassUint8Struct4BytesInlineArrayMultiDimensionalInType = Uint32
+ Function(Uint8, Struct4BytesInlineArrayMultiDimensionalInt, Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Struct4BytesInlineArrayMultiDimensionalIn_a0 = 0;
+Struct4BytesInlineArrayMultiDimensionalInt
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1 =
+ Struct4BytesInlineArrayMultiDimensionalInt();
+int passUint8Struct4BytesInlineArrayMultiDimensionalIn_a2 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Struct4BytesInlineArrayMultiDimensionalInResult = 0;
+
+int passUint8Struct4BytesInlineArrayMultiDimensionalInCalculateResult() {
+ int result = 0;
+
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[0][0].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[0][1].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[1][0].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1.a0[1][1].a0;
+ result += passUint8Struct4BytesInlineArrayMultiDimensionalIn_a2;
+
+ passUint8Struct4BytesInlineArrayMultiDimensionalInResult = result;
+
+ return result;
+}
+
+/// Test struct in multi dimensional inline array.
+int passUint8Struct4BytesInlineArrayMultiDimensionalIn(
+ int a0, Struct4BytesInlineArrayMultiDimensionalInt a1, int a2) {
+ print(
+ "passUint8Struct4BytesInlineArrayMultiDimensionalIn(${a0}, ${a1}, ${a2})");
+
+ // In legacy mode, possibly return null.
+ if (a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassUint8Struct4BytesInlineArrayMultiDimensionalIn throwing on purpose!");
+ }
+
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a0 = a0;
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a1 = a1;
+ passUint8Struct4BytesInlineArrayMultiDimensionalIn_a2 = a2;
+
+ final result =
+ passUint8Struct4BytesInlineArrayMultiDimensionalInCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback() {
+ final result =
+ passUint8Struct4BytesInlineArrayMultiDimensionalInCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(5, result);
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart
index a3f7561..91747de 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -68,6 +68,8 @@
testPassStructStruct16BytesHomogeneousFloat2x5();
testPassStructStruct32BytesHomogeneousDouble2x5();
testPassStructStruct16BytesMixed3x10();
+ testPassUint8Struct32BytesInlineArrayMultiDimensionalI();
+ testPassUint8Struct4BytesInlineArrayMultiDimensionalIn();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@@ -1043,7 +1045,7 @@
@Array(8)
Array<Uint8> a0;
- String toString() => "(${[for (var i = 0; i < 8; i += 1) a0[i]]})";
+ String toString() => "(${[for (var i0 = 0; i0 < 8; i0 += 1) a0[i0]]})";
}
class StructInlineArrayIrregular extends Struct {
@@ -1053,14 +1055,14 @@
@Uint8()
int a1;
- String toString() => "(${[for (var i = 0; i < 2; i += 1) a0[i]]}, ${a1})";
+ String toString() => "(${[for (var i0 = 0; i0 < 2; i0 += 1) a0[i0]]}, ${a1})";
}
class StructInlineArray100Bytes extends Struct {
@Array(100)
Array<Uint8> a0;
- String toString() => "(${[for (var i = 0; i < 100; i += 1) a0[i]]})";
+ String toString() => "(${[for (var i0 = 0; i0 < 100; i0 += 1) a0[i0]]})";
}
class StructInlineArrayBig extends Struct {
@@ -1074,7 +1076,7 @@
Array<Uint8> a2;
String toString() =>
- "(${a0}, ${a1}, ${[for (var i = 0; i < 4000; i += 1) a2[i]]})";
+ "(${a0}, ${a1}, ${[for (var i0 = 0; i0 < 4000; i0 += 1) a2[i0]]})";
}
class StructStruct16BytesHomogeneousFloat2 extends Struct {
@@ -1087,7 +1089,7 @@
double a2;
String toString() =>
- "(${a0}, ${[for (var i = 0; i < 2; i += 1) a1[i]]}, ${a2})";
+ "(${a0}, ${[for (var i0 = 0; i0 < 2; i0 += 1) a1[i0]]}, ${a2})";
}
class StructStruct32BytesHomogeneousDouble2 extends Struct {
@@ -1100,7 +1102,7 @@
double a2;
String toString() =>
- "(${a0}, ${[for (var i = 0; i < 2; i += 1) a1[i]]}, ${a2})";
+ "(${a0}, ${[for (var i0 = 0; i0 < 2; i0 += 1) a1[i0]]}, ${a2})";
}
class StructStruct16BytesMixed3 extends Struct {
@@ -1112,8 +1114,75 @@
@Array(2)
Array<Int16> a2;
- String toString() => "(${a0}, ${[for (var i = 0; i < 1; i += 1) a1[i]]}, ${[
- for (var i = 0; i < 2; i += 1) a2[i]
+ String toString() => "(${a0}, ${[
+ for (var i0 = 0; i0 < 1; i0 += 1) a1[i0]
+ ]}, ${[for (var i0 = 0; i0 < 2; i0 += 1) a2[i0]]})";
+}
+
+class Struct8BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array(2, 2, 2)
+ Array<Array<Array<Uint8>>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [
+ for (var i1 = 0; i1 < 2; i1 += 1)
+ [for (var i2 = 0; i2 < 2; i2 += 1) a0[i0][i1][i2]]
+ ]
+ ]})";
+}
+
+class Struct32BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array(2, 2, 2, 2, 2)
+ Array<Array<Array<Array<Array<Uint8>>>>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [
+ for (var i1 = 0; i1 < 2; i1 += 1)
+ [
+ for (var i2 = 0; i2 < 2; i2 += 1)
+ [
+ for (var i3 = 0; i3 < 2; i3 += 1)
+ [for (var i4 = 0; i4 < 2; i4 += 1) a0[i0][i1][i2][i3][i4]]
+ ]
+ ]
+ ]
+ ]})";
+}
+
+class Struct64BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array.multi([2, 2, 2, 2, 2, 2])
+ Array<Array<Array<Array<Array<Array<Uint8>>>>>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [
+ for (var i1 = 0; i1 < 2; i1 += 1)
+ [
+ for (var i2 = 0; i2 < 2; i2 += 1)
+ [
+ for (var i3 = 0; i3 < 2; i3 += 1)
+ [
+ for (var i4 = 0; i4 < 2; i4 += 1)
+ [
+ for (var i5 = 0; i5 < 2; i5 += 1)
+ a0[i0][i1][i2][i3][i4][i5]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]})";
+}
+
+class Struct4BytesInlineArrayMultiDimensionalInt extends Struct {
+ @Array(2, 2)
+ Array<Array<Struct1ByteInt>> a0;
+
+ String toString() => "(${[
+ for (var i0 = 0; i0 < 2; i0 += 1)
+ [for (var i1 = 0; i1 < 2; i1 += 1) a0[i0][i1]]
]})";
}
@@ -5227,6 +5296,133 @@
calloc.free(a9Pointer);
}
+final passUint8Struct32BytesInlineArrayMultiDimensionalI =
+ ffiTestFunctions.lookupFunction<
+ Uint32 Function(
+ Uint8,
+ Struct32BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ Uint8),
+ int Function(
+ int,
+ Struct32BytesInlineArrayMultiDimensionalInt,
+ int,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ int,
+ Struct8BytesInlineArrayMultiDimensionalInt,
+ int)>("PassUint8Struct32BytesInlineArrayMultiDimensionalI");
+
+/// Test multi dimensional inline array struct as argument.
+void testPassUint8Struct32BytesInlineArrayMultiDimensionalI() {
+ int a0;
+ final a1Pointer = calloc<Struct32BytesInlineArrayMultiDimensionalInt>();
+ final Struct32BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref;
+ int a2;
+ final a3Pointer = calloc<Struct8BytesInlineArrayMultiDimensionalInt>();
+ final Struct8BytesInlineArrayMultiDimensionalInt a3 = a3Pointer.ref;
+ int a4;
+ final a5Pointer = calloc<Struct8BytesInlineArrayMultiDimensionalInt>();
+ final Struct8BytesInlineArrayMultiDimensionalInt a5 = a5Pointer.ref;
+ int a6;
+
+ a0 = 1;
+ a1.a0[0][0][0][0][0] = 2;
+ a1.a0[0][0][0][0][1] = 3;
+ a1.a0[0][0][0][1][0] = 4;
+ a1.a0[0][0][0][1][1] = 5;
+ a1.a0[0][0][1][0][0] = 6;
+ a1.a0[0][0][1][0][1] = 7;
+ a1.a0[0][0][1][1][0] = 8;
+ a1.a0[0][0][1][1][1] = 9;
+ a1.a0[0][1][0][0][0] = 10;
+ a1.a0[0][1][0][0][1] = 11;
+ a1.a0[0][1][0][1][0] = 12;
+ a1.a0[0][1][0][1][1] = 13;
+ a1.a0[0][1][1][0][0] = 14;
+ a1.a0[0][1][1][0][1] = 15;
+ a1.a0[0][1][1][1][0] = 16;
+ a1.a0[0][1][1][1][1] = 17;
+ a1.a0[1][0][0][0][0] = 18;
+ a1.a0[1][0][0][0][1] = 19;
+ a1.a0[1][0][0][1][0] = 20;
+ a1.a0[1][0][0][1][1] = 21;
+ a1.a0[1][0][1][0][0] = 22;
+ a1.a0[1][0][1][0][1] = 23;
+ a1.a0[1][0][1][1][0] = 24;
+ a1.a0[1][0][1][1][1] = 25;
+ a1.a0[1][1][0][0][0] = 26;
+ a1.a0[1][1][0][0][1] = 27;
+ a1.a0[1][1][0][1][0] = 28;
+ a1.a0[1][1][0][1][1] = 29;
+ a1.a0[1][1][1][0][0] = 30;
+ a1.a0[1][1][1][0][1] = 31;
+ a1.a0[1][1][1][1][0] = 32;
+ a1.a0[1][1][1][1][1] = 33;
+ a2 = 34;
+ a3.a0[0][0][0] = 35;
+ a3.a0[0][0][1] = 36;
+ a3.a0[0][1][0] = 37;
+ a3.a0[0][1][1] = 38;
+ a3.a0[1][0][0] = 39;
+ a3.a0[1][0][1] = 40;
+ a3.a0[1][1][0] = 41;
+ a3.a0[1][1][1] = 42;
+ a4 = 43;
+ a5.a0[0][0][0] = 44;
+ a5.a0[0][0][1] = 45;
+ a5.a0[0][1][0] = 46;
+ a5.a0[0][1][1] = 47;
+ a5.a0[1][0][0] = 48;
+ a5.a0[1][0][1] = 49;
+ a5.a0[1][1][0] = 50;
+ a5.a0[1][1][1] = 51;
+ a6 = 52;
+
+ final result = passUint8Struct32BytesInlineArrayMultiDimensionalI(
+ a0, a1, a2, a3, a4, a5, a6);
+
+ print("result = $result");
+
+ Expect.equals(1378, result);
+
+ calloc.free(a1Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a5Pointer);
+}
+
+final passUint8Struct4BytesInlineArrayMultiDimensionalIn =
+ ffiTestFunctions.lookupFunction<
+ Uint32 Function(
+ Uint8, Struct4BytesInlineArrayMultiDimensionalInt, Uint8),
+ int Function(int, Struct4BytesInlineArrayMultiDimensionalInt,
+ int)>("PassUint8Struct4BytesInlineArrayMultiDimensionalIn");
+
+/// Test struct in multi dimensional inline array.
+void testPassUint8Struct4BytesInlineArrayMultiDimensionalIn() {
+ int a0;
+ final a1Pointer = calloc<Struct4BytesInlineArrayMultiDimensionalInt>();
+ final Struct4BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref;
+ int a2;
+
+ a0 = 1;
+ a1.a0[0][0].a0 = 2;
+ a1.a0[0][1].a0 = -3;
+ a1.a0[1][0].a0 = 4;
+ a1.a0[1][1].a0 = -5;
+ a2 = 6;
+
+ final result = passUint8Struct4BytesInlineArrayMultiDimensionalIn(a0, a1, a2);
+
+ print("result = $result");
+
+ Expect.equals(5, result);
+
+ calloc.free(a1Pointer);
+}
+
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
diff --git a/tests/ffi_2/generator/c_types.dart b/tests/ffi_2/generator/c_types.dart
index 70fbfe1..e94b35b 100644
--- a/tests/ffi_2/generator/c_types.dart
+++ b/tests/ffi_2/generator/c_types.dart
@@ -133,8 +133,8 @@
String get cStructField {
String postFix = "";
if (type is FixedLengthArrayType) {
- final length = (type as FixedLengthArrayType).length;
- postFix = "[$length]";
+ final dimensions = (type as FixedLengthArrayType).dimensions;
+ postFix = "[${dimensions.join("][")}]";
}
return "${type.cType} $name$postFix;";
}
@@ -193,6 +193,12 @@
bool get hasInlineArrays =>
members.map((e) => e.type is FixedLengthArrayType).contains(true);
+ bool get hasMultiDimensionalInlineArrays => members
+ .map((e) => e.type)
+ .whereType<FixedLengthArrayType>()
+ .where((e) => e.isMulti)
+ .isNotEmpty;
+
/// All members have the same type.
bool get isHomogeneous => memberTypes.toSet().length == 1;
@@ -216,6 +222,9 @@
}
if (hasInlineArrays) {
result += "InlineArray";
+ if (hasMultiDimensionalInlineArrays) {
+ result += "MultiDimensional";
+ }
}
if (members.length == 0) {
// No suffix.
@@ -241,10 +250,37 @@
FixedLengthArrayType(this.elementType, this.length);
+ factory FixedLengthArrayType.multi(CType elementType, List<int> dimensions) {
+ if (dimensions.length == 1) {
+ return FixedLengthArrayType(elementType, dimensions.single);
+ }
+
+ final remainingDimensions = dimensions.sublist(1);
+ final nestedArray =
+ FixedLengthArrayType.multi(elementType, remainingDimensions);
+ return FixedLengthArrayType(nestedArray, dimensions.first);
+ }
+
String get cType => elementType.cType;
- String get dartCType => "Array<${elementType.dartType}>";
+ String get dartCType => "Array<${elementType.dartCType}>";
String get dartType => "Array<${elementType.dartCType}>";
- String get dartStructFieldAnnotation => "@Array($length)";
+
+ String get dartStructFieldAnnotation {
+ if (dimensions.length > 5) {
+ return "@Array.multi([${dimensions.join(", ")}])";
+ }
+ return "@Array(${dimensions.join(", ")})";
+ }
+
+ List<int> get dimensions {
+ final elementType = this.elementType;
+ if (elementType is FixedLengthArrayType) {
+ return [length, ...elementType.dimensions];
+ }
+ return [length];
+ }
+
+ bool get isMulti => elementType is FixedLengthArrayType;
bool get hasSize => elementType.hasSize;
int get size => elementType.size * length;
diff --git a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
index c681f8a..d0a22b0 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
@@ -310,6 +310,24 @@
On x64, it will exhaust the integer registers with the 6th argument.
The rest goes on the stack.
On arm, arguments are 4 byte aligned."""),
+ FunctionType(
+ [
+ uint8,
+ struct8bytesInlineArrayMultiDimesional,
+ uint8,
+ struct8bytesInlineArrayMultiDimesional,
+ uint8,
+ struct8bytesInlineArrayMultiDimesional,
+ uint8
+ ],
+ uint32,
+ """
+Test multi dimensional inline array struct as argument."""),
+ FunctionType(
+ [uint8, structMultiDimensionalStruct, uint8],
+ uint32,
+ """
+Test struct in multi dimensional inline array."""),
FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
Smallest struct with data."""),
FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -507,6 +525,10 @@
struct16bytesFloatInlineNested,
struct32bytesDoubleInlineNested,
struct16bytesMixedInlineNested,
+ struct8bytesInlineArrayMultiDimesional,
+ struct32bytesInlineArrayMultiDimesional,
+ struct64bytesInlineArrayMultiDimesional,
+ structMultiDimensionalStruct,
];
final struct1byteInt = StructType([int8]);
@@ -634,3 +656,19 @@
FixedLengthArrayType(StructType([float, int16, int16]), 1),
FixedLengthArrayType(int16, 2),
], "Struct16BytesMixed3");
+
+final struct8bytesInlineArrayMultiDimesional = StructType([
+ FixedLengthArrayType.multi(uint8, [2, 2, 2])
+]);
+
+final struct32bytesInlineArrayMultiDimesional = StructType([
+ FixedLengthArrayType.multi(uint8, [2, 2, 2, 2, 2])
+]);
+
+final struct64bytesInlineArrayMultiDimesional = StructType([
+ FixedLengthArrayType.multi(uint8, [2, 2, 2, 2, 2, 2])
+]);
+
+final structMultiDimensionalStruct = StructType([
+ FixedLengthArrayType.multi(struct1byteInt, [2, 2])
+]);
diff --git a/tests/ffi_2/generator/structs_by_value_tests_generator.dart b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
index 7ebb535..51f51ab 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
@@ -332,7 +332,7 @@
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
return """
-for(int i = 0; i < ${this_.length}; i++){
+for (int i = 0; i < ${this_.length}; i++){
${this_.elementType.dartExpectsStatements("$expected[i]", "$actual[i]")}
}
""";
@@ -370,7 +370,7 @@
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
return """
-for(intptr_t i = 0; i < ${this_.length}; i++){
+for (intptr_t i = 0; i < ${this_.length}; i++){
${this_.elementType.cExpectsStatements("$expected[i]", "$actual[i]")}
}
""";
@@ -397,7 +397,7 @@
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
return """
-for(intptr_t i = 0; i < ${this_.length}; i++){
+for (intptr_t i = 0; i < ${this_.length}; i++){
${this_.elementType.cExpectsZeroStatements("$actual[i]")}
}
""";
@@ -461,8 +461,18 @@
}
String toStringBody = members.map((m) {
if (m.type is FixedLengthArrayType) {
- final length = (m.type as FixedLengthArrayType).length;
- return "\$\{[for (var i = 0; i < $length; i += 1) ${m.name}[i]]\}";
+ int dimensionNumber = 0;
+ String inlineFor = "";
+ String read = m.name;
+ String closing = "";
+ for (final dimension in (m.type as FixedLengthArrayType).dimensions) {
+ final i = "i$dimensionNumber";
+ inlineFor += "[for (var $i = 0; $i < $dimension; $i += 1)";
+ read += "[$i]";
+ closing += "]";
+ dimensionNumber++;
+ }
+ return "\$\{$inlineFor $read $closing\}";
}
return "\$\{${m.name}\}";
}).join(", ");
diff --git a/tests/ffi_2/inline_array_multi_dimensional_test.dart b/tests/ffi_2/inline_array_multi_dimensional_test.dart
new file mode 100644
index 0000000..15d2059
--- /dev/null
+++ b/tests/ffi_2/inline_array_multi_dimensional_test.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import 'package:ffi/ffi.dart';
+
+// Reuse struct definitions.
+import 'function_structs_by_value_generated_test.dart';
+
+void main() {
+ testSizeOf();
+ testLoad();
+ testLoadMultiAnnotation();
+ testStore();
+ testToString();
+ testRange();
+}
+
+void testSizeOf() {
+ Expect.equals(32, sizeOf<Struct32BytesInlineArrayMultiDimensionalInt>());
+ Expect.equals(64, sizeOf<Struct64BytesInlineArrayMultiDimensionalInt>());
+}
+
+/// Tests the load of nested `Array`s.
+///
+/// Only stores into arrays which do not have nested arrays.
+void testLoad() {
+ final Pointer<Struct32BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ array[i][j][k][l][m] = i + j + k + l + m;
+ }
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ Expect.equals(i + j + k + l + m, array[i][j][k][l][m]);
+ }
+ }
+ }
+ }
+ }
+ calloc.free(pointer);
+}
+
+/// Tests the load of nested `Array`s.
+///
+/// Only stores into arrays which do not have nested arrays.
+void testLoadMultiAnnotation() {
+ final Pointer<Struct64BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ for (int o = 0; o < 2; o++) {
+ array[i][j][k][l][m][o] = i + j + k + l + m + o;
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ for (int o = 0; o < 2; o++) {
+ Expect.equals(i + j + k + l + m + o, array[i][j][k][l][m][o]);
+ }
+ }
+ }
+ }
+ }
+ }
+ calloc.free(pointer);
+}
+
+void testStore() {
+ final Pointer<Struct32BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ array[i][j][k][l][m] = i + j + k + l + m;
+ }
+ }
+ }
+ }
+ }
+ array[0] = array[1]; // Copy many things.
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ Expect.equals(array[1][j][k][l][m], array[0][j][k][l][m]);
+ }
+ }
+ }
+ }
+ calloc.free(pointer);
+}
+
+// // Tests the toString of the test generator.
+void testToString() {
+ final Pointer<Struct32BytesInlineArrayMultiDimensionalInt> pointer = calloc();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ for (int l = 0; l < 2; l++) {
+ for (int m = 0; m < 2; m++) {
+ array[i][j][k][l][m] = 16 * i + 8 * j + 4 * k + 2 * l + m;
+ }
+ }
+ }
+ }
+ }
+ Expect.equals(
+ "([[[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]], [[[[16, 17], [18, 19]], [[20, 21], [22, 23]]], [[[24, 25], [26, 27]], [[28, 29], [30, 31]]]]])",
+ struct.toString());
+ calloc.free(pointer);
+}
+
+void testRange() {
+ final pointer = calloc<Struct32BytesInlineArrayMultiDimensionalInt>();
+ final struct = pointer.ref;
+ final array = struct.a0;
+ array[0];
+ array[1];
+ Expect.throws(() => array[-1]);
+ Expect.throws(() => array[-1] = array[1]);
+ Expect.throws(() => array[2]);
+ Expect.throws(() => array[2] = array[1]);
+ array[0][0];
+ array[0][1];
+ Expect.throws(() => array[0][-1]);
+ Expect.throws(() => array[0][-1] = array[0][1]);
+ Expect.throws(() => array[0][2]);
+ Expect.throws(() => array[0][2] = array[0][1]);
+ calloc.free(pointer);
+}
diff --git a/tests/ffi_2/inline_array_test.dart b/tests/ffi_2/inline_array_test.dart
index 518d62e..868d117 100644
--- a/tests/ffi_2/inline_array_test.dart
+++ b/tests/ffi_2/inline_array_test.dart
@@ -30,11 +30,11 @@
void testLoad() {
final pointer = calloc<Struct8BytesInlineArrayInt>();
final struct = pointer.ref;
- final cArray = struct.a0;
+ final array = struct.a0;
pointer.cast<Uint8>()[0] = 42;
pointer.cast<Uint8>()[7] = 3;
- Expect.equals(42, cArray[0]);
- Expect.equals(3, cArray[7]);
+ Expect.equals(42, array[0]);
+ Expect.equals(3, array[7]);
calloc.free(pointer);
}
@@ -55,9 +55,9 @@
void testToString() {
final pointer = calloc<Struct8BytesInlineArrayInt>();
final struct = pointer.ref;
- final cArray = struct.a0;
+ final array = struct.a0;
for (var i = 0; i < 8; i++) {
- cArray[i] = i;
+ array[i] = i;
}
Expect.equals("([0, 1, 2, 3, 4, 5, 6, 7])", struct.toString());
calloc.free(pointer);
@@ -77,14 +77,14 @@
void testRange() {
final pointer = calloc<Struct8BytesInlineArrayInt>();
final struct = pointer.ref;
- final cArray = struct.a0;
- cArray[0] = 1;
- Expect.equals(1, cArray[0]);
- cArray[7] = 7;
- Expect.equals(7, cArray[7]);
- Expect.throws(() => cArray[-1]);
- Expect.throws(() => cArray[-1] = 0);
- Expect.throws(() => cArray[8]);
- Expect.throws(() => cArray[8] = 0);
+ final array = struct.a0;
+ array[0] = 1;
+ Expect.equals(1, array[0]);
+ array[7] = 7;
+ Expect.equals(7, array[7]);
+ Expect.throws(() => array[-1]);
+ Expect.throws(() => array[-1] = 0);
+ Expect.throws(() => array[8]);
+ Expect.throws(() => array[8] = 0);
calloc.free(pointer);
}
diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart
index 217df34..978fb2d 100644
--- a/tests/ffi_2/vmspecific_static_checks_test.dart
+++ b/tests/ffi_2/vmspecific_static_checks_test.dart
@@ -675,8 +675,29 @@
}
class TestStruct1402 extends Struct {
- @Array(8) //# 1402: compile-time error
+ @Array(8, 8, 8) //# 1402: compile-time error
Array<Array<Uint8>> a0; //# 1402: compile-time error
Pointer<Uint8> notEmpty;
}
+
+class TestStruct1403 extends Struct {
+ @Array(8, 8) //# 1403: compile-time error
+ Array<Array<Array<Uint8>>> a0; //# 1403: compile-time error
+
+ Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1404 extends Struct {
+ @Array.multi([8, 8, 8]) //# 1404: compile-time error
+ Array<Array<Uint8>> a0; //# 1404: compile-time error
+
+ Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1405 extends Struct {
+ @Array.multi([8, 8]) //# 1405: compile-time error
+ Array<Array<Array<Uint8>>> a0; //# 1405: compile-time error
+
+ Pointer<Uint8> notEmpty;
+}
diff --git a/tools/VERSION b/tools/VERSION
index 984173b..150d415 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 120
+PRERELEASE 121
PRERELEASE_PATCH 0
\ No newline at end of file