Version 2.16.0-152.0.dev
Merge commit '2766add19a274cef9eb0c763fa3627026fd9b923' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/type_info.dart b/pkg/_fe_analyzer_shared/lib/src/parser/type_info.dart
index d599819..9e76440 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/type_info.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/type_info.dart
@@ -251,6 +251,18 @@
assert(typeParamOrArg == noTypeParamOrArg);
next = next.next!;
+
+ // TODO(scheglov) This is a hack to partially fix.
+ // https://github.com/dart-lang/sdk/issues/47951
+ if (optional('?', next) &&
+ optional('super', next.next!) &&
+ optional('.', next.next!.next!)) {
+ return simpleNullableType;
+ }
+ if (optional('super', next) && optional('.', next.next!)) {
+ return simpleType;
+ }
+
if (optional('.', next)) {
next = next.next!;
if (isValidTypeReference(next)) {
diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart
index c592b1f..aa69ec9 100644
--- a/pkg/analyzer/lib/src/summary2/informative_data.dart
+++ b/pkg/analyzer/lib/src/summary2/informative_data.dart
@@ -1370,6 +1370,9 @@
} else if (notDefault is FunctionTypedFormalParameter) {
_writeTypeParameters(notDefault.typeParameters);
_writeFormalParameters(notDefault.parameters);
+ } else if (notDefault is SuperFormalParameter) {
+ _writeTypeParameters(notDefault.typeParameters);
+ _writeFormalParameters(notDefault.parameters);
} else {
_writeTypeParameters(null);
_writeFormalParameters(null);
diff --git a/pkg/analyzer/test/generated/utilities_test.dart b/pkg/analyzer/test/generated/utilities_test.dart
index cd93433..dfaa5e0 100644
--- a/pkg/analyzer/test/generated/utilities_test.dart
+++ b/pkg/analyzer/test/generated/utilities_test.dart
@@ -1644,7 +1644,6 @@
);
}
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47951')
void test_superFormalParameter() {
var findNode = _parseStringToFindNode(r'''
class A {
@@ -1666,7 +1665,6 @@
);
}
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47741')
void test_superFormalParameter_functionTyped() {
var findNode = _parseStringToFindNode(r'''
class A {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index b5fdd89..7a39830 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -1453,7 +1453,6 @@
''');
}
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47951')
test_class_constructor_parameters_super_explicitType_function() async {
var library = await checkLibrary('''
class A {
@@ -1492,7 +1491,6 @@
''');
}
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47951')
test_class_constructor_parameters_super_explicitType_interface() async {
var library = await checkLibrary('''
class A {
@@ -1518,13 +1516,45 @@
constructors
@47
parameters
- requiredPositional final super.a @49
+ requiredPositional final super.a @59
type: int
superConstructorParameter: a@18
superConstructor: self::@class::A::@constructor::•
''');
}
+ test_class_constructor_parameters_super_explicitType_interface_nullable() async {
+ var library = await checkLibrary('''
+class A {
+ A(num? a);
+}
+
+class B extends A {
+ B(int? super.a);
+}
+''');
+ checkElementText(library, r'''
+library
+ definingUnit
+ classes
+ class A @6
+ constructors
+ @12
+ parameters
+ requiredPositional a @19
+ type: num?
+ class B @32
+ supertype: A
+ constructors
+ @48
+ parameters
+ requiredPositional final super.a @61
+ type: int?
+ superConstructorParameter: a@19
+ superConstructor: self::@class::A::@constructor::•
+''');
+ }
+
test_class_constructor_parameters_super_invalid_topFunction() async {
var library = await checkLibrary('''
void f(super.a) {}
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index d545175..c6b977b 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -39,7 +39,6 @@
import '../names.dart' show noSuchMethodName;
import '../problems.dart' show internalProblem, unhandled;
import '../scope.dart';
-import '../source/source_constructor_builder.dart';
import '../source/source_factory_builder.dart';
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../source/source_loader.dart';
@@ -47,10 +46,8 @@
import '../type_inference/type_schema.dart' show UnknownType;
import '../util/helpers.dart' show DelayedActionPerformer;
import 'builder.dart';
-import 'constructor_builder.dart';
import 'constructor_reference_builder.dart';
import 'declaration_builder.dart';
-import 'field_builder.dart';
import 'function_builder.dart';
import 'library_builder.dart';
import 'member_builder.dart';
@@ -89,8 +86,6 @@
@override
Uri get fileUri;
- ClassBuilder? get patchForTesting;
-
bool get isAbstract;
bool get isMacro;
@@ -120,13 +115,6 @@
void forEach(void f(String name, Builder builder));
- void forEachDeclaredField(
- void Function(String name, FieldBuilder fieldBuilder) f);
-
- void forEachDeclaredConstructor(
- void Function(String name, ConstructorBuilder constructorBuilder)
- callback);
-
/// The [Class] built by this builder.
///
/// For a patch class the origin class is returned.
@@ -259,16 +247,12 @@
ClassBuilder? actualOrigin;
@override
- ClassBuilder? get patchForTesting => _patchBuilder;
-
- @override
bool isNullClass = false;
InterfaceType? _legacyRawType;
InterfaceType? _nullableRawType;
InterfaceType? _nonNullableRawType;
InterfaceType? _thisType;
- ClassBuilder? _patchBuilder;
ClassBuilderImpl(
List<MetadataBuilder>? metadata,
@@ -319,14 +303,6 @@
includeInjectedConstructors: includeInjectedConstructors);
} else {
constructors.forEach(f);
- if (includeInjectedConstructors) {
- _patchBuilder?.constructors
- .forEach((String name, MemberBuilder builder) {
- if (!builder.isPatch) {
- f(name, builder);
- }
- });
- }
}
}
@@ -416,57 +392,6 @@
}
@override
- void forEachDeclaredField(
- void Function(String name, FieldBuilder fieldBuilder) callback) {
- void callbackFilteringFieldBuilders(String name, Builder builder) {
- if (builder is FieldBuilder) {
- callback(name, builder);
- }
- }
-
- // Currently, fields can't be patched, but can be injected. When the fields
- // will be made available for patching, the following code should iterate
- // first over the fields from the patch and then -- over the fields in the
- // original declaration, filtering out the patched fields. For now, the
- // assert checks that the names of the fields from the original declaration
- // and from the patch don't intersect.
- assert(
- _patchBuilder == null ||
- _patchBuilder!.scope.localMembers
- .where((b) => b is FieldBuilder)
- .map((b) => (b as FieldBuilder).name)
- .toSet()
- .intersection(scope.localMembers
- .where((b) => b is FieldBuilder)
- .map((b) => (b as FieldBuilder).name)
- .toSet())
- .isEmpty,
- "Detected an attempt to patch a field.");
- _patchBuilder?.scope.forEach(callbackFilteringFieldBuilders);
- scope.forEach(callbackFilteringFieldBuilders);
- }
-
- @override
- void forEachDeclaredConstructor(
- void Function(
- String name, DeclaredSourceConstructorBuilder constructorBuilder)
- callback) {
- Set<String> visitedConstructorNames = {};
- void callbackFilteringFieldBuilders(String name, Builder builder) {
- if (builder is DeclaredSourceConstructorBuilder &&
- visitedConstructorNames.add(builder.name)) {
- callback(name, builder);
- }
- }
-
- // Constructors can be patched, so iterate first over constructors in the
- // patch, and then over constructors in the original declaration skipping
- // those with the names that are in the patch.
- _patchBuilder?.constructors.forEach(callbackFilteringFieldBuilders);
- constructors.forEach(callbackFilteringFieldBuilders);
- }
-
- @override
Builder? lookupLocalMember(String name,
{bool setter: false, bool required: false}) {
Builder? builder = scope.lookupLocalMember(name, setter: setter);
@@ -819,55 +744,6 @@
}
@override
- void applyPatch(Builder patch) {
- if (patch is ClassBuilder) {
- patch.actualOrigin = this;
- _patchBuilder = patch;
- // TODO(ahe): Complain if `patch.supertype` isn't null.
- scope.forEachLocalMember((String name, Builder member) {
- Builder? memberPatch =
- patch.scope.lookupLocalMember(name, setter: false);
- if (memberPatch != null) {
- member.applyPatch(memberPatch);
- }
- });
- scope.forEachLocalSetter((String name, Builder member) {
- Builder? memberPatch =
- patch.scope.lookupLocalMember(name, setter: true);
- if (memberPatch != null) {
- member.applyPatch(memberPatch);
- }
- });
- constructors.local.forEach((String name, Builder member) {
- Builder? memberPatch = patch.constructors.local[name];
- if (memberPatch != null) {
- member.applyPatch(memberPatch);
- }
- });
-
- int originLength = typeVariables?.length ?? 0;
- int patchLength = patch.typeVariables?.length ?? 0;
- if (originLength != patchLength) {
- patch.addProblem(messagePatchClassTypeVariablesMismatch,
- patch.charOffset, noLength, context: [
- messagePatchClassOrigin.withLocation(fileUri, charOffset, noLength)
- ]);
- } else if (typeVariables != null) {
- int count = 0;
- for (TypeVariableBuilder t in patch.typeVariables!) {
- typeVariables![count++].applyPatch(t);
- }
- }
- } else {
- library.addProblem(messagePatchDeclarationMismatch, patch.charOffset,
- noLength, patch.fileUri, context: [
- messagePatchDeclarationOrigin.withLocation(
- fileUri, charOffset, noLength)
- ]);
- }
- }
-
- @override
FunctionType? computeRedirecteeType(
RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment) {
ConstructorReferenceBuilder redirectionTarget = factory.redirectionTarget;
diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
index 725b34c..a5fd7b7 100644
--- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
@@ -5,46 +5,9 @@
library fasta.field_builder;
import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
import 'member_builder.dart';
-import 'metadata_builder.dart';
-import 'type_builder.dart';
abstract class FieldBuilder implements MemberBuilder {
Field get field;
-
- List<MetadataBuilder>? get metadata;
-
- TypeBuilder? get type;
-
- bool get isCovariantByDeclaration;
-
- bool get isLate;
-
- bool get hasInitializer;
-
- /// Whether the body of this field has been built.
- ///
- /// Constant fields have their initializer built in the outline so we avoid
- /// building them twice as part of the non-outline build.
- bool get hasBodyBeenBuilt;
-
- /// Builds the body of this field using [initializer] as the initializer
- /// expression.
- void buildBody(CoreTypes coreTypes, Expression? initializer);
-
- /// Builds the field initializers for each field used to encode this field
- /// using the [fileOffset] for the created nodes and [value] as the initial
- /// field value.
- List<Initializer> buildInitializer(int fileOffset, Expression value,
- {required bool isSynthetic});
-
- bool get isEligibleForInference;
-
- DartType get builtType;
-
- DartType inferType();
-
- DartType get fieldType;
}
diff --git a/pkg/front_end/lib/src/fasta/builder/formal_parameter_builder.dart b/pkg/front_end/lib/src/fasta/builder/formal_parameter_builder.dart
index 1503956..eefc5d1 100644
--- a/pkg/front_end/lib/src/fasta/builder/formal_parameter_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/formal_parameter_builder.dart
@@ -22,12 +22,12 @@
import '../modifier.dart';
import '../scope.dart' show Scope;
import '../source/source_factory_builder.dart';
+import '../source/source_field_builder.dart';
import '../source/source_library_builder.dart';
import '../util/helpers.dart' show DelayedActionPerformer;
import 'builder.dart';
import 'class_builder.dart';
import 'constructor_builder.dart';
-import 'field_builder.dart';
import 'library_builder.dart';
import 'metadata_builder.dart';
import 'modifier_builder.dart';
@@ -197,7 +197,7 @@
void finalizeInitializingFormal(ClassBuilder classBuilder) {
Builder? fieldBuilder = classBuilder.lookupLocalMember(name);
- if (fieldBuilder is FieldBuilder) {
+ if (fieldBuilder is SourceFieldBuilder) {
variable!.type = fieldBuilder.inferType();
} else {
variable!.type = const DynamicType();
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_member_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_member_builder.dart
index f3046a7..21cde4c 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_member_builder.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_member_builder.dart
@@ -9,6 +9,7 @@
import '../builder/builder.dart';
import '../builder/constructor_builder.dart';
+import '../builder/field_builder.dart';
import '../builder/member_builder.dart';
import '../builder/procedure_builder.dart';
@@ -89,7 +90,8 @@
: const <ClassMember>[];
}
-class DillFieldBuilder extends DillMemberBuilder {
+class DillFieldBuilder extends DillMemberBuilder implements FieldBuilder {
+ @override
final Field field;
DillFieldBuilder(this.field, Builder parent) : super(field, parent);
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 6ecde36..ed6468b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -88,6 +88,7 @@
import '../source/source_constructor_builder.dart';
import '../source/source_enum_builder.dart';
import '../source/source_factory_builder.dart';
+import '../source/source_field_builder.dart';
import '../source/source_function_builder.dart';
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../source/source_procedure_builder.dart';
@@ -790,7 +791,7 @@
debugEvent("finishFields");
assert(checkState(null, [/*field count*/ ValueKinds.Integer]));
int count = pop() as int;
- List<FieldBuilder> fields = <FieldBuilder>[];
+ List<SourceFieldBuilder> fields = [];
for (int i = 0; i < count; i++) {
assert(checkState(null, [
ValueKinds.FieldInitializerOrNull,
@@ -806,9 +807,9 @@
} else {
declaration = libraryBuilder.lookupLocalMember(name, required: true)!;
}
- FieldBuilder fieldBuilder;
+ SourceFieldBuilder fieldBuilder;
if (declaration.isField && declaration.next == null) {
- fieldBuilder = declaration as FieldBuilder;
+ fieldBuilder = declaration as SourceFieldBuilder;
} else {
continue;
}
@@ -3122,8 +3123,8 @@
classBuilder!.declaresConstConstructor
? ConstantContext.required
: ConstantContext.none;
- if (member is FieldBuilder) {
- FieldBuilder fieldBuilder = member as FieldBuilder;
+ if (member is SourceFieldBuilder) {
+ SourceFieldBuilder fieldBuilder = member as SourceFieldBuilder;
inLateFieldInitializer = fieldBuilder.isLate;
if (fieldBuilder.isAbstract) {
addProblem(
@@ -6855,7 +6856,8 @@
name.length),
fieldNameOffset)
];
- } else if (builder is FieldBuilder && builder.isDeclarationInstanceMember) {
+ } else if (builder is SourceFieldBuilder &&
+ builder.isDeclarationInstanceMember) {
initializedFields ??= <String, int>{};
if (initializedFields!.containsKey(name)) {
return <Initializer>[
@@ -6922,7 +6924,7 @@
uri,
context: [
fasta.messageInitializingFormalTypeMismatchField.withLocation(
- builder.fileUri!, builder.charOffset, noLength)
+ builder.fileUri, builder.charOffset, noLength)
]);
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index f2a6f7f..0073678 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1020,11 +1020,12 @@
/// Quotes below are from [Dart Programming Language Specification, 4th
/// Edition](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-408.pdf):
- List<FieldBuilder> uninitializedFields = <FieldBuilder>[];
- List<FieldBuilder> nonFinalFields = <FieldBuilder>[];
- List<FieldBuilder> lateFinalFields = <FieldBuilder>[];
+ List<SourceFieldBuilder> uninitializedFields = [];
+ List<SourceFieldBuilder> nonFinalFields = [];
+ List<SourceFieldBuilder> lateFinalFields = [];
- builder.forEachDeclaredField((String name, FieldBuilder fieldBuilder) {
+ builder
+ .forEachDeclaredField((String name, SourceFieldBuilder fieldBuilder) {
if (fieldBuilder.isAbstract || fieldBuilder.isExternal) {
// Skip abstract and external fields. These are abstract/external
// getters/setters and have no initialization.
@@ -1043,11 +1044,11 @@
// declared towards the beginning of the file) come last in the list.
// To report errors on the first definition of a field, we need to
// iterate until that last element.
- FieldBuilder earliest = fieldBuilder;
+ SourceFieldBuilder earliest = fieldBuilder;
Builder current = fieldBuilder;
while (current.next != null) {
current = current.next!;
- if (current is FieldBuilder && !fieldBuilder.hasInitializer) {
+ if (current is SourceFieldBuilder && !fieldBuilder.hasInitializer) {
earliest = current;
}
}
@@ -1131,7 +1132,7 @@
constructor.fileOffset, noLength,
context: nonFinalFields
.map((field) => messageConstConstructorNonFinalFieldCause
- .withLocation(field.fileUri!, field.charOffset, noLength))
+ .withLocation(field.fileUri, field.charOffset, noLength))
.toList());
nonFinalFields.clear();
}
@@ -1152,9 +1153,9 @@
}
}
- Map<ConstructorBuilder, Set<FieldBuilder>> constructorInitializedFields =
- new Map<ConstructorBuilder, Set<FieldBuilder>>.identity();
- Set<FieldBuilder>? initializedFields = null;
+ Map<ConstructorBuilder, Set<SourceFieldBuilder>>
+ constructorInitializedFields = new Map.identity();
+ Set<SourceFieldBuilder>? initializedFields = null;
builder.forEachDeclaredConstructor(
(String name, DeclaredSourceConstructorBuilder constructorBuilder) {
@@ -1179,15 +1180,17 @@
}
}
if (!isRedirecting) {
- Set<FieldBuilder> fields = earliest.takeInitializedFields() ?? const {};
+ Set<SourceFieldBuilder> fields =
+ earliest.takeInitializedFields() ?? const {};
constructorInitializedFields[earliest] = fields;
- (initializedFields ??= new Set<FieldBuilder>.identity()).addAll(fields);
+ (initializedFields ??= new Set<SourceFieldBuilder>.identity())
+ .addAll(fields);
}
});
// Run through all fields that aren't initialized by any constructor, and
// set their initializer to `null`.
- for (FieldBuilder fieldBuilder in uninitializedFields) {
+ for (SourceFieldBuilder fieldBuilder in uninitializedFields) {
if (initializedFields == null ||
!initializedFields!.contains(fieldBuilder)) {
bool uninitializedFinalOrNonNullableFieldIsError =
@@ -1197,7 +1200,7 @@
if (fieldBuilder.isFinal &&
uninitializedFinalOrNonNullableFieldIsError) {
String uri = '${fieldBuilder.library.importUri}';
- String file = fieldBuilder.fileUri!.pathSegments.last;
+ String file = fieldBuilder.fileUri.pathSegments.last;
if (uri == 'dart:html' ||
uri == 'dart:svg' ||
uri == 'dart:_native_typed_data' ||
@@ -1235,7 +1238,7 @@
// make sure that all other constructors also initialize them.
constructorInitializedFields.forEach((ConstructorBuilder constructorBuilder,
Set<FieldBuilder> fieldBuilders) {
- for (FieldBuilder fieldBuilder
+ for (SourceFieldBuilder fieldBuilder
in initializedFields!.difference(fieldBuilders)) {
if (!fieldBuilder.hasInitializer && !fieldBuilder.isLate) {
FieldInitializer initializer =
@@ -1253,7 +1256,7 @@
context: [
templateMissingImplementationCause
.withArguments(fieldBuilder.name)
- .withLocation(fieldBuilder.fileUri!,
+ .withLocation(fieldBuilder.fileUri,
fieldBuilder.charOffset, fieldBuilder.name.length)
]);
} else if (fieldBuilder.field.type is! InvalidType &&
@@ -1271,7 +1274,7 @@
context: [
templateMissingImplementationCause
.withArguments(fieldBuilder.name)
- .withLocation(fieldBuilder.fileUri!,
+ .withLocation(fieldBuilder.fileUri,
fieldBuilder.charOffset, fieldBuilder.name.length)
]);
}
@@ -1293,14 +1296,14 @@
// it's so.
assert(() {
Set<String> patchFieldNames = {};
- builder.forEachDeclaredField((String name, FieldBuilder fieldBuilder) {
+ builder
+ .forEachDeclaredField((String name, SourceFieldBuilder fieldBuilder) {
patchFieldNames.add(NameScheme.createFieldName(
FieldNameType.Field,
name,
isInstanceMember: fieldBuilder.isClassInstanceMember,
className: builder.name,
- isSynthesized:
- fieldBuilder is SourceFieldBuilder && fieldBuilder.isLateLowered,
+ isSynthesized: fieldBuilder.isLateLowered,
));
});
builder.forEach((String name, Builder builder) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index c9eacac..e645c59 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -98,6 +98,8 @@
@override
final bool isMacro;
+ SourceClassBuilder? _patchBuilder;
+
SourceClassBuilder(
List<MetadataBuilder>? metadata,
int modifiers,
@@ -125,6 +127,8 @@
actualCls.hasConstConstructor = declaresConstConstructor;
}
+ SourceClassBuilder? get patchForTesting => _patchBuilder;
+
@override
Class get cls => origin.actualCls;
@@ -310,6 +314,123 @@
return false;
}
+ @override
+ void forEachConstructor(void Function(String, MemberBuilder) f,
+ {bool includeInjectedConstructors: false}) {
+ if (isPatch) {
+ actualOrigin!.forEachConstructor(f,
+ includeInjectedConstructors: includeInjectedConstructors);
+ } else {
+ constructors.forEach(f);
+ if (includeInjectedConstructors) {
+ _patchBuilder?.constructors
+ .forEach((String name, MemberBuilder builder) {
+ if (!builder.isPatch) {
+ f(name, builder);
+ }
+ });
+ }
+ }
+ }
+
+ void forEachDeclaredField(
+ void Function(String name, SourceFieldBuilder fieldBuilder) callback) {
+ void callbackFilteringFieldBuilders(String name, Builder builder) {
+ if (builder is SourceFieldBuilder) {
+ callback(name, builder);
+ }
+ }
+
+ // Currently, fields can't be patched, but can be injected. When the fields
+ // will be made available for patching, the following code should iterate
+ // first over the fields from the patch and then -- over the fields in the
+ // original declaration, filtering out the patched fields. For now, the
+ // assert checks that the names of the fields from the original declaration
+ // and from the patch don't intersect.
+ assert(
+ _patchBuilder == null ||
+ _patchBuilder!.scope.localMembers
+ .where((b) => b is SourceFieldBuilder)
+ .map((b) => (b as SourceFieldBuilder).name)
+ .toSet()
+ .intersection(scope.localMembers
+ .where((b) => b is SourceFieldBuilder)
+ .map((b) => (b as SourceFieldBuilder).name)
+ .toSet())
+ .isEmpty,
+ "Detected an attempt to patch a field.");
+ _patchBuilder?.scope.forEach(callbackFilteringFieldBuilders);
+ scope.forEach(callbackFilteringFieldBuilders);
+ }
+
+ void forEachDeclaredConstructor(
+ void Function(
+ String name, DeclaredSourceConstructorBuilder constructorBuilder)
+ callback) {
+ Set<String> visitedConstructorNames = {};
+ void callbackFilteringFieldBuilders(String name, Builder builder) {
+ if (builder is DeclaredSourceConstructorBuilder &&
+ visitedConstructorNames.add(builder.name)) {
+ callback(name, builder);
+ }
+ }
+
+ // Constructors can be patched, so iterate first over constructors in the
+ // patch, and then over constructors in the original declaration skipping
+ // those with the names that are in the patch.
+ _patchBuilder?.constructors.forEach(callbackFilteringFieldBuilders);
+ constructors.forEach(callbackFilteringFieldBuilders);
+ }
+
+ @override
+ void applyPatch(Builder patch) {
+ if (patch is SourceClassBuilder) {
+ patch.actualOrigin = this;
+ _patchBuilder = patch;
+ // TODO(ahe): Complain if `patch.supertype` isn't null.
+ scope.forEachLocalMember((String name, Builder member) {
+ Builder? memberPatch =
+ patch.scope.lookupLocalMember(name, setter: false);
+ if (memberPatch != null) {
+ member.applyPatch(memberPatch);
+ }
+ });
+ scope.forEachLocalSetter((String name, Builder member) {
+ Builder? memberPatch =
+ patch.scope.lookupLocalMember(name, setter: true);
+ if (memberPatch != null) {
+ member.applyPatch(memberPatch);
+ }
+ });
+ constructors.local.forEach((String name, Builder member) {
+ Builder? memberPatch = patch.constructors.local[name];
+ if (memberPatch != null) {
+ member.applyPatch(memberPatch);
+ }
+ });
+
+ int originLength = typeVariables?.length ?? 0;
+ int patchLength = patch.typeVariables?.length ?? 0;
+ if (originLength != patchLength) {
+ patch.addProblem(messagePatchClassTypeVariablesMismatch,
+ patch.charOffset, noLength, context: [
+ messagePatchClassOrigin.withLocation(fileUri, charOffset, noLength)
+ ]);
+ } else if (typeVariables != null) {
+ int count = 0;
+ for (TypeVariableBuilder t in patch.typeVariables!) {
+ typeVariables![count++].applyPatch(t);
+ }
+ }
+ } else {
+ library.addProblem(messagePatchDeclarationMismatch, patch.charOffset,
+ noLength, patch.fileUri, context: [
+ messagePatchDeclarationOrigin.withLocation(
+ fileUri, charOffset, noLength)
+ ]);
+ }
+ }
+
TypeBuilder checkSupertype(TypeBuilder supertype) {
if (typeVariables == null) return supertype;
Message? message;
diff --git a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
index d6c3ae4..cb8ee92 100644
--- a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
@@ -10,7 +10,6 @@
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/constructor_builder.dart';
-import '../builder/field_builder.dart';
import '../builder/formal_parameter_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
@@ -46,6 +45,7 @@
import '../type_inference/type_inferrer.dart';
import '../type_inference/type_schema.dart';
import '../util/helpers.dart' show DelayedActionPerformer;
+import 'source_field_builder.dart';
import 'source_function_builder.dart';
abstract class SourceConstructorBuilder
@@ -56,7 +56,7 @@
final Constructor _constructor;
final Procedure? _constructorTearOff;
- Set<FieldBuilder>? _initializedFields;
+ Set<SourceFieldBuilder>? _initializedFields;
final int charOpenParenOffset;
@@ -656,7 +656,7 @@
///
/// The field can be initialized either via an initializing formal or via an
/// entry in the constructor initializer list.
- void registerInitializedField(FieldBuilder fieldBuilder) {
+ void registerInitializedField(SourceFieldBuilder fieldBuilder) {
(_initializedFields ??= {}).add(fieldBuilder);
}
@@ -665,8 +665,8 @@
/// Returns the set of fields previously registered via
/// [registerInitializedField] and passes on the ownership of the collection
/// to the caller.
- Set<FieldBuilder>? takeInitializedFields() {
- Set<FieldBuilder>? result = _initializedFields;
+ Set<SourceFieldBuilder>? takeInitializedFields() {
+ Set<SourceFieldBuilder>? result = _initializedFields;
_initializedFields = null;
return result;
}
diff --git a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
index 216861b..9dc50e7 100644
--- a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
@@ -255,7 +255,7 @@
referencesFromIndexed.lookupSetterReference(valuesName);
}
- FieldBuilder valuesBuilder = new SourceFieldBuilder(
+ SourceFieldBuilder valuesBuilder = new SourceFieldBuilder(
/* metadata = */ null,
listType,
"values",
@@ -546,9 +546,9 @@
if (enumConstantInfo != null) {
String constant = enumConstantInfo.name;
Builder declaration = firstMemberNamed(constant)!;
- FieldBuilder field;
+ SourceFieldBuilder field;
if (declaration.isField) {
- field = declaration as FieldBuilder;
+ field = declaration as SourceFieldBuilder;
} else {
continue;
}
@@ -604,9 +604,7 @@
bodyBuilder.typeInferrer.inferFieldInitializer(
bodyBuilder, const UnknownType(), initializer);
initializer = inferenceResult.expression;
- if (field is SourceFieldBuilder) {
- field.fieldType = inferenceResult.inferredType;
- }
+ field.fieldType = inferenceResult.inferredType;
}
field.buildBody(classHierarchy.coreTypes, initializer);
}
diff --git a/pkg/front_end/lib/src/fasta/source/source_field_builder.dart b/pkg/front_end/lib/src/fasta/source/source_field_builder.dart
index d864cd8..377b82c 100644
--- a/pkg/front_end/lib/src/fasta/source/source_field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_field_builder.dart
@@ -47,17 +47,18 @@
late FieldEncoding _fieldEncoding;
- @override
final List<MetadataBuilder>? metadata;
- @override
final TypeBuilder? type;
Token? _constInitializerToken;
bool hadTypesInferred = false;
- @override
+ /// Whether the body of this field has been built.
+ ///
+ /// Constant fields have their initializer built in the outline so we avoid
+ /// building them twice as part of the non-outline build.
bool hasBodyBeenBuilt = false;
// TODO(johnniwinther): [parent] is not trust-worthy for determining
@@ -291,16 +292,14 @@
@override
bool get isField => true;
- @override
bool get isLate => (modifiers & lateMask) != 0;
- @override
bool get isCovariantByDeclaration => (modifiers & covariantMask) != 0;
- @override
bool get hasInitializer => (modifiers & hasInitializerMask) != 0;
- @override
+ /// Builds the body of this field using [initializer] as the initializer
+ /// expression.
void buildBody(CoreTypes coreTypes, Expression? initializer) {
assert(!hasBodyBeenBuilt);
hasBodyBeenBuilt = true;
@@ -315,14 +314,15 @@
_fieldEncoding.createBodies(coreTypes, initializer);
}
- @override
+ /// Builds the field initializers for each field used to encode this field
+ /// using the [fileOffset] for the created nodes and [value] as the initial
+ /// field value.
List<Initializer> buildInitializer(int fileOffset, Expression value,
{required bool isSynthetic}) {
return _fieldEncoding.createInitializer(fileOffset, value,
isSynthetic: isSynthetic);
}
- @override
bool get isEligibleForInference {
return type == null && (hasInitializer || isClassInstanceMember);
}
@@ -425,7 +425,6 @@
_constInitializerToken = null;
}
- @override
DartType get fieldType => _fieldEncoding.type;
void set fieldType(DartType value) {
@@ -447,7 +446,6 @@
}
}
- @override
DartType inferType() {
SourceLibraryBuilder library = this.library;
if (fieldType is! ImplicitFieldType) {
@@ -486,7 +484,6 @@
return fieldType;
}
- @override
DartType get builtType => fieldType;
List<ClassMember>? _localMembers;
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 4070f2c..9aa1463 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -213,7 +213,7 @@
/// the error message is the corresponding value in the map.
Map<String, String?>? unserializableExports;
- List<FieldBuilder>? _implicitlyTypedFields;
+ List<SourceFieldBuilder>? _implicitlyTypedFields;
/// The language version of this library as defined by the language version
/// of the package it belongs to, if present, or the current language version
@@ -1310,7 +1310,7 @@
(library.problemsAsJson ??= <String>[])
.addAll(part.library.problemsAsJson!);
}
- List<FieldBuilder> partImplicitlyTypedFields = [];
+ List<SourceFieldBuilder> partImplicitlyTypedFields = [];
part.collectImplicitlyTypedFields(partImplicitlyTypedFields);
if (partImplicitlyTypedFields.isNotEmpty) {
if (_implicitlyTypedFields == null) {
@@ -3697,9 +3697,9 @@
count += computeDefaultTypesForVariables(member.typeVariables,
inErrorRecovery: issues.isNotEmpty);
} else {
- assert(member is FieldBuilder,
+ assert(member is SourceFieldBuilder,
"Unexpected class member $member (${member.runtimeType}).");
- TypeBuilder? fieldType = (member as FieldBuilder).type;
+ TypeBuilder? fieldType = (member as SourceFieldBuilder).type;
if (fieldType != null) {
List<NonSimplicityIssue> issues =
getInboundReferenceIssuesInType(fieldType);
@@ -3763,7 +3763,7 @@
reportIssues(issues);
count += computeDefaultTypesForVariables(member.typeVariables,
inErrorRecovery: issues.isNotEmpty);
- } else if (member is FieldBuilder) {
+ } else if (member is SourceFieldBuilder) {
if (member.type != null) {
_recursivelyReportGenericFunctionTypesAsBoundsForType(
member.type);
@@ -3773,7 +3773,7 @@
"Unexpected extension member $member (${member.runtimeType}).");
}
});
- } else if (declaration is FieldBuilder) {
+ } else if (declaration is SourceFieldBuilder) {
if (declaration.type != null) {
List<NonSimplicityIssue> issues =
getInboundReferenceIssuesInType(declaration.type);
@@ -4046,10 +4046,10 @@
}
void checkTypesInField(
- FieldBuilder fieldBuilder, TypeEnvironment typeEnvironment) {
+ SourceFieldBuilder fieldBuilder, TypeEnvironment typeEnvironment) {
// Check the bounds in the field's type.
checkBoundsInType(fieldBuilder.fieldType, typeEnvironment,
- fieldBuilder.fileUri!, fieldBuilder.charOffset,
+ fieldBuilder.fileUri, fieldBuilder.charOffset,
allowSuperBounded: true);
// Check that the field has an initializer if its type is potentially
@@ -4532,7 +4532,7 @@
Iterator<Builder> iterator = this.iterator;
while (iterator.moveNext()) {
Builder declaration = iterator.current;
- if (declaration is FieldBuilder) {
+ if (declaration is SourceFieldBuilder) {
checkTypesInField(declaration, typeEnvironment);
} else if (declaration is SourceProcedureBuilder) {
checkTypesInFunctionBuilder(declaration, typeEnvironment);
@@ -4776,8 +4776,8 @@
}
}
- void registerImplicitlyTypedField(FieldBuilder fieldBuilder) {
- (_implicitlyTypedFields ??= <FieldBuilder>[]).add(fieldBuilder);
+ void registerImplicitlyTypedField(SourceFieldBuilder fieldBuilder) {
+ (_implicitlyTypedFields ??= <SourceFieldBuilder>[]).add(fieldBuilder);
}
void collectImplicitlyTypedFields(
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 2b0ad49..2d2cac8 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -1992,7 +1992,7 @@
typeInferenceEngine.prepareTopLevel(coreTypes, hierarchy);
membersBuilder.computeTypes();
- List<FieldBuilder> allImplicitlyTypedFields = <FieldBuilder>[];
+ List<SourceFieldBuilder> allImplicitlyTypedFields = [];
for (SourceLibraryBuilder library in sourceLibraryBuilders) {
library.collectImplicitlyTypedFields(allImplicitlyTypedFields);
}
diff --git a/pkg/front_end/testcases/super_parameters/default_values.dart.strong.expect b/pkg/front_end/testcases/super_parameters/default_values.dart.strong.expect
index d27a628..29ffdbc 100644
--- a/pkg/front_end/testcases/super_parameters/default_values.dart.strong.expect
+++ b/pkg/front_end/testcases/super_parameters/default_values.dart.strong.expect
@@ -2,13 +2,10 @@
//
// Problems in library:
//
-// pkg/front_end/testcases/super_parameters/default_values.dart:51:11: Error: Expected ']' before this.
+// pkg/front_end/testcases/super_parameters/default_values.dart:51:17: Error: The parameter 'x' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
+// Try adding either an explicit non-'null' default value or the 'required' modifier.
// C5([int super.x]); // Error.
-// ^^^^^
-//
-// pkg/front_end/testcases/super_parameters/default_values.dart:61:10: Error: Expected ']' before this.
-// C6([int? super.x]); // Ok.
-// ^
+// ^
//
import self as self;
import "dart:core" as core;
@@ -68,8 +65,8 @@
;
}
class C5 extends self::S5 {
- constructor •([dynamic int = #C4]) → self::C5
- : super self::S5::•()
+ constructor •([core::int x = #C4]) → self::C5
+ : super self::S5::•(x)
;
}
class S6 extends core::Object {
@@ -80,8 +77,8 @@
}
class C6 extends self::S6 {
field core::int? b = null;
- constructor •([dynamic int = #C4]) → self::C6
- : super self::S6::•()
+ constructor •([core::int? x = #C4]) → self::C6
+ : super self::S6::•(x)
;
}
class S7 extends core::Object {
diff --git a/pkg/front_end/testcases/super_parameters/default_values.dart.strong.transformed.expect b/pkg/front_end/testcases/super_parameters/default_values.dart.strong.transformed.expect
index d27a628..29ffdbc 100644
--- a/pkg/front_end/testcases/super_parameters/default_values.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/super_parameters/default_values.dart.strong.transformed.expect
@@ -2,13 +2,10 @@
//
// Problems in library:
//
-// pkg/front_end/testcases/super_parameters/default_values.dart:51:11: Error: Expected ']' before this.
+// pkg/front_end/testcases/super_parameters/default_values.dart:51:17: Error: The parameter 'x' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
+// Try adding either an explicit non-'null' default value or the 'required' modifier.
// C5([int super.x]); // Error.
-// ^^^^^
-//
-// pkg/front_end/testcases/super_parameters/default_values.dart:61:10: Error: Expected ']' before this.
-// C6([int? super.x]); // Ok.
-// ^
+// ^
//
import self as self;
import "dart:core" as core;
@@ -68,8 +65,8 @@
;
}
class C5 extends self::S5 {
- constructor •([dynamic int = #C4]) → self::C5
- : super self::S5::•()
+ constructor •([core::int x = #C4]) → self::C5
+ : super self::S5::•(x)
;
}
class S6 extends core::Object {
@@ -80,8 +77,8 @@
}
class C6 extends self::S6 {
field core::int? b = null;
- constructor •([dynamic int = #C4]) → self::C6
- : super self::S6::•()
+ constructor •([core::int? x = #C4]) → self::C6
+ : super self::S6::•(x)
;
}
class S7 extends core::Object {
diff --git a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.expect b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.expect
index d27a628..29ffdbc 100644
--- a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.expect
+++ b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.expect
@@ -2,13 +2,10 @@
//
// Problems in library:
//
-// pkg/front_end/testcases/super_parameters/default_values.dart:51:11: Error: Expected ']' before this.
+// pkg/front_end/testcases/super_parameters/default_values.dart:51:17: Error: The parameter 'x' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
+// Try adding either an explicit non-'null' default value or the 'required' modifier.
// C5([int super.x]); // Error.
-// ^^^^^
-//
-// pkg/front_end/testcases/super_parameters/default_values.dart:61:10: Error: Expected ']' before this.
-// C6([int? super.x]); // Ok.
-// ^
+// ^
//
import self as self;
import "dart:core" as core;
@@ -68,8 +65,8 @@
;
}
class C5 extends self::S5 {
- constructor •([dynamic int = #C4]) → self::C5
- : super self::S5::•()
+ constructor •([core::int x = #C4]) → self::C5
+ : super self::S5::•(x)
;
}
class S6 extends core::Object {
@@ -80,8 +77,8 @@
}
class C6 extends self::S6 {
field core::int? b = null;
- constructor •([dynamic int = #C4]) → self::C6
- : super self::S6::•()
+ constructor •([core::int? x = #C4]) → self::C6
+ : super self::S6::•(x)
;
}
class S7 extends core::Object {
diff --git a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.modular.expect b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.modular.expect
index d27a628..29ffdbc 100644
--- a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.modular.expect
@@ -2,13 +2,10 @@
//
// Problems in library:
//
-// pkg/front_end/testcases/super_parameters/default_values.dart:51:11: Error: Expected ']' before this.
+// pkg/front_end/testcases/super_parameters/default_values.dart:51:17: Error: The parameter 'x' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
+// Try adding either an explicit non-'null' default value or the 'required' modifier.
// C5([int super.x]); // Error.
-// ^^^^^
-//
-// pkg/front_end/testcases/super_parameters/default_values.dart:61:10: Error: Expected ']' before this.
-// C6([int? super.x]); // Ok.
-// ^
+// ^
//
import self as self;
import "dart:core" as core;
@@ -68,8 +65,8 @@
;
}
class C5 extends self::S5 {
- constructor •([dynamic int = #C4]) → self::C5
- : super self::S5::•()
+ constructor •([core::int x = #C4]) → self::C5
+ : super self::S5::•(x)
;
}
class S6 extends core::Object {
@@ -80,8 +77,8 @@
}
class C6 extends self::S6 {
field core::int? b = null;
- constructor •([dynamic int = #C4]) → self::C6
- : super self::S6::•()
+ constructor •([core::int? x = #C4]) → self::C6
+ : super self::S6::•(x)
;
}
class S7 extends core::Object {
diff --git a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.outline.expect b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.outline.expect
index 9cb4ff2..aca3fd0 100644
--- a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.outline.expect
@@ -2,13 +2,10 @@
//
// Problems in library:
//
-// pkg/front_end/testcases/super_parameters/default_values.dart:51:11: Error: Expected ']' before this.
+// pkg/front_end/testcases/super_parameters/default_values.dart:51:17: Error: The parameter 'x' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
+// Try adding either an explicit non-'null' default value or the 'required' modifier.
// C5([int super.x]); // Error.
-// ^^^^^
-//
-// pkg/front_end/testcases/super_parameters/default_values.dart:61:10: Error: Expected ']' before this.
-// C6([int? super.x]); // Ok.
-// ^
+// ^
//
import self as self;
import "dart:core" as core;
@@ -59,7 +56,7 @@
;
}
class C5 extends self::S5 {
- constructor •([dynamic int]) → self::C5
+ constructor •([core::int x]) → self::C5
;
}
class S6 extends core::Object {
@@ -69,7 +66,7 @@
}
class C6 extends self::S6 {
field core::int? b;
- constructor •([dynamic int]) → self::C6
+ constructor •([core::int? x]) → self::C6
;
}
class S7 extends core::Object {
diff --git a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.transformed.expect b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.transformed.expect
index d27a628..29ffdbc 100644
--- a/pkg/front_end/testcases/super_parameters/default_values.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/super_parameters/default_values.dart.weak.transformed.expect
@@ -2,13 +2,10 @@
//
// Problems in library:
//
-// pkg/front_end/testcases/super_parameters/default_values.dart:51:11: Error: Expected ']' before this.
+// pkg/front_end/testcases/super_parameters/default_values.dart:51:17: Error: The parameter 'x' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
+// Try adding either an explicit non-'null' default value or the 'required' modifier.
// C5([int super.x]); // Error.
-// ^^^^^
-//
-// pkg/front_end/testcases/super_parameters/default_values.dart:61:10: Error: Expected ']' before this.
-// C6([int? super.x]); // Ok.
-// ^
+// ^
//
import self as self;
import "dart:core" as core;
@@ -68,8 +65,8 @@
;
}
class C5 extends self::S5 {
- constructor •([dynamic int = #C4]) → self::C5
- : super self::S5::•()
+ constructor •([core::int x = #C4]) → self::C5
+ : super self::S5::•(x)
;
}
class S6 extends core::Object {
@@ -80,8 +77,8 @@
}
class C6 extends self::S6 {
field core::int? b = null;
- constructor •([dynamic int = #C4]) → self::C6
- : super self::S6::•()
+ constructor •([core::int? x = #C4]) → self::C6
+ : super self::S6::•(x)
;
}
class S7 extends core::Object {
diff --git a/pkg/vm/bin/compare_il.dart b/pkg/vm/bin/compare_il.dart
index 5b9caa3..7545b08 100644
--- a/pkg/vm/bin/compare_il.dart
+++ b/pkg/vm/bin/compare_il.dart
@@ -29,13 +29,14 @@
final graphs = _loadGraphs(ilFile, rename);
final tests = await _loadTestCases(testFile);
- Map<String, FlowGraph> findMatchingGraphs(String name) {
- final suffix = '_${rename(name)}';
+ Map<String, FlowGraph> findMatchingGraphs(String name, String? closureName) {
+ final closureSuffix = closureName != null ? '_${rename(closureName)}' : '';
+ final suffix = '_${rename(name)}${closureSuffix}';
return graphs.entries.firstWhere((f) => f.key.contains(suffix)).value;
}
for (var test in tests) {
- test.run(findMatchingGraphs(test.name));
+ test.run(findMatchingGraphs(test.name, test.closureName));
}
exit(0); // Success.
@@ -43,6 +44,7 @@
class TestCase {
final String name;
+ final String? closureName;
final String phasesFilter;
final LibraryMirror library;
@@ -51,13 +53,16 @@
TestCase({
required this.name,
+ this.closureName,
required this.phasesFilter,
required this.library,
});
+ late final fullName = name + (closureName != null ? '_$closureName' : '');
+
void run(Map<String, FlowGraph> graphs) {
- print('matching IL (${phases.join(', ')}) for $name');
- library.invoke(MirrorSystem.getSymbol('matchIL\$$name'),
+ print('matching IL (${phases.join(', ')}) for $fullName');
+ library.invoke(MirrorSystem.getSymbol('matchIL\$$fullName'),
phases.map((phase) => graphs[phase]!).toList());
print('... ok');
}
@@ -104,20 +109,35 @@
.firstWhereOrNull((p) => p.name == name);
final cases = LinkedHashSet<TestCase>(
- equals: (a, b) => a.name == b.name,
- hashCode: (a) => a.name.hashCode,
+ equals: (a, b) => a.fullName == b.fullName,
+ hashCode: (a) => a.fullName.hashCode,
);
void processDeclaration(DeclarationMirror decl) {
- final p = getPragma(decl, 'vm:testing:print-flow-graph');
+ TestCase? testCase;
+ pragma? p = getPragma(decl, 'vm:testing:print-flow-graph');
if (p != null) {
final name = MirrorSystem.getName(decl.simpleName);
- final added = cases.add(TestCase(
+ testCase = TestCase(
name: name,
phasesFilter: (p.options as String?) ?? 'AllocateRegisters',
library: library,
- ));
- if (!added) throw 'duplicate test case with name $name';
+ );
+ }
+ p = getPragma(decl, 'vm:testing:match-inner-flow-graph');
+ if (p != null) {
+ final name = MirrorSystem.getName(decl.simpleName);
+ final closureName = p.options as String;
+ testCase = TestCase(
+ name: name,
+ closureName: closureName,
+ phasesFilter: 'AllocateRegisters',
+ library: library,
+ );
+ }
+ if (testCase != null) {
+ final added = cases.add(testCase);
+ if (!added) throw 'duplicate test case with name ${testCase.fullName}';
}
}
diff --git a/runtime/docs/infra/il_tests.md b/runtime/docs/infra/il_tests.md
index 94c4500..c8c2b40 100644
--- a/runtime/docs/infra/il_tests.md
+++ b/runtime/docs/infra/il_tests.md
@@ -36,6 +36,10 @@
Actual matching is done by the `pkg/vm/tool/compare_il` script.
+In order to test IL of the inner (local) function, use
+`@pragma('vm:testing:match-inner-flow-graph', 'inner name')`.
+Specifying a particular phase is not supported for inner closures.
+
## Example
```dart
@@ -54,4 +58,15 @@
]),
]);
}
-```
\ No newline at end of file
+
+@pragma('vm:testing:match-inner-flow-graph', 'bar')
+void foo() {
+ @pragma('vm:testing:print-flow-graph')
+ bar() {
+ }
+}
+
+void matchIL$foo_bar(FlowGraph graph) {
+ // Test IL of local bar() in foo().
+}
+```
diff --git a/runtime/tests/vm/dart/typed_data_aot_not_inlining_il_test.dart b/runtime/tests/vm/dart/typed_data_aot_not_inlining_il_test.dart
new file mode 100644
index 0000000..4b50aea
--- /dev/null
+++ b/runtime/tests/vm/dart/typed_data_aot_not_inlining_il_test.dart
@@ -0,0 +1,47 @@
+// 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.
+
+// This test asserts that we are not inlining accesses to typed data interfaces
+// (e.g. Uint8List) if there are instantiated 3rd party classes (e.g.
+// UnmodifiableUint8ListView).
+
+import 'dart:typed_data';
+import 'package:vm/testing/il_matchers.dart';
+
+createThirdPartyUint8List() => UnmodifiableUint8ListView(Uint8List(10));
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph')
+void foo(Uint8List list, int from) {
+ if (from >= list.length) {
+ list[from];
+ }
+}
+
+void matchIL$foo(FlowGraph graph) {
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'list' << match.Parameter(index: 0),
+ 'from' << match.Parameter(index: 1),
+ 'v13' << match.LoadClassId('list'),
+ match.PushArgument('list'),
+ match.DispatchTableCall('v13', selector_name: 'get:length'),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '>='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'v15' << match.LoadClassId('list'),
+ match.PushArgument('list'),
+ match.PushArgument(/* BoxInt64(Parameter) or Parameter */),
+ match.DispatchTableCall('v15', selector_name: '[]'),
+ ]),
+ ]);
+}
+
+void main() {
+ foo(int.parse('1') == 1 ? createThirdPartyUint8List() : Uint8List(1),
+ int.parse('0'));
+}
diff --git a/runtime/tests/vm/dart/typed_data_aot_regress43534_il_test.dart b/runtime/tests/vm/dart/typed_data_aot_regress43534_il_test.dart
new file mode 100644
index 0000000..50fe78c
--- /dev/null
+++ b/runtime/tests/vm/dart/typed_data_aot_regress43534_il_test.dart
@@ -0,0 +1,35 @@
+// 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:typed_data';
+import 'package:vm/testing/il_matchers.dart';
+
+@pragma('vm:never-inline')
+void callWith<T>(void Function(T arg) fun, T arg) {
+ fun(arg);
+}
+
+@pragma('vm:testing:match-inner-flow-graph', 'foo')
+void main() {
+ @pragma('vm:testing:print-flow-graph')
+ foo(Uint8List list) {
+ if (list[0] != 0) throw 'a';
+ }
+
+ callWith<Uint8List>(foo, Uint8List(10));
+}
+
+void matchIL$main(FlowGraph graph) {}
+
+void matchIL$main_foo(FlowGraph graph) {
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ match.LoadField(),
+ match.GenericCheckBound(),
+ match.LoadUntagged(),
+ match.LoadIndexed(),
+ ]),
+ ]);
+}
diff --git a/runtime/tests/vm/dart_2/typed_data_aot_not_inlining_il_test.dart b/runtime/tests/vm/dart_2/typed_data_aot_not_inlining_il_test.dart
new file mode 100644
index 0000000..4b50aea
--- /dev/null
+++ b/runtime/tests/vm/dart_2/typed_data_aot_not_inlining_il_test.dart
@@ -0,0 +1,47 @@
+// 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.
+
+// This test asserts that we are not inlining accesses to typed data interfaces
+// (e.g. Uint8List) if there are instantiated 3rd party classes (e.g.
+// UnmodifiableUint8ListView).
+
+import 'dart:typed_data';
+import 'package:vm/testing/il_matchers.dart';
+
+createThirdPartyUint8List() => UnmodifiableUint8ListView(Uint8List(10));
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph')
+void foo(Uint8List list, int from) {
+ if (from >= list.length) {
+ list[from];
+ }
+}
+
+void matchIL$foo(FlowGraph graph) {
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'list' << match.Parameter(index: 0),
+ 'from' << match.Parameter(index: 1),
+ 'v13' << match.LoadClassId('list'),
+ match.PushArgument('list'),
+ match.DispatchTableCall('v13', selector_name: 'get:length'),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '>='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'v15' << match.LoadClassId('list'),
+ match.PushArgument('list'),
+ match.PushArgument(/* BoxInt64(Parameter) or Parameter */),
+ match.DispatchTableCall('v15', selector_name: '[]'),
+ ]),
+ ]);
+}
+
+void main() {
+ foo(int.parse('1') == 1 ? createThirdPartyUint8List() : Uint8List(1),
+ int.parse('0'));
+}
diff --git a/runtime/tests/vm/dart_2/typed_data_aot_regress43534_il_test.dart b/runtime/tests/vm/dart_2/typed_data_aot_regress43534_il_test.dart
new file mode 100644
index 0000000..50fe78c
--- /dev/null
+++ b/runtime/tests/vm/dart_2/typed_data_aot_regress43534_il_test.dart
@@ -0,0 +1,35 @@
+// 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:typed_data';
+import 'package:vm/testing/il_matchers.dart';
+
+@pragma('vm:never-inline')
+void callWith<T>(void Function(T arg) fun, T arg) {
+ fun(arg);
+}
+
+@pragma('vm:testing:match-inner-flow-graph', 'foo')
+void main() {
+ @pragma('vm:testing:print-flow-graph')
+ foo(Uint8List list) {
+ if (list[0] != 0) throw 'a';
+ }
+
+ callWith<Uint8List>(foo, Uint8List(10));
+}
+
+void matchIL$main(FlowGraph graph) {}
+
+void matchIL$main_foo(FlowGraph graph) {
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ match.LoadField(),
+ match.GenericCheckBound(),
+ match.LoadUntagged(),
+ match.LoadIndexed(),
+ ]),
+ ]);
+}
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 1dbf213..22ff2d3 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -1954,136 +1954,153 @@
}
}
-static void UnboxPhi(PhiInstr* phi, bool is_aot) {
- Representation unboxed = phi->representation();
+namespace {
+class PhiUnboxingHeuristic : public ValueObject {
+ public:
+ explicit PhiUnboxingHeuristic(FlowGraph* flow_graph)
+ : worklist_(flow_graph, 10) {}
- switch (phi->Type()->ToCid()) {
- case kDoubleCid:
- if (CanUnboxDouble()) {
- unboxed = kUnboxedDouble;
- }
- break;
- case kFloat32x4Cid:
- if (ShouldInlineSimd()) {
- unboxed = kUnboxedFloat32x4;
- }
- break;
- case kInt32x4Cid:
- if (ShouldInlineSimd()) {
- unboxed = kUnboxedInt32x4;
- }
- break;
- case kFloat64x2Cid:
- if (ShouldInlineSimd()) {
- unboxed = kUnboxedFloat64x2;
- }
- break;
- }
+ void Process(PhiInstr* phi) {
+ Representation unboxed = phi->representation();
- // If all the inputs are unboxed, leave the Phi unboxed.
- if ((unboxed == kTagged) && phi->Type()->IsInt()) {
- bool should_unbox = true;
- Representation new_representation = kTagged;
- for (intptr_t i = 0; i < phi->InputCount(); i++) {
- Definition* input = phi->InputAt(i)->definition();
- if (input->representation() != kUnboxedInt64 &&
- input->representation() != kUnboxedInt32 &&
- input->representation() != kUnboxedUint32 && !(input == phi)) {
- should_unbox = false;
+ switch (phi->Type()->ToCid()) {
+ case kDoubleCid:
+ if (CanUnboxDouble()) {
+ unboxed = kUnboxedDouble;
+ }
break;
- }
-
- if (new_representation == kTagged) {
- new_representation = input->representation();
- } else if (new_representation != input->representation()) {
- new_representation = kNoRepresentation;
- }
- }
- if (should_unbox) {
- unboxed = new_representation != kNoRepresentation
- ? new_representation
- : RangeUtils::Fits(phi->range(),
- RangeBoundary::kRangeBoundaryInt32)
- ? kUnboxedInt32
- : kUnboxedInt64;
- }
- }
-
- if ((unboxed == kTagged) && phi->Type()->IsInt() &&
- !phi->Type()->can_be_sentinel()) {
- // Conservatively unbox phis that:
- // - are proven to be of type Int;
- // - fit into 64bits range;
- // - have either constants or Box() operations as inputs;
- // - have at least one Box() operation as an input;
- // - are used in at least 1 Unbox() operation.
- bool should_unbox = false;
- for (intptr_t i = 0; i < phi->InputCount(); i++) {
- Definition* input = phi->InputAt(i)->definition();
- if (input->IsBox()) {
- should_unbox = true;
- } else if (!input->IsConstant()) {
- should_unbox = false;
+ case kFloat32x4Cid:
+ if (ShouldInlineSimd()) {
+ unboxed = kUnboxedFloat32x4;
+ }
break;
- }
+ case kInt32x4Cid:
+ if (ShouldInlineSimd()) {
+ unboxed = kUnboxedInt32x4;
+ }
+ break;
+ case kFloat64x2Cid:
+ if (ShouldInlineSimd()) {
+ unboxed = kUnboxedFloat64x2;
+ }
+ break;
}
- if (should_unbox) {
- // We checked inputs. Check if phi is used in at least one unbox
- // operation.
- bool has_unboxed_use = false;
- for (Value* use = phi->input_use_list(); use != NULL;
- use = use->next_use()) {
- Instruction* instr = use->instruction();
- if (instr->IsUnbox()) {
- has_unboxed_use = true;
+ // If all the inputs are unboxed, leave the Phi unboxed.
+ if ((unboxed == kTagged) && phi->Type()->IsInt()) {
+ bool should_unbox = true;
+ Representation new_representation = kTagged;
+ for (auto input : phi->inputs()) {
+ if (input == phi) continue;
+
+ if (!IsUnboxedInteger(input->representation())) {
+ should_unbox = false;
break;
- } else if (IsUnboxedInteger(
- instr->RequiredInputRepresentation(use->use_index()))) {
- has_unboxed_use = true;
- break;
+ }
+
+ if (new_representation == kTagged) {
+ new_representation = input->representation();
+ } else if (new_representation != input->representation()) {
+ new_representation = kNoRepresentation;
}
}
- if (!has_unboxed_use) {
- should_unbox = false;
+ if (should_unbox) {
+ unboxed =
+ new_representation != kNoRepresentation ? new_representation
+ : RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32)
+ ? kUnboxedInt32
+ : kUnboxedInt64;
}
}
- if (should_unbox) {
- unboxed =
- RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32)
- ? kUnboxedInt32
- : kUnboxedInt64;
- }
- }
-
+ // Decide if it is worth to unbox an integer phi.
+ if ((unboxed == kTagged) && phi->Type()->IsInt() &&
+ !phi->Type()->can_be_sentinel()) {
#if defined(TARGET_ARCH_IS_64_BIT)
- // In AOT mode on 64-bit platforms always unbox integer typed phis (similar
- // to how we treat doubles and other boxed numeric types).
- // In JIT mode only unbox phis which are not fully known to be Smi.
- if ((unboxed == kTagged) && phi->Type()->IsInt() &&
- !phi->Type()->can_be_sentinel() &&
- (is_aot || phi->Type()->ToCid() != kSmiCid)) {
- unboxed = kUnboxedInt64;
- }
-#endif
+ // In AOT mode on 64-bit platforms always unbox integer typed phis
+ // (similar to how we treat doubles and other boxed numeric types).
+ // In JIT mode only unbox phis which are not fully known to be Smi.
+ if (is_aot_ || phi->Type()->ToCid() != kSmiCid) {
+ unboxed = kUnboxedInt64;
+ }
+#else
+ // If we are on a 32-bit platform check if there are unboxed values
+ // flowing into the phi and the phi value itself is flowing into an
+ // unboxed operation prefer to keep it unboxed.
+ // We use this heuristic instead of eagerly unboxing all the phis
+ // because we are concerned about the code size and register pressure.
+ const bool has_unboxed_incomming_value = HasUnboxedIncommingValue(phi);
+ const bool flows_into_unboxed_use = FlowsIntoUnboxedUse(phi);
- phi->set_representation(unboxed);
-}
+ if (has_unboxed_incomming_value && flows_into_unboxed_use) {
+ unboxed =
+ RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32)
+ ? kUnboxedInt32
+ : kUnboxedInt64;
+ }
+#endif
+ }
+
+ phi->set_representation(unboxed);
+ }
+
+ private:
+ // Returns |true| iff there is an unboxed definition among all potential
+ // definitions that can flow into the |phi|.
+ // This function looks through phis.
+ bool HasUnboxedIncommingValue(PhiInstr* phi) {
+ worklist_.Clear();
+ worklist_.Add(phi);
+ for (intptr_t i = 0; i < worklist_.definitions().length(); i++) {
+ const auto defn = worklist_.definitions()[i];
+ for (auto input : defn->inputs()) {
+ if (IsUnboxedInteger(input->representation()) || input->IsBox()) {
+ return true;
+ } else if (input->IsPhi()) {
+ worklist_.Add(input);
+ }
+ }
+ }
+ return false;
+ }
+
+ // Returns |true| iff |phi| potentially flows into an unboxed use.
+ // This function looks through phis.
+ bool FlowsIntoUnboxedUse(PhiInstr* phi) {
+ worklist_.Clear();
+ worklist_.Add(phi);
+ for (intptr_t i = 0; i < worklist_.definitions().length(); i++) {
+ const auto defn = worklist_.definitions()[i];
+ for (auto use : defn->input_uses()) {
+ if (IsUnboxedInteger(use->instruction()->RequiredInputRepresentation(
+ use->use_index())) ||
+ use->instruction()->IsUnbox()) {
+ return true;
+ } else if (auto phi_use = use->instruction()->AsPhi()) {
+ worklist_.Add(phi_use);
+ }
+ }
+ }
+ return false;
+ }
+
+ const bool is_aot_ = CompilerState::Current().is_aot();
+ DefinitionWorklist worklist_;
+};
+} // namespace
void FlowGraph::SelectRepresentations() {
- const auto is_aot = CompilerState::Current().is_aot();
-
// First we decide for each phi if it is beneficial to unbox it. If so, we
// change it's `phi->representation()`
+ PhiUnboxingHeuristic phi_unboxing_heuristic(this);
for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
block_it.Advance()) {
JoinEntryInstr* join_entry = block_it.Current()->AsJoinEntry();
if (join_entry != NULL) {
for (PhiIterator it(join_entry); !it.Done(); it.Advance()) {
PhiInstr* phi = it.Current();
- UnboxPhi(phi, is_aot);
+ phi_unboxing_heuristic.Process(phi);
}
}
}
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index c261131..719d0d9 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -62,7 +62,6 @@
class Range;
class RangeAnalysis;
class RangeBoundary;
-class SuccessorsIterable;
class TypeUsageInfo;
class UnboxIntegerInstr;
@@ -769,6 +768,64 @@
typedef ZoneGrowableArray<Value*> InputsArray;
typedef ZoneGrowableArray<PushArgumentInstr*> PushArgumentsArray;
+template <typename Trait>
+class InstructionIndexedPropertyIterable {
+ public:
+ struct Iterator {
+ const Instruction* instr;
+ intptr_t index;
+
+ decltype(Trait::At(instr, index)) operator*() const {
+ return Trait::At(instr, index);
+ }
+ Iterator& operator++() {
+ index++;
+ return *this;
+ }
+
+ bool operator==(const Iterator& other) {
+ return instr == other.instr && index == other.index;
+ }
+
+ bool operator!=(const Iterator& other) { return !(*this == other); }
+ };
+
+ explicit InstructionIndexedPropertyIterable(const Instruction* instr)
+ : instr_(instr) {}
+
+ Iterator begin() const { return {instr_, 0}; }
+ Iterator end() const { return {instr_, Trait::Length(instr_)}; }
+
+ private:
+ const Instruction* instr_;
+};
+
+class ValueListIterable {
+ public:
+ struct Iterator {
+ Value* value;
+
+ Value* operator*() const { return value; }
+
+ Iterator& operator++() {
+ value = value->next_use();
+ return *this;
+ }
+
+ bool operator==(const Iterator& other) { return value == other.value; }
+
+ bool operator!=(const Iterator& other) { return !(*this == other); }
+ };
+
+ explicit ValueListIterable(Value* value) : value_(value) {}
+
+ Iterator begin() const { return {value_}; }
+ Iterator end() const { return {nullptr}; }
+
+ private:
+ Value* value_;
+};
+
class Instruction : public ZoneAllocated {
public:
#define DECLARE_TAG(type, attrs) k##type,
@@ -828,6 +885,20 @@
RawSetInputAt(i, value);
}
+ struct InputsTrait {
+ static Definition* At(const Instruction* instr, intptr_t index) {
+ return instr->InputAt(index)->definition();
+ }
+
+ static intptr_t Length(const Instruction* instr) {
+ return instr->InputCount();
+ }
+ };
+
+ using InputsIterable = InstructionIndexedPropertyIterable<InputsTrait>;
+
+ InputsIterable inputs() { return InputsIterable(this); }
+
// Remove all inputs (including in the environment) from their
// definition's use lists.
void UnuseAllInputs();
@@ -917,7 +988,22 @@
virtual intptr_t SuccessorCount() const;
virtual BlockEntryInstr* SuccessorAt(intptr_t index) const;
- inline SuccessorsIterable successors() const;
+ struct SuccessorsTrait {
+ static BlockEntryInstr* At(const Instruction* instr, intptr_t index) {
+ return instr->SuccessorAt(index);
+ }
+
+ static intptr_t Length(const Instruction* instr) {
+ return instr->SuccessorCount();
+ }
+ };
+
+ using SuccessorsIterable =
+ InstructionIndexedPropertyIterable<SuccessorsTrait>;
+
+ inline SuccessorsIterable successors() const {
+ return SuccessorsIterable(this);
+ }
void Goto(JoinEntryInstr* entry);
@@ -2297,6 +2383,10 @@
Value* env_use_list() const { return env_use_list_; }
void set_env_use_list(Value* head) { env_use_list_ = head; }
+ ValueListIterable input_uses() const {
+ return ValueListIterable(input_use_list_);
+ }
+
void AddInputUse(Value* value) { Value::AddToList(value, &input_use_list_); }
void AddEnvUse(Value* value) { Value::AddToList(value, &env_use_list_); }
@@ -4384,9 +4474,13 @@
const compiler::TableSelector* selector);
DECLARE_INSTRUCTION(DispatchTableCall)
+ DECLARE_ATTRIBUTES(selector_name())
const Function& interface_target() const { return interface_target_; }
const compiler::TableSelector* selector() const { return selector_; }
+ const char* selector_name() const {
+ return String::Handle(interface_target().name()).ToCString();
+ }
Value* class_id() const { return InputAt(InputCount() - 1); }
@@ -9764,37 +9858,6 @@
return (constant == nullptr) || constant->value().ptr() == value.ptr();
}
-class SuccessorsIterable {
- public:
- struct Iterator {
- const Instruction* instr;
- intptr_t index;
-
- BlockEntryInstr* operator*() const { return instr->SuccessorAt(index); }
- Iterator& operator++() {
- index++;
- return *this;
- }
-
- bool operator==(const Iterator& other) {
- return instr == other.instr && index == other.index;
- }
-
- bool operator!=(const Iterator& other) { return !(*this == other); }
- };
-
- explicit SuccessorsIterable(const Instruction* instr) : instr_(instr) {}
-
- Iterator begin() const { return {instr_, 0}; }
- Iterator end() const { return {instr_, instr_->SuccessorCount()}; }
-
- private:
- const Instruction* instr_;
-};
-
-SuccessorsIterable Instruction::successors() const {
- return SuccessorsIterable(this);
-}
} // namespace dart
diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc
index 5d332bc..caa35ac 100644
--- a/runtime/vm/compiler/backend/il_test_helper.cc
+++ b/runtime/vm/compiler/backend/il_test_helper.cc
@@ -94,10 +94,6 @@
FlowGraph* TestPipeline::RunPasses(
std::initializer_list<CompilerPass::Id> passes) {
- // The table dispatch transformation needs a precompiler, which is not
- // available in the test pipeline.
- SetFlagScope<bool> sfs(&FLAG_use_table_dispatch, false);
-
auto thread = Thread::Current();
auto zone = thread->zone();
const bool optimized = true;
diff --git a/runtime/vm/compiler/backend/loops_test.cc b/runtime/vm/compiler/backend/loops_test.cc
index 1722f3b..dec8c01 100644
--- a/runtime/vm/compiler/backend/loops_test.cc
+++ b/runtime/vm/compiler/backend/loops_test.cc
@@ -387,15 +387,7 @@
const char* expected =
" [0\n"
" LIN(9223372036854775806 + 1 * i)\n" // phi
-#if !defined(TARGET_ARCH_IS_64_BIT)
- " LIN(9223372036854775806 + 1 * i)\n" // (un)boxing
- " LIN(9223372036854775806 + 1 * i)\n"
- " LIN(9223372036854775806 + 1 * i)\n"
-#endif // !defined(TARGET_ARCH_IS_64_BIT)
" LIN(9223372036854775807 + 1 * i)\n" // add
-#if !defined(TARGET_ARCH_IS_64_BIT)
- " LIN(9223372036854775807 + 1 * i)\n" // unbox
-#endif // !defined(TARGET_ARCH_IS_64_BIT)
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
@@ -434,15 +426,7 @@
const char* expected =
" [0\n"
" LIN(-9223372036854775807 + -1 * i)\n" // phi
-#if !defined(TARGET_ARCH_IS_64_BIT)
- " LIN(-9223372036854775807 + -1 * i)\n" // (un)boxing
- " LIN(-9223372036854775807 + -1 * i)\n"
- " LIN(-9223372036854775807 + -1 * i)\n"
-#endif // !defined(TARGET_ARCH_IS_64_BIT)
" LIN(-9223372036854775808 + -1 * i)\n" // sub
-#if !defined(TARGET_ARCH_IS_64_BIT)
- " LIN(-9223372036854775808 + -1 * i)\n" // unbox
-#endif // !defined(TARGET_ARCH_IS_64_BIT)
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
diff --git a/runtime/vm/compiler/backend/typed_data_aot_test.cc b/runtime/vm/compiler/backend/typed_data_aot_test.cc
index 557139a..1087c72 100644
--- a/runtime/vm/compiler/backend/typed_data_aot_test.cc
+++ b/runtime/vm/compiler/backend/typed_data_aot_test.cc
@@ -85,62 +85,6 @@
EXPECT(load_indexed->InputAt(0)->definition() == load_untagged);
}
-// This test asserts that we are not inlining accesses to typed data interfaces
-// (e.g. Uint8List) if there are instantiated 3rd party classes (e.g.
-// UnmodifiableUint8ListView).
-ISOLATE_UNIT_TEST_CASE(IRTest_TypedDataAOT_NotInlining) {
- const char* kScript =
- R"(
- import 'dart:typed_data';
-
- createThirdPartyUint8List() => UnmodifiableUint8ListView(Uint8List(10));
-
- void foo(Uint8List list, int from) {
- if (from >= list.length) {
- list[from];
- }
- }
- )";
-
- const auto& root_library = Library::Handle(LoadTestScript(kScript));
-
- // Firstly we ensure a non internal/external/view Uint8List is allocated.
- Invoke(root_library, "createThirdPartyUint8List");
-
- // Now we ensure that we don't perform the inlining of the `list[from]`
- // access.
- const auto& function = Function::Handle(GetFunction(root_library, "foo"));
- TestPipeline pipeline(function, CompilerPass::kAOT);
- FlowGraph* flow_graph = pipeline.RunPasses({});
-
- auto entry = flow_graph->graph_entry()->normal_entry();
- EXPECT(entry != nullptr);
-
- InstanceCallInstr* length_call = nullptr;
- PushArgumentInstr* pusharg1 = nullptr;
- PushArgumentInstr* pusharg2 = nullptr;
- InstanceCallInstr* index_get_call = nullptr;
-
- ILMatcher cursor(flow_graph, entry);
- RELEASE_ASSERT(cursor.TryMatch({
- kMoveGlob,
- {kMatchAndMoveInstanceCall, &length_call},
- kMoveGlob,
- kMatchAndMoveBranchTrue,
- kMoveGlob,
- {kMatchAndMovePushArgument, &pusharg1},
- {kMatchAndMovePushArgument, &pusharg2},
- {kMatchAndMoveInstanceCall, &index_get_call},
- kMoveGlob,
- kMatchReturn,
- }));
-
- EXPECT(length_call->Selector() == Symbols::GetLength().ptr());
- EXPECT(pusharg1->InputAt(0)->definition()->IsParameter());
- EXPECT(pusharg2->InputAt(0)->definition()->IsParameter());
- EXPECT(index_get_call->Selector() == Symbols::IndexToken().ptr());
-}
-
// This test asserts that we are inlining get:length, [] and []= for all typed
// data interfaces. It also ensures that the asserted IR actually works by
// exercising it.
@@ -448,51 +392,6 @@
}
}
-ISOLATE_UNIT_TEST_CASE(IRTest_TypedDataAOT_Regress43534) {
- const char* kScript =
- R"(
- import 'dart:typed_data';
-
- @pragma('vm:never-inline')
- void callWith<T>(void Function(T arg) fun, T arg) {
- fun(arg);
- }
-
- void test() {
- callWith<Uint8List>((Uint8List list) {
- if (list[0] != 0) throw 'a';
- }, Uint8List(10));
- }
- )";
-
- const auto& root_library = Library::Handle(LoadTestScript(kScript));
- Invoke(root_library, "test");
- const auto& test_function =
- Function::Handle(GetFunction(root_library, "test"));
-
- const auto& function = Function::Handle(
- ClosureFunctionsCache::GetUniqueInnerClosure(test_function));
- RELEASE_ASSERT(function.IsFunction());
-
- TestPipeline pipeline(function, CompilerPass::kAOT);
- FlowGraph* flow_graph = pipeline.RunPasses({});
-
- auto entry = flow_graph->graph_entry()->normal_entry();
- EXPECT(entry != nullptr);
- ILMatcher cursor(flow_graph, entry, /*trace=*/true);
- RELEASE_ASSERT(cursor.TryMatch(
- {
- kMatchAndMoveLoadField,
- kMatchAndMoveGenericCheckBound,
- kMatchAndMoveLoadUntagged,
- kMatchAndMoveLoadIndexed,
- kMatchAndMoveBranchFalse,
- kMoveGlob,
- kMatchReturn,
- },
- kMoveGlob));
-}
-
#endif // defined(DART_PRECOMPILER)
} // namespace dart
diff --git a/tools/VERSION b/tools/VERSION
index 777b780..8faa73f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 151
+PRERELEASE 152
PRERELEASE_PATCH 0
\ No newline at end of file