Version 3.6.0-109.0.dev
Merge 786742f34cb4a55e6cfbc349a0918c97b54aa43a into dev
diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart
index 55e4020..6d6d000 100644
--- a/pkg/dart2wasm/lib/async.dart
+++ b/pkg/dart2wasm/lib/async.dart
@@ -10,9 +10,7 @@
import 'code_generator.dart';
import 'state_machine.dart';
-class AsyncCodeGenerator extends StateMachineEntryCodeGenerator {
- AsyncCodeGenerator(super.translator, super.function, super.reference);
-
+mixin AsyncCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
late final ClassInfo asyncSuspendStateInfo =
translator.classInfo[translator.asyncSuspendStateClass]!;
@@ -76,9 +74,9 @@
b.return_();
b.end();
- AsyncStateMachineCodeGenerator(translator, resumeFun, reference,
+ AsyncStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
functionNode, functionSource, closures)
- .generate();
+ .generate(resumeFun.locals.toList(), null);
}
w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
@@ -98,11 +96,26 @@
"${function.functionName} inner");
}
+/// Generates code for async procedures.
+class AsyncProcedureCodeGenerator
+ extends ProcedureStateMachineEntryCodeGenerator
+ with AsyncCodeGeneratorMixin {
+ AsyncProcedureCodeGenerator(
+ super.translator, super.function, super.enclosingMember);
+}
+
+/// Generates code for async closures.
+class AsyncLambdaCodeGenerator extends LambdaStateMachineEntryCodeGenerator
+ with AsyncCodeGeneratorMixin {
+ AsyncLambdaCodeGenerator(
+ super.translator, super.enclosingMember, super.lambda, super.closures);
+}
+
class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator {
AsyncStateMachineCodeGenerator(
super.translator,
super.function,
- super.reference,
+ super.enclosingMember,
super.functionNode,
super.functionSource,
super.closures);
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index aa3bf24..b8c65b6 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -21,6 +21,22 @@
import 'translator.dart';
import 'types.dart';
+abstract class CodeGenerator {
+ // The two parameters here are used for inlining:
+ //
+ // If the user
+ //
+ // * inlines the code, it will provide locals and a return label
+ //
+ // * doesn't inline (i.e. makes new function with this code) it will provide
+ // the parameters of the function and no return label.
+ //
+ void generate(List<w.Local> paramLocals, w.Label? returnLabel);
+
+ // TODO(kustermann): Remove this again.
+ Closures get closures;
+}
+
/// Main code generator for member bodies.
///
/// The [generate] method first collects all local functions and function
@@ -36,19 +52,24 @@
/// code generation for every expression or subexpression is done via the [wrap]
/// method, which emits appropriate conversion code if the produced type is not
/// a subtype of the expected type.
-class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
+abstract class AstCodeGenerator
+ extends ExpressionVisitor1<w.ValueType, w.ValueType>
with ExpressionVisitor1DefaultMixin<w.ValueType, w.ValueType>
- implements InitializerVisitor<void>, StatementVisitor<void> {
+ implements InitializerVisitor<void>, StatementVisitor<void>, CodeGenerator {
final Translator translator;
final w.FunctionType functionType;
final w.InstructionsBuilder b;
- final Reference reference;
+ final Member enclosingMember;
+
+ // To be initialized in `generate()`
late final List<w.Local> paramLocals;
- final w.Label? returnLabel;
+ late final w.Label? returnLabel;
- late final Intrinsifier intrinsifier;
- late final StaticTypeContext typeContext;
+ late final Intrinsifier intrinsifier = Intrinsifier(this);
+ late final StaticTypeContext typeContext =
+ StaticTypeContext(enclosingMember, translator.typeEnvironment);
+ @override
late final Closures closures;
bool exceptionLocationPrinted = false;
@@ -82,45 +103,11 @@
{};
/// Create a code generator for a member or one of its lambdas.
- ///
- /// The [paramLocals] and [returnLabel] parameters can be used to generate
- /// code for an inlined function by specifying the locals containing the
- /// parameters (instead of the function inputs) and the label to jump to on
- /// return (instead of emitting a `return` instruction).
- CodeGenerator(this.translator, this.functionType, this.b, this.reference,
- {required this.paramLocals, this.returnLabel}) {
- intrinsifier = Intrinsifier(this);
- typeContext = StaticTypeContext(member, translator.typeEnvironment);
- }
-
- /// Factory constructor for instantiating a code generator appropriate for
- /// generating code for the given function. This will either return a
- /// [CodeGenerator] or a [SyncStarCodeGenerator].
- factory CodeGenerator.forFunction(
- Translator translator,
- FunctionNode? functionNode,
- w.FunctionBuilder function,
- Reference reference) {
- bool isSyncStar = functionNode?.asyncMarker == AsyncMarker.SyncStar &&
- !reference.isTearOffReference;
- bool isAsync = functionNode?.asyncMarker == AsyncMarker.Async &&
- !reference.isTearOffReference;
- bool isTypeChecker = reference.isTypeCheckerReference;
-
- if (!isTypeChecker && isSyncStar) {
- return SyncStarCodeGenerator(translator, function, reference);
- } else if (!isTypeChecker && isAsync) {
- return AsyncCodeGenerator(translator, function, reference);
- } else {
- return CodeGenerator(translator, function.type, function.body, reference,
- paramLocals: function.locals.toList());
- }
- }
+ AstCodeGenerator(
+ this.translator, this.functionType, this.b, this.enclosingMember);
w.ModuleBuilder get m => translator.m;
- Member get member => reference.asMember;
-
List<w.ValueType> get outputs => functionType.outputs;
w.ValueType get returnType => translator.outputOrVoid(outputs);
@@ -206,8 +193,8 @@
final location = source.getLocation(fileUri, fileOffset);
final old = _sourceMapFileOffset;
_sourceMapFileOffset = fileOffset;
- b.startSourceMapping(
- fileUri, location.line - 1, location.column - 1, member.name.text);
+ b.startSourceMapping(fileUri, location.line - 1, location.column - 1,
+ enclosingMember.name.text);
return old;
}
@@ -219,152 +206,18 @@
return (oldSource, oldFileOffset);
}
- /// Generate code for the member.
- void generate() {
- Member member = this.member;
+ /// Generate code while preventing recursive inlining.
+ @override
+ void generate(List<w.Local> paramLocals, w.Label? returnLabel) {
+ this.paramLocals = paramLocals;
+ this.returnLabel = returnLabel;
- if (member is Constructor) {
- // Closures are built when constructor functions are added to worklist.
- closures = translator.constructorClosures[member.reference]!;
- } else {
- // Build closure information.
- closures = Closures(translator, this.member);
- }
-
- if (reference.isTearOffReference) {
- return generateTearOffGetter(member as Procedure);
- }
-
- if (reference.isTypeCheckerReference) {
- if (member is Field || (member is Procedure && member.isSetter)) {
- return _generateFieldSetterTypeCheckerMethod();
- } else {
- return _generateProcedureTypeCheckerMethod();
- }
- }
-
- if (intrinsifier.generateMemberIntrinsic(
- reference, functionType, paramLocals, returnLabel)) {
- b.end();
- return;
- }
-
- if (member.isExternal) {
- b.comment("Unimplemented external member $member at ${member.location}");
- if (member.isInstanceMember) {
- b.local_get(paramLocals[0]);
- } else {
- b.ref_null(w.HeapType.none);
- }
- translator.constants.instantiateConstant(
- b,
- SymbolConstant(member.name.text, null),
- translator.classInfo[translator.symbolClass]!.nonNullableType);
- call(translator
- .noSuchMethodErrorThrowUnimplementedExternalMemberError.reference);
- b.unreachable();
- b.end();
- return;
- }
-
- final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
- setSourceMapSourceAndFileOffset(source, member.fileOffset);
-
- if (member is Constructor) {
- translator.membersBeingGenerated.add(member);
- if (reference.isConstructorBodyReference) {
- generateConstructorBody(reference);
- } else if (reference.isInitializerReference) {
- generateInitializerList(reference);
- } else {
- generateConstructorAllocator(member);
- }
- translator.membersBeingGenerated.remove(member);
- return;
- }
-
- if (member is Field) {
- if (member.isStatic) {
- return generateStaticFieldInitializer(member);
- } else {
- return generateImplicitAccessor(member);
- }
- }
-
- assert(member.function!.asyncMarker != AsyncMarker.SyncStar);
- assert(member.function!.asyncMarker != AsyncMarker.Async);
-
- translator.membersBeingGenerated.add(member);
- generateBody(member);
- translator.membersBeingGenerated.remove(member);
+ translator.membersBeingGenerated.add(enclosingMember);
+ generateInternal();
+ translator.membersBeingGenerated.remove(enclosingMember);
}
- void generateTearOffGetter(Procedure procedure) {
- _initializeThis(member.reference);
- DartType functionType = translator.getTearOffType(procedure);
- ClosureImplementation closure = translator.getTearOffClosure(procedure);
- w.StructType struct = closure.representation.closureStruct;
-
- ClassInfo info = translator.closureInfo;
- translator.functions.recordClassAllocation(info.classId);
-
- b.i32_const(info.classId);
- b.i32_const(initialIdentityHash);
- b.local_get(paramLocals[0]); // `this` as context
- b.global_get(closure.vtable);
- types.makeType(this, functionType);
- b.struct_new(struct);
- b.end();
- }
-
- void generateStaticFieldInitializer(Field field) {
- // Static field initializer function
- assert(reference == field.fieldReference);
- closures.findCaptures(field);
- closures.collectContexts(field);
- closures.buildContexts();
-
- w.Global global = translator.globals.getGlobal(field);
- w.Global? flag = translator.globals.getGlobalInitializedFlag(field);
- wrap(field.initializer!, global.type.type);
- b.global_set(global);
- if (flag != null) {
- b.i32_const(1);
- b.global_set(flag);
- }
- b.global_get(global);
- translator.convertType(b, global.type.type, outputs.single);
- b.end();
- }
-
- void generateImplicitAccessor(Field field) {
- // Implicit getter or setter
- w.StructType struct = translator.classInfo[field.enclosingClass!]!.struct;
- int fieldIndex = translator.fieldIndex[field]!;
- w.ValueType fieldType = struct.fields[fieldIndex].type.unpacked;
-
- void getThis() {
- w.Local thisLocal = paramLocals[0];
- w.RefType structType = w.RefType.def(struct, nullable: false);
- b.local_get(thisLocal);
- translator.convertType(b, thisLocal.type, structType);
- }
-
- if (reference.isImplicitGetter) {
- // Implicit getter
- getThis();
- b.struct_get(struct, fieldIndex);
- translator.convertType(b, fieldType, returnType);
- } else {
- // Implicit setter
- w.Local valueLocal = paramLocals[1];
- getThis();
- b.local_get(valueLocal);
- translator.convertType(b, valueLocal.type, fieldType);
- b.struct_set(struct, fieldIndex);
- }
- b.end();
- }
+ void generateInternal();
void _setupLocalParameters(Member member, ParameterInfo paramInfo,
int parameterOffset, int implicitParams,
@@ -506,8 +359,8 @@
isForwarder: isForwarder);
}
- void setupParametersAndContexts(Reference reference) {
- setupParameters(reference);
+ void setupParametersAndContexts(Member member) {
+ setupParameters(member.reference);
closures.findCaptures(member);
closures.collectContexts(member);
@@ -517,37 +370,6 @@
captureParameters();
}
- void setupInitializerListParametersAndContexts(Reference reference) {
- setupParameters(reference, isForwarder: true);
- allocateContext(member);
- captureParameters();
- }
-
- void setupConstructorBodyParametersAndContexts(Reference reference) {
- Constructor member = reference.asConstructor;
- ParameterInfo paramInfo = translator.paramInfoForDirectCall(reference);
-
- // For constructor body functions, the first parameter is always the
- // receiver parameter, and the second parameter is a reference to the
- // current context (if it exists).
- Context? context = closures.contexts[member];
- bool hasConstructorContext = context != null;
-
- if (hasConstructorContext) {
- assert(!context.isEmpty);
- _initializeContextLocals(member, contextParamIndex: 1);
- }
-
- // Skips the receiver param (_initializeThis will return 1), and the
- // context param if this exists.
- int parameterOffset =
- _initializeThis(reference) + (hasConstructorContext ? 1 : 0);
- int implicitParams = parameterOffset + paramInfo.typeParamCount;
-
- _setupLocalParameters(member, paramInfo, parameterOffset, implicitParams);
- allocateContext(member.function);
- }
-
void _setupDefaultFieldValues(ClassInfo info) {
fieldLocals.clear();
@@ -569,62 +391,6 @@
}
}
- List<w.Local> _generateInitializers(Constructor member) {
- Class cls = member.enclosingClass;
- ClassInfo info = translator.classInfo[cls]!;
- List<w.Local> superclassFields = [];
-
- _setupDefaultFieldValues(info);
-
- // Generate initializer list
- for (Initializer initializer in member.initializers) {
- visitInitializer(initializer);
-
- if (initializer is SuperInitializer) {
- // Save super classes' fields to locals
- ClassInfo superInfo = info.superInfo!;
-
- for (w.ValueType outputType
- in superInfo.getClassFieldTypes().reversed) {
- w.Local local = addLocal(outputType);
- b.local_set(local);
- superclassFields.add(local);
- }
- } else if (initializer is RedirectingInitializer) {
- // Save redirected classes' fields to locals
- List<w.Local> redirectedFields = [];
-
- for (w.ValueType outputType in info.getClassFieldTypes().reversed) {
- w.Local local = addLocal(outputType);
- b.local_set(local);
- redirectedFields.add(local);
- }
-
- return redirectedFields.reversed.toList();
- }
- }
-
- List<w.Local> typeFields = [];
-
- for (TypeParameter typeParam in cls.typeParameters) {
- TypeParameter? match = info.typeParameterMatch[typeParam];
-
- if (match == null) {
- // Type is not contained in super class' fields
- typeFields.add(typeLocals[typeParam]!);
- }
- }
-
- List<w.Local> orderedFieldLocals = Map.fromEntries(
- fieldLocals.entries.toList()
- ..sort((x, y) => translator.fieldIndex[x.key]!
- .compareTo(translator.fieldIndex[y.key]!)))
- .values
- .toList();
-
- return superclassFields.reversed.toList() + typeFields + orderedFieldLocals;
- }
-
List<w.Local> _getConstructorArgumentLocals(Reference target,
[reverse = false]) {
Constructor member = target.asConstructor;
@@ -661,113 +427,6 @@
return constructorArgs;
}
- void generateBody(Member member) {
- assert(member is! Constructor);
- setupParametersAndContexts(member.reference);
-
- Statement? body = member.function!.body;
- if (body != null) {
- visitStatement(body);
- }
-
- _implicitReturn();
- b.end();
- }
-
- // Generates a function for allocating an object. This calls the separate
- // initializer list and constructor body methods, and allocates a struct for
- // the object.
- void generateConstructorAllocator(Constructor member) {
- setupParameters(member.reference, isForwarder: true);
-
- w.FunctionType initializerMethodType =
- translator.signatureForDirectCall(member.initializerReference);
-
- List<w.Local> constructorArgs =
- _getConstructorArgumentLocals(member.reference);
-
- for (w.Local local in constructorArgs) {
- b.local_get(local);
- }
-
- b.comment("Direct call of '$member Initializer'");
- call(member.initializerReference);
-
- ClassInfo info = translator.classInfo[member.enclosingClass]!;
-
- // Add evaluated fields to locals
- List<w.Local> orderedFieldLocals = [];
-
- List<w.FieldType> fieldTypes = info.struct.fields
- .sublist(FieldIndex.objectFieldBase)
- .reversed
- .toList();
-
- for (w.FieldType field in fieldTypes) {
- w.Local local = addLocal(field.type.unpacked);
- orderedFieldLocals.add(local);
- b.local_set(local);
- }
-
- Context? context = closures.contexts[member];
- w.Local? contextLocal;
-
- bool hasContext = context != null;
-
- if (hasContext) {
- assert(!context.isEmpty);
- w.ValueType contextRef = w.RefType.struct(nullable: true);
- contextLocal = addLocal(contextRef);
- b.local_set(contextLocal);
- }
-
- List<w.ValueType> initializerOutputTypes = initializerMethodType.outputs;
- int numConstructorBodyArgs = initializerOutputTypes.length -
- fieldTypes.length -
- (hasContext ? 1 : 0);
-
- // Pop all arguments to constructor body
- List<w.ValueType> constructorArgTypes =
- initializerOutputTypes.sublist(0, numConstructorBodyArgs);
-
- List<w.Local> constructorArguments = [];
-
- for (w.ValueType argType in constructorArgTypes.reversed) {
- w.Local local = addLocal(argType);
- b.local_set(local);
- constructorArguments.add(local);
- }
-
- // Set field values
- b.i32_const(info.classId);
- b.i32_const(initialIdentityHash);
-
- for (w.Local local in orderedFieldLocals.reversed) {
- b.local_get(local);
- }
-
- // create new struct with these fields and set to local
- w.Local temp = addLocal(info.nonNullableType);
- b.struct_new(info.struct);
- b.local_tee(temp);
-
- // Push context local if it is present
- if (contextLocal != null) {
- b.local_get(contextLocal);
- }
-
- // Push all constructor arguments
- for (w.Local constructorArg in constructorArguments) {
- b.local_get(constructorArg);
- }
-
- b.comment("Direct call of $member Constructor Body");
- call(member.constructorBodyReference);
-
- b.local_get(temp);
- b.end();
- }
-
void setupLambdaParametersAndContexts(Lambda lambda) {
FunctionNode functionNode = lambda.functionNode;
_initializeContextLocals(functionNode);
@@ -787,22 +446,6 @@
captureParameters();
}
- /// Generate code for the body of a lambda.
- void generateLambda(Lambda lambda, Closures closures) {
- // Initialize closure information from enclosing member.
- this.closures = closures;
-
- setSourceMapSource(lambda.functionNodeSource);
-
- assert(lambda.functionNode.asyncMarker != AsyncMarker.Async);
-
- setupLambdaParametersAndContexts(lambda);
-
- visitStatement(lambda.functionNode.body!);
- _implicitReturn();
- b.end();
- }
-
/// Initialize locals containing `this` in constructors and instance members.
/// Returns the number of parameter locals taken up by the receiver parameter,
/// i.e. the parameter offset for the first type parameter (or the first
@@ -1005,9 +648,11 @@
}
List<w.ValueType> call(Reference target) {
- if (translator.shouldInline(target)) {
- w.FunctionType targetFunctionType =
- translator.signatureForDirectCall(target);
+ w.FunctionType targetFunctionType =
+ translator.signatureForDirectCall(target);
+ final inliningCodeGen =
+ translator.getInliningCodeGenerator(target, targetFunctionType, b);
+ if (inliningCodeGen != null) {
List<w.Local> inlinedLocals =
targetFunctionType.inputs.map((t) => addLocal(t)).toList();
for (w.Local local in inlinedLocals.reversed) {
@@ -1015,9 +660,7 @@
}
w.Label block = b.block(const [], targetFunctionType.outputs);
b.comment("Inlined ${target.asMember}");
- CodeGenerator(translator, targetFunctionType, b, target,
- paramLocals: inlinedLocals, returnLabel: block)
- .generate();
+ inliningCodeGen.generate(inlinedLocals, block);
return targetFunctionType.outputs;
} else {
w.BaseFunction targetFunction = translator.functions.getFunction(target);
@@ -1879,7 +1522,7 @@
Member _lookupSuperTarget(Member interfaceTarget, {required bool setter}) {
return translator.hierarchy.getDispatchTarget(
- member.enclosingClass!.superclass!, interfaceTarget.name,
+ enclosingMember.enclosingClass!.superclass!, interfaceTarget.name,
setter: setter)!;
}
@@ -3187,22 +2830,17 @@
w.ValueType instantiateTypeParameter(TypeParameter parameter) {
w.ValueType resultType;
- // `this` will not be initialized yet for constructor initializer lists
- if (parameter.declaration is GenericFunction ||
- reference.isInitializerReference) {
- // Type argument to function
- w.Local? local = typeLocals[parameter];
- if (local != null) {
- b.local_get(local);
- resultType = local.type;
- } else {
- Capture capture = closures.captures[parameter]!;
- b.local_get(capture.context.currentLocal);
- b.struct_get(capture.context.struct, capture.fieldIndex);
- resultType = capture.type;
- }
+ w.Local? local = typeLocals[parameter];
+ Capture? capture = closures.captures[parameter];
+ if (local != null) {
+ b.local_get(local);
+ resultType = local.type;
+ } else if (capture != null) {
+ Capture capture = closures.captures[parameter]!;
+ b.local_get(capture.context.currentLocal);
+ b.struct_get(capture.context.struct, capture.fieldIndex);
+ resultType = capture.type;
} else {
- // Type argument of class
Class cls = parameter.declaration as Class;
ClassInfo info = translator.classInfo[cls]!;
int fieldIndex = translator.typeParameterIndex[parameter]!;
@@ -3210,6 +2848,7 @@
b.struct_get(info.struct, fieldIndex);
resultType = info.struct.fields[fieldIndex].type.unpacked;
}
+
translator.convertType(b, resultType, types.nonNullableTypeType);
return types.nonNullableTypeType;
}
@@ -3267,199 +2906,317 @@
return wrap(node.expression, expectedType);
}
- // Generates a function for a constructor's body, where the allocated struct
- // object is passed to this function.
- void generateConstructorBody(Reference target) {
- assert(target.isConstructorBodyReference);
- Constructor member = target.asConstructor;
+ /// Generate code that checks type of an argument against an expected type
+ /// and throws a `TypeError` on failure.
+ ///
+ /// Does not expect any values on stack and does not leave any values on
+ /// stack.
+ ///
+ /// Locals [argLocal] are used to store values pushed by [pushArg]
+ /// and reuse the values.
+ ///
+ /// [argName] is used in the type error as the name of the argument that
+ /// doesn't match the expected type.
+ void _generateArgumentTypeCheck(
+ String argName,
+ void Function() pushArg,
+ DartType testedAgainstType,
+ w.Local argLocal,
+ ) {
+ if (translator.options.minify) {
+ // We don't need to include the name in the error message, so we can use
+ // the optimized `as` checks.
+ pushArg();
+ types.emitAsCheck(
+ this,
+ testedAgainstType,
+ translator.coreTypes.objectNullableRawType,
+ argLocal.type as w.RefType);
+ b.drop();
+ } else {
+ pushArg();
+ b.local_tee(argLocal);
+ types.emitIsTest(
+ this, testedAgainstType, translator.coreTypes.objectNullableRawType);
+ b.i32_eqz();
+ b.if_();
+ b.local_get(argLocal);
+ types.makeType(this, testedAgainstType);
+ _emitString(argName);
+ call(translator.stackTraceCurrent.reference);
+ call(translator.throwArgumentTypeCheckError.reference);
+ b.unreachable();
+ b.end();
+ }
+ }
- setupConstructorBodyParametersAndContexts(target);
+ void _generateTypeArgumentBoundCheck(
+ String argName,
+ w.Local typeLocal,
+ DartType bound,
+ ) {
+ b.local_get(typeLocal);
+ final boundLocal =
+ b.addLocal(translator.classInfo[translator.typeClass]!.nonNullableType);
+ types.makeType(this, bound);
+ b.local_tee(boundLocal);
+ call(translator.isTypeSubtype.reference);
- int getStartIndexForSuperOrRedirectedConstructorArguments() {
- // Skips the receiver param and the current constructor's context
- // (if it exists)
- Context? context = closures.contexts[member];
- bool hasContext = context != null;
+ b.i32_eqz();
+ b.if_();
+ // Type check failed
+ b.local_get(typeLocal);
+ b.local_get(boundLocal);
+ _emitString(argName);
+ call(translator.stackTraceCurrent.reference);
+ call(translator.throwTypeArgumentBoundCheckError.reference);
+ b.unreachable();
+ b.end();
+ }
- if (hasContext) {
- assert(!context.isEmpty);
- }
+ void _emitString(String str) => wrap(StringLiteral(str),
+ translator.translateType(translator.coreTypes.stringNonNullableRawType));
- int numSkippedParams = hasContext ? 2 : 1;
+ @override
+ void visitPatternSwitchStatement(PatternSwitchStatement node) {
+ // This node is internal to the front end and removed by the constant
+ // evaluator.
+ throw UnsupportedError("CodeGenerator.visitPatternSwitchStatement");
+ }
- // Skips the current constructor's arguments
- int numConstructorArgs = _getConstructorArgumentLocals(target).length;
+ @override
+ void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
+ // This node is internal to the front end and removed by the constant
+ // evaluator.
+ throw UnsupportedError("CodeGenerator.visitPatternVariableDeclaration");
+ }
- return numSkippedParams + numConstructorArgs;
+ @override
+ void visitIfCaseStatement(IfCaseStatement node) {
+ // This node is internal to the front end and removed by the constant
+ // evaluator.
+ throw UnsupportedError("CodeGenerator.visitIfCaseStatement");
+ }
+
+ void debugRuntimePrint(String s) {
+ final printFunction =
+ translator.functions.getFunction(translator.printToConsole.reference);
+ translator.constants.instantiateConstant(
+ b, StringConstant(s), printFunction.type.inputs[0]);
+ b.call(printFunction);
+ }
+
+ @override
+ void visitAuxiliaryStatement(AuxiliaryStatement node) {
+ throw UnsupportedError(
+ "Unsupported auxiliary statement $node (${node.runtimeType}).");
+ }
+
+ @override
+ void visitAuxiliaryInitializer(AuxiliaryInitializer node) {
+ throw UnsupportedError(
+ "Unsupported auxiliary initializer $node (${node.runtimeType}).");
+ }
+
+ void emitUnimplementedExternalError(Member member) {
+ b.comment("Unimplemented external member $member at ${member.location}");
+ if (member.isInstanceMember) {
+ b.local_get(paramLocals[0]);
+ } else {
+ b.ref_null(w.HeapType.none);
+ }
+ translator.constants.instantiateConstant(
+ b,
+ SymbolConstant(member.name.text, null),
+ translator.classInfo[translator.symbolClass]!.nonNullableType);
+ call(translator
+ .noSuchMethodErrorThrowUnimplementedExternalMemberError.reference);
+ b.unreachable();
+ }
+}
+
+CodeGenerator getMemberCodeGenerator(Translator translator,
+ w.FunctionBuilder functionBuilder, Reference memberReference) {
+ final member = memberReference.asMember;
+ final asyncMarker = member.function?.asyncMarker ?? AsyncMarker.Sync;
+ final codeGen = getInlinableMemberCodeGenerator(translator, asyncMarker,
+ functionBuilder.type, functionBuilder.body, memberReference);
+ if (codeGen != null) return codeGen;
+
+ final procedure = member as Procedure;
+
+ if (asyncMarker == AsyncMarker.SyncStar) {
+ return SyncStarProcedureCodeGenerator(
+ translator, functionBuilder, procedure);
+ }
+ assert(asyncMarker == AsyncMarker.Async);
+ return AsyncProcedureCodeGenerator(translator, functionBuilder, procedure);
+}
+
+CodeGenerator getLambdaCodeGenerator(Translator translator, Lambda lambda,
+ Member enclosingMember, Closures enclosingMemberClosures) {
+ final asyncMarker = lambda.functionNode.asyncMarker;
+
+ if (asyncMarker == AsyncMarker.Async) {
+ return AsyncLambdaCodeGenerator(
+ translator, enclosingMember, lambda, enclosingMemberClosures);
+ }
+ if (asyncMarker == AsyncMarker.SyncStar) {
+ return SyncStarLambdaCodeGenerator(
+ translator, enclosingMember, lambda, enclosingMemberClosures);
+ }
+ assert(asyncMarker == AsyncMarker.Sync);
+ return SynchronousLambdaCodeGenerator(
+ translator, enclosingMember, lambda, enclosingMemberClosures);
+}
+
+/// Returns a [CodeGenerator] for the given member iff that member can be
+/// inlined.
+CodeGenerator? getInlinableMemberCodeGenerator(
+ Translator translator,
+ AsyncMarker asyncMarker,
+ w.FunctionType functionType,
+ w.InstructionsBuilder b,
+ Reference reference) {
+ final Member member = reference.asMember;
+
+ if (reference.isTearOffReference) {
+ return TearOffCodeGenerator(translator, functionType, b, member);
+ }
+ if (reference.isTypeCheckerReference) {
+ return TypeCheckerCodeGenerator(translator, functionType, b, member);
+ }
+
+ if (member is Constructor) {
+ if (reference.isConstructorBodyReference) {
+ return ConstructorCodeGenerator(translator, functionType, b, member);
+ } else if (reference.isInitializerReference) {
+ return InitializerListCodeGenerator(translator, functionType, b, member);
+ } else {
+ return ConstructorAllocatorCodeGenerator(
+ translator, functionType, b, member);
+ }
+ }
+
+ if (member is Field) {
+ if (member.isStatic) {
+ return StaticFieldInitializerCodeGenerator(
+ translator, functionType, b, member);
+ }
+ return ImplicitFieldAccessorCodeGenerator(
+ translator, functionType, b, member, reference.isImplicitGetter);
+ }
+
+ if (member is Procedure && asyncMarker == AsyncMarker.Sync) {
+ return SynchronousProcedureCodeGenerator(
+ translator, functionType, b, member);
+ }
+ assert(
+ asyncMarker == AsyncMarker.SyncStar || asyncMarker == AsyncMarker.Async);
+ return null;
+}
+
+class SynchronousProcedureCodeGenerator extends AstCodeGenerator {
+ final Procedure member;
+
+ SynchronousProcedureCodeGenerator(Translator translator,
+ w.FunctionType functionType, w.InstructionsBuilder b, this.member)
+ : super(translator, functionType, b, member);
+
+ @override
+ void generateInternal() {
+ final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+ setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+ // Build closure information.
+ closures = Closures(translator, member);
+
+ if (intrinsifier.generateMemberIntrinsic(
+ member.reference, functionType, paramLocals, returnLabel)) {
+ b.end();
+ return;
}
- // Call super class' constructor body, or redirected constructor
- for (Initializer initializer in member.initializers) {
- if (initializer is SuperInitializer ||
- initializer is RedirectingInitializer) {
- Constructor target = initializer is SuperInitializer
- ? initializer.target
- : (initializer as RedirectingInitializer).target;
-
- Supertype? supersupertype = target.enclosingClass.supertype;
-
- if (supersupertype == null) {
- break;
- }
-
- int startIndex =
- getStartIndexForSuperOrRedirectedConstructorArguments();
-
- List<w.Local> superOrRedirectedConstructorArgs =
- paramLocals.sublist(startIndex);
-
- w.Local object = thisLocal!;
- b.local_get(object);
-
- for (w.Local local in superOrRedirectedConstructorArgs) {
- b.local_get(local);
- }
-
- call(target.constructorBodyReference);
- break;
- }
+ if (member.isExternal) {
+ emitUnimplementedExternalError(member);
+ b.end();
+ return;
}
+ setupParametersAndContexts(member);
+
Statement? body = member.function.body;
-
if (body != null) {
visitStatement(body);
}
+ _implicitReturn();
b.end();
}
+}
- // Generates a constructor's initializer list method, and returns:
- // 1. Arguments and contexts returned from a super or redirecting initializer
- // method (in reverse order).
- // 2. Arguments for this constructor (in reverse order).
- // 3. A reference to the context for this constructor (or null if there is no
- // context).
- // 4. Class fields (including superclass fields, excluding class id and
- // identity hash).
- void generateInitializerList(Reference target) {
- assert(target.isInitializerReference);
- Constructor member = target.asConstructor;
+class TearOffCodeGenerator extends AstCodeGenerator {
+ final Member member;
- setupInitializerListParametersAndContexts(target);
+ TearOffCodeGenerator(Translator translator, w.FunctionType functionType,
+ w.InstructionsBuilder b, this.member)
+ : super(translator, functionType, b, member);
- Class cls = member.enclosingClass;
- ClassInfo info = translator.classInfo[cls]!;
-
- List<w.Local> initializedFields = _generateInitializers(member);
- bool containsSuperInitializer = false;
- bool containsRedirectingInitializer = false;
-
- for (Initializer initializer in member.initializers) {
- if (initializer is SuperInitializer) {
- containsSuperInitializer = true;
- } else if (initializer is RedirectingInitializer) {
- containsRedirectingInitializer = true;
- }
+ @override
+ void generateInternal() {
+ if (member is Constructor) {
+ // Closures are built when constructor functions are added to worklist.
+ closures = translator.constructorClosures[member.reference]!;
+ } else {
+ // Build closure information.
+ closures = Closures(translator, member);
}
- if (cls.superclass != null && !containsRedirectingInitializer) {
- // checks if a SuperInitializer was dropped because the constructor body
- // throws an error
- if (!containsSuperInitializer) {
- b.unreachable();
- b.end();
- return;
- }
-
- // checks if a FieldInitializer was dropped because the constructor body
- // throws an error
- for (Field field in info.cls!.fields) {
- if (field.isInstanceMember && !fieldLocals.containsKey(field)) {
- b.unreachable();
- b.end();
- return;
- }
- }
- }
-
- // push constructor arguments
- List<w.Local> constructorArgs =
- _getConstructorArgumentLocals(member.reference, true);
-
- for (w.Local arg in constructorArgs) {
- b.local_get(arg);
- }
-
- // push reference to context
- Context? context = closures.contexts[member];
- if (context != null) {
- assert(!context.isEmpty);
- b.local_get(context.currentLocal);
- }
-
- // push initialized fields
- for (w.Local field in initializedFields) {
- b.local_get(field);
- }
-
- b.end();
+ return generateTearOffGetter(member as Procedure);
}
- /// Generate type checker method for a setter.
- ///
- /// This function will be called by a setter forwarder in a dynamic set to
- /// type check the setter argument before calling the actual setter.
- void _generateFieldSetterTypeCheckerMethod() {
- final receiverLocal = paramLocals[0];
- final positionalArgLocal = paramLocals[1];
-
+ void generateTearOffGetter(Procedure procedure) {
_initializeThis(member.reference);
+ DartType functionType = translator.getTearOffType(procedure);
+ ClosureImplementation closure = translator.getTearOffClosure(procedure);
+ w.StructType struct = closure.representation.closureStruct;
- // Local for the argument.
- final argLocal = addLocal(translator.topInfo.nullableType);
+ ClassInfo info = translator.closureInfo;
+ translator.functions.recordClassAllocation(info.classId);
- final member_ = member;
- DartType paramType;
- if (member_ is Field) {
- paramType = member_.type;
+ b.i32_const(info.classId);
+ b.i32_const(initialIdentityHash);
+ b.local_get(paramLocals[0]); // `this` as context
+ b.global_get(closure.vtable);
+ types.makeType(this, functionType);
+ b.struct_new(struct);
+ b.end();
+ }
+}
+
+class TypeCheckerCodeGenerator extends AstCodeGenerator {
+ final Member member;
+
+ TypeCheckerCodeGenerator(Translator translator, w.FunctionType functionType,
+ w.InstructionsBuilder b, this.member)
+ : super(translator, functionType, b, member);
+
+ @override
+ void generateInternal() {
+ if (member is Constructor) {
+ // Closures are built when constructor functions are added to worklist.
+ closures = translator.constructorClosures[member.reference]!;
} else {
- paramType = (member_ as Procedure).setterType;
+ // Build closure information.
+ closures = Closures(translator, member);
}
- if (!translator.options.omitImplicitTypeChecks) {
- _generateArgumentTypeCheck(
- member.name.text,
- () => b.local_get(positionalArgLocal),
- paramType,
- argLocal,
- );
- }
-
- ClassInfo info = translator.classInfo[member_.enclosingClass]!;
- if (member_ is Field) {
- int fieldIndex = translator.fieldIndex[member_]!;
- b.local_get(receiverLocal);
- translator.convertType(b, receiverLocal.type, info.nonNullableType);
- b.local_get(argLocal);
- translator.convertType(
- b, argLocal.type, info.struct.fields[fieldIndex].type.unpacked);
- b.struct_set(info.struct, fieldIndex);
+ if (member is Field ||
+ (member is Procedure && (member as Procedure).isSetter)) {
+ return _generateFieldSetterTypeCheckerMethod();
} else {
- final setterProcedure = member_ as Procedure;
- final setterProcedureWasmType =
- translator.signatureForDirectCall(setterProcedure.reference);
- final setterWasmInputs = setterProcedureWasmType.inputs;
- assert(setterWasmInputs.length == 2);
- b.local_get(receiverLocal);
- translator.convertType(b, receiverLocal.type, setterWasmInputs[0]);
- b.local_get(argLocal);
- translator.convertType(b, argLocal.type, setterWasmInputs[1]);
- call(setterProcedure.reference);
+ return _generateProcedureTypeCheckerMethod();
}
-
- b.local_get(argLocal);
- b.end(); // end function
}
/// Generate type checker method for a method.
@@ -3632,116 +3389,544 @@
b.end();
}
- /// Generate code that checks type of an argument against an expected type
- /// and throws a `TypeError` on failure.
+ /// Generate type checker method for a setter.
///
- /// Does not expect any values on stack and does not leave any values on
- /// stack.
- ///
- /// Locals [argLocal] are used to store values pushed by [pushArg]
- /// and reuse the values.
- ///
- /// [argName] is used in the type error as the name of the argument that
- /// doesn't match the expected type.
- void _generateArgumentTypeCheck(
- String argName,
- void Function() pushArg,
- DartType testedAgainstType,
- w.Local argLocal,
- ) {
- if (translator.options.minify) {
- // We don't need to include the name in the error message, so we can use
- // the optimized `as` checks.
- pushArg();
- types.emitAsCheck(
- this,
- testedAgainstType,
- translator.coreTypes.objectNullableRawType,
- argLocal.type as w.RefType);
- b.drop();
+ /// This function will be called by a setter forwarder in a dynamic set to
+ /// type check the setter argument before calling the actual setter.
+ void _generateFieldSetterTypeCheckerMethod() {
+ final receiverLocal = paramLocals[0];
+ final positionalArgLocal = paramLocals[1];
+
+ _initializeThis(member.reference);
+
+ // Local for the argument.
+ final argLocal = addLocal(translator.topInfo.nullableType);
+
+ final member_ = member;
+ DartType paramType;
+ if (member_ is Field) {
+ paramType = member_.type;
} else {
- pushArg();
- b.local_tee(argLocal);
- types.emitIsTest(
- this, testedAgainstType, translator.coreTypes.objectNullableRawType);
- b.i32_eqz();
- b.if_();
- b.local_get(argLocal);
- types.makeType(this, testedAgainstType);
- _emitString(argName);
- call(translator.stackTraceCurrent.reference);
- call(translator.throwArgumentTypeCheckError.reference);
- b.unreachable();
- b.end();
+ paramType = (member_ as Procedure).setterType;
}
+
+ if (!translator.options.omitImplicitTypeChecks) {
+ _generateArgumentTypeCheck(
+ member.name.text,
+ () => b.local_get(positionalArgLocal),
+ paramType,
+ argLocal,
+ );
+ }
+
+ ClassInfo info = translator.classInfo[member_.enclosingClass]!;
+ if (member_ is Field) {
+ int fieldIndex = translator.fieldIndex[member_]!;
+ b.local_get(receiverLocal);
+ translator.convertType(b, receiverLocal.type, info.nonNullableType);
+ b.local_get(argLocal);
+ translator.convertType(
+ b, argLocal.type, info.struct.fields[fieldIndex].type.unpacked);
+ b.struct_set(info.struct, fieldIndex);
+ } else {
+ final setterProcedure = member_ as Procedure;
+ final setterProcedureWasmType =
+ translator.signatureForDirectCall(setterProcedure.reference);
+ final setterWasmInputs = setterProcedureWasmType.inputs;
+ assert(setterWasmInputs.length == 2);
+ b.local_get(receiverLocal);
+ translator.convertType(b, receiverLocal.type, setterWasmInputs[0]);
+ b.local_get(argLocal);
+ translator.convertType(b, argLocal.type, setterWasmInputs[1]);
+ call(setterProcedure.reference);
+ }
+
+ b.local_get(argLocal);
+ b.end(); // end function
+ }
+}
+
+class InitializerListCodeGenerator extends AstCodeGenerator {
+ final Constructor member;
+
+ InitializerListCodeGenerator(Translator translator,
+ w.FunctionType functionType, w.InstructionsBuilder b, this.member)
+ : super(translator, functionType, b, member);
+
+ @override
+ void generateInternal() {
+ // Closures are built when constructor functions are added to worklist.
+ closures = translator.constructorClosures[member.reference]!;
+
+ final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+ setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+ if (member.isExternal) {
+ emitUnimplementedExternalError(member);
+ b.end();
+ return;
+ }
+
+ generateInitializerList();
}
- void _generateTypeArgumentBoundCheck(
- String argName,
- w.Local typeLocal,
- DartType bound,
- ) {
- b.local_get(typeLocal);
- final boundLocal =
- b.addLocal(translator.classInfo[translator.typeClass]!.nonNullableType);
- types.makeType(this, bound);
- b.local_tee(boundLocal);
- call(translator.isTypeSubtype.reference);
+ // Generates a constructor's initializer list method, and returns:
+ // 1. Arguments and contexts returned from a super or redirecting initializer
+ // method (in reverse order).
+ // 2. Arguments for this constructor (in reverse order).
+ // 3. A reference to the context for this constructor (or null if there is no
+ // context).
+ // 4. Class fields (including superclass fields, excluding class id and
+ // identity hash).
+ void generateInitializerList() {
+ _setupInitializerListParametersAndContexts();
- b.i32_eqz();
- b.if_();
- // Type check failed
- b.local_get(typeLocal);
- b.local_get(boundLocal);
- _emitString(argName);
- call(translator.stackTraceCurrent.reference);
- call(translator.throwTypeArgumentBoundCheckError.reference);
- b.unreachable();
+ Class cls = member.enclosingClass;
+ ClassInfo info = translator.classInfo[cls]!;
+
+ List<w.Local> initializedFields = _generateInitializers(member);
+ bool containsSuperInitializer = false;
+ bool containsRedirectingInitializer = false;
+
+ for (Initializer initializer in member.initializers) {
+ if (initializer is SuperInitializer) {
+ containsSuperInitializer = true;
+ } else if (initializer is RedirectingInitializer) {
+ containsRedirectingInitializer = true;
+ }
+ }
+
+ if (cls.superclass != null && !containsRedirectingInitializer) {
+ // checks if a SuperInitializer was dropped because the constructor body
+ // throws an error
+ if (!containsSuperInitializer) {
+ b.unreachable();
+ b.end();
+ return;
+ }
+
+ // checks if a FieldInitializer was dropped because the constructor body
+ // throws an error
+ for (Field field in info.cls!.fields) {
+ if (field.isInstanceMember && !fieldLocals.containsKey(field)) {
+ b.unreachable();
+ b.end();
+ return;
+ }
+ }
+ }
+
+ // push constructor arguments
+ List<w.Local> constructorArgs =
+ _getConstructorArgumentLocals(member.reference, true);
+
+ for (w.Local arg in constructorArgs) {
+ b.local_get(arg);
+ }
+
+ // push reference to context
+ Context? context = closures.contexts[member];
+ if (context != null) {
+ assert(!context.isEmpty);
+ b.local_get(context.currentLocal);
+ }
+
+ // push initialized fields
+ for (w.Local field in initializedFields) {
+ b.local_get(field);
+ }
+
b.end();
}
- void _emitString(String str) => wrap(StringLiteral(str),
- translator.translateType(translator.coreTypes.stringNonNullableRawType));
-
- @override
- void visitPatternSwitchStatement(PatternSwitchStatement node) {
- // This node is internal to the front end and removed by the constant
- // evaluator.
- throw UnsupportedError("CodeGenerator.visitPatternSwitchStatement");
+ void _setupInitializerListParametersAndContexts() {
+ setupParameters(member.initializerReference, isForwarder: true);
+ allocateContext(member);
+ captureParameters();
}
- @override
- void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
- // This node is internal to the front end and removed by the constant
- // evaluator.
- throw UnsupportedError("CodeGenerator.visitPatternVariableDeclaration");
+ List<w.Local> _generateInitializers(Constructor member) {
+ Class cls = member.enclosingClass;
+ ClassInfo info = translator.classInfo[cls]!;
+ List<w.Local> superclassFields = [];
+
+ _setupDefaultFieldValues(info);
+
+ // Generate initializer list
+ for (Initializer initializer in member.initializers) {
+ visitInitializer(initializer);
+
+ if (initializer is SuperInitializer) {
+ // Save super classes' fields to locals
+ ClassInfo superInfo = info.superInfo!;
+
+ for (w.ValueType outputType
+ in superInfo.getClassFieldTypes().reversed) {
+ w.Local local = addLocal(outputType);
+ b.local_set(local);
+ superclassFields.add(local);
+ }
+ } else if (initializer is RedirectingInitializer) {
+ // Save redirected classes' fields to locals
+ List<w.Local> redirectedFields = [];
+
+ for (w.ValueType outputType in info.getClassFieldTypes().reversed) {
+ w.Local local = addLocal(outputType);
+ b.local_set(local);
+ redirectedFields.add(local);
+ }
+
+ return redirectedFields.reversed.toList();
+ }
+ }
+
+ List<w.Local> typeFields = [];
+
+ for (TypeParameter typeParam in cls.typeParameters) {
+ TypeParameter? match = info.typeParameterMatch[typeParam];
+
+ if (match == null) {
+ // Type is not contained in super class' fields
+ typeFields.add(typeLocals[typeParam]!);
+ }
+ }
+
+ List<w.Local> orderedFieldLocals = Map.fromEntries(
+ fieldLocals.entries.toList()
+ ..sort((x, y) => translator.fieldIndex[x.key]!
+ .compareTo(translator.fieldIndex[y.key]!)))
+ .values
+ .toList();
+
+ return superclassFields.reversed.toList() + typeFields + orderedFieldLocals;
}
+}
+
+class ConstructorAllocatorCodeGenerator extends AstCodeGenerator {
+ final Constructor member;
+
+ ConstructorAllocatorCodeGenerator(Translator translator,
+ w.FunctionType functionType, w.InstructionsBuilder b, this.member)
+ : super(translator, functionType, b, member);
@override
- void visitIfCaseStatement(IfCaseStatement node) {
- // This node is internal to the front end and removed by the constant
- // evaluator.
- throw UnsupportedError("CodeGenerator.visitIfCaseStatement");
+ void generateInternal() {
+ // Closures are built when constructor functions are added to worklist.
+ closures = translator.constructorClosures[member.reference]!;
+
+ final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+ setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+ generateConstructorAllocator();
}
- void debugRuntimePrint(String s) {
- final printFunction =
- translator.functions.getFunction(translator.printToConsole.reference);
- translator.constants.instantiateConstant(
- b, StringConstant(s), printFunction.type.inputs[0]);
- b.call(printFunction);
+ // Generates a function for allocating an object. This calls the separate
+ // initializer list and constructor body methods, and allocates a struct for
+ // the object.
+ void generateConstructorAllocator() {
+ setupParameters(member.reference, isForwarder: true);
+
+ w.FunctionType initializerMethodType =
+ translator.signatureForDirectCall(member.initializerReference);
+
+ List<w.Local> constructorArgs =
+ _getConstructorArgumentLocals(member.reference);
+
+ for (w.Local local in constructorArgs) {
+ b.local_get(local);
+ }
+
+ b.comment("Direct call of '$member Initializer'");
+ call(member.initializerReference);
+
+ ClassInfo info = translator.classInfo[member.enclosingClass]!;
+
+ // Add evaluated fields to locals
+ List<w.Local> orderedFieldLocals = [];
+
+ List<w.FieldType> fieldTypes = info.struct.fields
+ .sublist(FieldIndex.objectFieldBase)
+ .reversed
+ .toList();
+
+ for (w.FieldType field in fieldTypes) {
+ w.Local local = addLocal(field.type.unpacked);
+ orderedFieldLocals.add(local);
+ b.local_set(local);
+ }
+
+ Context? context = closures.contexts[member];
+ w.Local? contextLocal;
+
+ bool hasContext = context != null;
+
+ if (hasContext) {
+ assert(!context.isEmpty);
+ w.ValueType contextRef = w.RefType.struct(nullable: true);
+ contextLocal = addLocal(contextRef);
+ b.local_set(contextLocal);
+ }
+
+ List<w.ValueType> initializerOutputTypes = initializerMethodType.outputs;
+ int numConstructorBodyArgs = initializerOutputTypes.length -
+ fieldTypes.length -
+ (hasContext ? 1 : 0);
+
+ // Pop all arguments to constructor body
+ List<w.ValueType> constructorArgTypes =
+ initializerOutputTypes.sublist(0, numConstructorBodyArgs);
+
+ List<w.Local> constructorArguments = [];
+
+ for (w.ValueType argType in constructorArgTypes.reversed) {
+ w.Local local = addLocal(argType);
+ b.local_set(local);
+ constructorArguments.add(local);
+ }
+
+ // Set field values
+ b.i32_const(info.classId);
+ b.i32_const(initialIdentityHash);
+
+ for (w.Local local in orderedFieldLocals.reversed) {
+ b.local_get(local);
+ }
+
+ // create new struct with these fields and set to local
+ w.Local temp = addLocal(info.nonNullableType);
+ b.struct_new(info.struct);
+ b.local_tee(temp);
+
+ // Push context local if it is present
+ if (contextLocal != null) {
+ b.local_get(contextLocal);
+ }
+
+ // Push all constructor arguments
+ for (w.Local constructorArg in constructorArguments) {
+ b.local_get(constructorArg);
+ }
+
+ b.comment("Direct call of $member Constructor Body");
+ call(member.constructorBodyReference);
+
+ b.local_get(temp);
+ b.end();
}
+}
+
+class ConstructorCodeGenerator extends AstCodeGenerator {
+ final Constructor member;
+
+ ConstructorCodeGenerator(Translator translator, w.FunctionType functionType,
+ w.InstructionsBuilder b, this.member)
+ : super(translator, functionType, b, member);
@override
- void visitAuxiliaryStatement(AuxiliaryStatement node) {
- throw UnsupportedError(
- "Unsupported auxiliary statement $node (${node.runtimeType}).");
+ void generateInternal() {
+ super.generate;
+ // Closures are built when constructor functions are added to worklist.
+ closures = translator.constructorClosures[member.reference]!;
+
+ final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+ setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+ generateConstructorBody();
}
+ // Generates a function for a constructor's body, where the allocated struct
+ // object is passed to this function.
+ void generateConstructorBody() {
+ _setupConstructorBodyParametersAndContexts();
+
+ int getStartIndexForSuperOrRedirectedConstructorArguments() {
+ // Skips the receiver param and the current constructor's context
+ // (if it exists)
+ Context? context = closures.contexts[member];
+ bool hasContext = context != null;
+
+ if (hasContext) {
+ assert(!context.isEmpty);
+ }
+
+ int numSkippedParams = hasContext ? 2 : 1;
+
+ // Skips the current constructor's arguments
+ int numConstructorArgs =
+ _getConstructorArgumentLocals(member.constructorBodyReference).length;
+
+ return numSkippedParams + numConstructorArgs;
+ }
+
+ // Call super class' constructor body, or redirected constructor
+ for (Initializer initializer in member.initializers) {
+ if (initializer is SuperInitializer ||
+ initializer is RedirectingInitializer) {
+ Constructor target = initializer is SuperInitializer
+ ? initializer.target
+ : (initializer as RedirectingInitializer).target;
+
+ Supertype? supersupertype = target.enclosingClass.supertype;
+
+ if (supersupertype == null) {
+ break;
+ }
+
+ int startIndex =
+ getStartIndexForSuperOrRedirectedConstructorArguments();
+
+ List<w.Local> superOrRedirectedConstructorArgs =
+ paramLocals.sublist(startIndex);
+
+ w.Local object = thisLocal!;
+ b.local_get(object);
+
+ for (w.Local local in superOrRedirectedConstructorArgs) {
+ b.local_get(local);
+ }
+
+ call(target.constructorBodyReference);
+ break;
+ }
+ }
+
+ Statement? body = member.function.body;
+
+ if (body != null) {
+ visitStatement(body);
+ }
+
+ b.end();
+ }
+
+ void _setupConstructorBodyParametersAndContexts() {
+ ParameterInfo paramInfo =
+ translator.paramInfoForDirectCall(member.constructorBodyReference);
+
+ // For constructor body functions, the first parameter is always the
+ // receiver parameter, and the second parameter is a reference to the
+ // current context (if it exists).
+ Context? context = closures.contexts[member];
+ bool hasConstructorContext = context != null;
+
+ if (hasConstructorContext) {
+ assert(!context.isEmpty);
+ _initializeContextLocals(member, contextParamIndex: 1);
+ }
+
+ // Skips the receiver param (_initializeThis will return 1), and the
+ // context param if this exists.
+ int parameterOffset = _initializeThis(member.constructorBodyReference) +
+ (hasConstructorContext ? 1 : 0);
+ int implicitParams = parameterOffset + paramInfo.typeParamCount;
+
+ _setupLocalParameters(member, paramInfo, parameterOffset, implicitParams);
+ allocateContext(member.function);
+ }
+}
+
+class StaticFieldInitializerCodeGenerator extends AstCodeGenerator {
+ final Field field;
+
+ StaticFieldInitializerCodeGenerator(Translator translator,
+ w.FunctionType functionType, w.InstructionsBuilder b, this.field)
+ : super(translator, functionType, b, field);
+
@override
- void visitAuxiliaryInitializer(AuxiliaryInitializer node) {
- throw UnsupportedError(
- "Unsupported auxiliary initializer $node (${node.runtimeType}).");
+ void generateInternal() {
+ final source = field.enclosingComponent!.uriToSource[field.fileUri]!;
+ setSourceMapSourceAndFileOffset(source, field.fileOffset);
+
+ // Static field initializer function
+ closures = Closures(translator, field);
+ closures.findCaptures(field);
+ closures.collectContexts(field);
+ closures.buildContexts();
+
+ w.Global global = translator.globals.getGlobal(field);
+ w.Global? flag = translator.globals.getGlobalInitializedFlag(field);
+ wrap(field.initializer!, global.type.type);
+ b.global_set(global);
+ if (flag != null) {
+ b.i32_const(1);
+ b.global_set(flag);
+ }
+ b.global_get(global);
+ translator.convertType(b, global.type.type, outputs.single);
+ b.end();
+ }
+}
+
+class ImplicitFieldAccessorCodeGenerator extends AstCodeGenerator {
+ final Field field;
+ final bool isImplicitGetter;
+
+ ImplicitFieldAccessorCodeGenerator(
+ Translator translator,
+ w.FunctionType functionType,
+ w.InstructionsBuilder b,
+ this.field,
+ this.isImplicitGetter,
+ ) : super(translator, functionType, b, field);
+
+ @override
+ void generateInternal() {
+ closures = Closures(translator, field);
+
+ final source = field.enclosingComponent!.uriToSource[field.fileUri]!;
+ setSourceMapSourceAndFileOffset(source, field.fileOffset);
+
+ // Implicit getter or setter
+ w.StructType struct = translator.classInfo[field.enclosingClass!]!.struct;
+ int fieldIndex = translator.fieldIndex[field]!;
+ w.ValueType fieldType = struct.fields[fieldIndex].type.unpacked;
+
+ void getThis() {
+ w.Local thisLocal = paramLocals[0];
+ w.RefType structType = w.RefType.def(struct, nullable: false);
+ b.local_get(thisLocal);
+ translator.convertType(b, thisLocal.type, structType);
+ }
+
+ if (isImplicitGetter) {
+ // Implicit getter
+ getThis();
+ b.struct_get(struct, fieldIndex);
+ translator.convertType(b, fieldType, returnType);
+ } else {
+ // Implicit setter
+ w.Local valueLocal = paramLocals[1];
+ getThis();
+ b.local_get(valueLocal);
+ translator.convertType(b, valueLocal.type, fieldType);
+ b.struct_set(struct, fieldIndex);
+ }
+ b.end();
+ }
+}
+
+class SynchronousLambdaCodeGenerator extends AstCodeGenerator {
+ final Lambda lambda;
+ final Closures enclosingMemberClosures;
+
+ SynchronousLambdaCodeGenerator(Translator translator, Member enclosingMember,
+ this.lambda, this.enclosingMemberClosures)
+ : super(translator, lambda.function.type, lambda.function.body,
+ enclosingMember);
+
+ @override
+ void generateInternal() {
+ closures = enclosingMemberClosures;
+
+ setSourceMapSource(lambda.functionNodeSource);
+
+ assert(lambda.functionNode.asyncMarker != AsyncMarker.Async);
+
+ setupLambdaParametersAndContexts(lambda);
+
+ visitStatement(lambda.functionNode.body!);
+ _implicitReturn();
+ b.end();
}
}
@@ -3805,7 +3990,7 @@
/// The `null: ...` case, if exists.
late final SwitchCase? nullCase;
- SwitchInfo(CodeGenerator codeGen, SwitchStatement node) {
+ SwitchInfo(AstCodeGenerator codeGen, SwitchStatement node) {
final translator = codeGen.translator;
final switchExprType = codeGen.dartTypeOf(node.expression);
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index df7261e..6d91ef7 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -12,14 +12,14 @@
import 'translator.dart';
import 'types.dart';
-typedef CodeGenCallback = void Function(CodeGenerator);
+typedef CodeGenCallback = void Function(AstCodeGenerator);
/// Specialized code generation for external members.
///
/// The code is generated either inlined at the call site, or as the body of
/// the member in [generateMemberIntrinsic].
class Intrinsifier {
- final CodeGenerator codeGen;
+ final AstCodeGenerator codeGen;
static const w.ValueType boolType = w.NumType.i32;
static const w.ValueType intType = w.NumType.i64;
diff --git a/pkg/dart2wasm/lib/state_machine.dart b/pkg/dart2wasm/lib/state_machine.dart
index 84b43af..1a93fa8 100644
--- a/pkg/dart2wasm/lib/state_machine.dart
+++ b/pkg/dart2wasm/lib/state_machine.dart
@@ -563,40 +563,11 @@
CatchVariables._(this.exception, this.stackTrace);
}
-abstract class StateMachineEntryCodeGenerator extends CodeGenerator {
+abstract class StateMachineEntryAstCodeGenerator extends AstCodeGenerator {
final w.FunctionBuilder function;
-
- StateMachineEntryCodeGenerator(
- Translator translator, this.function, Reference reference)
- : super(translator, function.type, function.body, reference,
- paramLocals: function.locals.toList());
-
- @override
- void generate() {
- final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
- closures = Closures(translator, member);
- setSourceMapSource(source);
- setSourceMapFileOffset(member.fileOffset);
- setupParametersAndContexts(member.reference);
- _generateBody(member.function!, source);
- }
-
- @override
- void generateLambda(Lambda lambda, Closures closures) {
- final source = lambda.functionNodeSource;
- this.closures = closures;
- setSourceMapSource(source);
- setSourceMapFileOffset(lambda.functionNode.fileOffset);
- setupLambdaParametersAndContexts(lambda);
- _generateBody(lambda.functionNode, source);
- }
-
- void _generateBody(FunctionNode functionNode, Source functionSource) {
- Context? context = closures.contexts[functionNode];
- if (context != null && context.isEmpty) context = context.parent;
-
- generateOuter(functionNode, context, functionSource);
- }
+ StateMachineEntryAstCodeGenerator(
+ Translator translator, Member enclosingMember, this.function)
+ : super(translator, function.type, function.body, enclosingMember);
/// Generate the outer function.
///
@@ -610,12 +581,59 @@
FunctionNode functionNode, Context? context, Source functionSource);
}
+abstract class ProcedureStateMachineEntryCodeGenerator
+ extends StateMachineEntryAstCodeGenerator {
+ final Procedure member;
+
+ ProcedureStateMachineEntryCodeGenerator(
+ Translator translator, w.FunctionBuilder function, this.member)
+ : super(translator, member, function);
+
+ @override
+ void generateInternal() {
+ final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+ closures = Closures(translator, member);
+ setSourceMapSource(source);
+ setSourceMapFileOffset(member.fileOffset);
+ setupParametersAndContexts(member);
+
+ Context? context = closures.contexts[member.function];
+ if (context != null && context.isEmpty) context = context.parent;
+
+ generateOuter(member.function, context, source);
+ }
+}
+
+abstract class LambdaStateMachineEntryCodeGenerator
+ extends StateMachineEntryAstCodeGenerator {
+ final Lambda lambda;
+
+ LambdaStateMachineEntryCodeGenerator(Translator translator,
+ Member enclosingMember, this.lambda, Closures closures)
+ : super(translator, enclosingMember, lambda.function) {
+ this.closures = closures;
+ }
+
+ @override
+ void generateInternal() {
+ final source = lambda.functionNodeSource;
+ setSourceMapSource(source);
+ setSourceMapFileOffset(lambda.functionNode.fileOffset);
+ setupLambdaParametersAndContexts(lambda);
+
+ Context? context = closures.contexts[lambda.functionNode];
+ if (context != null && context.isEmpty) context = context.parent;
+
+ generateOuter(lambda.functionNode, context, source);
+ }
+}
+
/// A [CodeGenerator] that compiles the function to a state machine based on
/// the suspension points in the function (`await` expressions and `yield`
/// statements).
///
/// This is used to compile `async` and `sync*` functions.
-abstract class StateMachineCodeGenerator extends CodeGenerator {
+abstract class StateMachineCodeGenerator extends AstCodeGenerator {
final w.FunctionBuilder function;
final FunctionNode functionNode;
final Source functionSource;
@@ -623,12 +641,11 @@
StateMachineCodeGenerator(
Translator translator,
this.function,
- Reference reference,
+ Member enclosingMember,
this.functionNode,
this.functionSource,
Closures closures)
- : super(translator, function.type, function.body, reference,
- paramLocals: function.locals.toList()) {
+ : super(translator, function.type, function.body, enclosingMember) {
this.closures = closures;
}
@@ -667,7 +684,7 @@
List<CatchVariables> catchVariableStack = [];
@override
- void generate() {
+ void generateInternal() {
setSourceMapSource(functionSource);
setSourceMapFileOffset(functionNode.fileOffset);
@@ -692,13 +709,6 @@
generateInner(functionNode, context);
}
- @override
- void generateLambda(Lambda lambda, Closures closures) {
- // This is only invoked for the actual async/async*/sync* code generator and
- // not for the (inner) state machine code generator.
- throw UnsupportedError('This should not be reachable');
- }
-
/// Store the exception value emitted by [emitValue] in suspension state.
/// [getSuspendStateCurrentException] should return the value even after
/// suspending the function and continuing it later.
diff --git a/pkg/dart2wasm/lib/sync_star.dart b/pkg/dart2wasm/lib/sync_star.dart
index 9718a48..9a9457b 100644
--- a/pkg/dart2wasm/lib/sync_star.dart
+++ b/pkg/dart2wasm/lib/sync_star.dart
@@ -10,9 +10,7 @@
import 'code_generator.dart';
import 'state_machine.dart';
-class SyncStarCodeGenerator extends StateMachineEntryCodeGenerator {
- SyncStarCodeGenerator(super.translator, super.function, super.reference);
-
+mixin SyncStarCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
late final ClassInfo suspendStateInfo =
translator.classInfo[translator.suspendStateClass]!;
@@ -42,9 +40,9 @@
b.return_();
b.end();
- SyncStarStateMachineCodeGenerator(translator, resumeFun, reference,
+ SyncStarStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
functionNode, functionSource, closures)
- .generate();
+ .generate(resumeFun.locals.toList(), null);
}
w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
@@ -61,6 +59,21 @@
"${function.functionName} inner");
}
+/// Generates code for sync* procedures.
+class SyncStarProcedureCodeGenerator
+ extends ProcedureStateMachineEntryCodeGenerator
+ with SyncStarCodeGeneratorMixin {
+ SyncStarProcedureCodeGenerator(
+ super.translator, super.function, super.enclosingMember);
+}
+
+/// Generates code for sync* closures.
+class SyncStarLambdaCodeGenerator extends LambdaStateMachineEntryCodeGenerator
+ with SyncStarCodeGeneratorMixin {
+ SyncStarLambdaCodeGenerator(
+ super.translator, super.enclosingMember, super.lambda, super.closures);
+}
+
/// A specialized code generator for generating code for `sync*` functions.
///
/// This will create an "outer" function which is a small function that just
@@ -80,7 +93,7 @@
SyncStarStateMachineCodeGenerator(
super.translator,
super.function,
- super.reference,
+ super.enclosingMember,
super.functionNode,
super.functionSource,
super.closures);
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index ecb0bc9..b1d797c 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -369,9 +369,10 @@
m.exports.export(canonicalName, function);
}
- final CodeGenerator codeGen = CodeGenerator.forFunction(
- this, member.function, function as w.FunctionBuilder, reference);
- codeGen.generate();
+ final functionBuilder = function as w.FunctionBuilder;
+ final codeGen = getMemberCodeGenerator(this, functionBuilder, reference);
+ codeGen.generate(functionBuilder.locals.toList(), null);
+ final memberClosures = codeGen.closures;
if (options.printWasm) {
print(function.type);
@@ -384,11 +385,10 @@
// the lambda functions once, so we only generate lambdas when the
// constructor initializer methods are generated.
if (member is! Constructor || reference.isInitializerReference) {
- for (Lambda lambda in codeGen.closures.lambdas.values) {
- final lambdaFunction = lambda.function;
- CodeGenerator.forFunction(
- this, lambda.functionNode, lambdaFunction, reference)
- .generateLambda(lambda, codeGen.closures);
+ final enclosingMember = member;
+ for (Lambda lambda in memberClosures.lambdas.values) {
+ getLambdaCodeGenerator(this, lambda, enclosingMember, memberClosures)
+ .generate(lambda.function.locals.toList(), null);
_printFunction(function, "$canonicalName (closure)");
}
}
@@ -1086,6 +1086,13 @@
NodeCounter().countNodes(body) <= options.inliningLimit;
}
+ CodeGenerator? getInliningCodeGenerator(
+ Reference target, w.FunctionType functionType, w.InstructionsBuilder b) {
+ if (!shouldInline(target)) return null;
+ return getInlinableMemberCodeGenerator(
+ this, AsyncMarker.Sync, functionType, b, target);
+ }
+
T? getPragma<T>(Annotatable node, String name, [T? defaultValue]) {
for (Expression annotation in node.annotations) {
if (annotation is ConstantExpression) {
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index e70b8b2..4bdd5b4 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -166,7 +166,7 @@
/// Allocates a `WasmArray<_Type>` from [types] and pushes it to the
/// stack.
- void _makeTypeArray(CodeGenerator codeGen, Iterable<DartType> types) {
+ void _makeTypeArray(AstCodeGenerator codeGen, Iterable<DartType> types) {
if (types.every(isTypeConstant)) {
translator.constants.instantiateConstant(codeGen.b,
translator.constants.makeTypeArray(types), typeArrayExpectedType);
@@ -178,7 +178,7 @@
}
}
- void _makeInterfaceType(CodeGenerator codeGen, InterfaceType type) {
+ void _makeInterfaceType(AstCodeGenerator codeGen, InterfaceType type) {
final b = codeGen.b;
ClassInfo typeInfo = translator.classInfo[type.classNode]!;
b.i32_const(encodedNullability(type));
@@ -186,7 +186,7 @@
_makeTypeArray(codeGen, type.typeArguments);
}
- void _makeRecordType(CodeGenerator codeGen, RecordType type) {
+ void _makeRecordType(AstCodeGenerator codeGen, RecordType type) {
codeGen.b.i32_const(encodedNullability(type));
final names = translator.constants.makeArrayOf(
@@ -236,14 +236,14 @@
return FutureOrType(s, declaredNullability);
}
- void _makeFutureOrType(CodeGenerator codeGen, FutureOrType type) {
+ void _makeFutureOrType(AstCodeGenerator codeGen, FutureOrType type) {
final b = codeGen.b;
b.i32_const(encodedNullability(type));
makeType(codeGen, type.typeArgument);
codeGen.call(translator.createNormalizedFutureOrType.reference);
}
- void _makeFunctionType(CodeGenerator codeGen, FunctionType type) {
+ void _makeFunctionType(AstCodeGenerator codeGen, FunctionType type) {
int typeParameterOffset = computeFunctionTypeParameterOffset(type);
final b = codeGen.b;
b.i32_const(encodedNullability(type));
@@ -298,7 +298,7 @@
/// Makes a `_Type` object on the stack.
/// TODO(joshualitt): Refactor this logic to remove the dependency on
/// CodeGenerator.
- w.ValueType makeType(CodeGenerator codeGen, DartType type) {
+ w.ValueType makeType(AstCodeGenerator codeGen, DartType type) {
// Always ensure type is normalized before making a type.
type = normalize(type);
final b = codeGen.b;
@@ -377,8 +377,8 @@
/// Emit code for testing a value against a Dart type. Expects the value on
/// the stack as a (ref null #Top) and leaves the result on the stack as an
/// i32.
- void emitIsTest(
- CodeGenerator codeGen, DartType testedAgainstType, DartType operandType,
+ void emitIsTest(AstCodeGenerator codeGen, DartType testedAgainstType,
+ DartType operandType,
[Location? location]) {
final b = codeGen.b;
b.comment("type check against $testedAgainstType");
@@ -438,7 +438,7 @@
}
}
- w.ValueType emitAsCheck(CodeGenerator codeGen, DartType testedAgainstType,
+ w.ValueType emitAsCheck(AstCodeGenerator codeGen, DartType testedAgainstType,
DartType operandType, w.RefType boxedOperandType,
[Location? location]) {
final b = codeGen.b;
diff --git a/pkg/front_end/lib/src/base/local_scope.dart b/pkg/front_end/lib/src/base/local_scope.dart
index 18335b7..e9e15a5 100644
--- a/pkg/front_end/lib/src/base/local_scope.dart
+++ b/pkg/front_end/lib/src/base/local_scope.dart
@@ -94,6 +94,7 @@
void _recordUse(String name, int charOffset) {}
@override
+ // Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_parent?.forEachExtension(f);
}
@@ -160,6 +161,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
Map<String, List<int>>? get usedNames => null;
}
@@ -223,6 +225,7 @@
ScopeKind get kind => _scope.kind;
@override
+ // Coverage-ignore(suite): Not run.
Iterable<Builder> get localVariables => const [];
@override
@@ -231,6 +234,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
Builder? lookupLocalVariable(String name) => null;
@override
@@ -239,6 +243,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_scope.forEachExtension(f);
}
diff --git a/pkg/front_end/lib/src/base/name_space.dart b/pkg/front_end/lib/src/base/name_space.dart
index a8e2734..882bbff 100644
--- a/pkg/front_end/lib/src/base/name_space.dart
+++ b/pkg/front_end/lib/src/base/name_space.dart
@@ -373,8 +373,8 @@
nameSpace._getables?.forEach(mergeMember);
}
if (nameSpace._setables != null) {
- map = _setables ??= // Coverage-ignore(suite): Not run.
- {};
+ // Coverage-ignore-block(suite): Not run.
+ map = _setables ??= {};
nameSpace._setables?.forEach(mergeMember);
}
if (nameSpace._extensions != null) {
diff --git a/pkg/front_end/lib/src/base/scope.dart b/pkg/front_end/lib/src/base/scope.dart
index 1aabf30..fa77578 100644
--- a/pkg/front_end/lib/src/base/scope.dart
+++ b/pkg/front_end/lib/src/base/scope.dart
@@ -182,15 +182,8 @@
required bool forStaticAccess}) {
if (builder == null) return null;
if (builder.next != null) {
- return new AmbiguousBuilder(
- name.isEmpty
- ?
- // Coverage-ignore(suite): Not run.
- classNameOrDebugName
- : name,
- builder,
- charOffset,
- fileUri);
+ return new AmbiguousBuilder(name.isEmpty ? classNameOrDebugName : name,
+ builder, charOffset, fileUri);
} else if (forStaticAccess && builder.isDeclarationInstanceMember) {
return null;
} else if (builder is MemberBuilder && builder.isConflictingSetter) {
@@ -308,6 +301,7 @@
TypeParameterScope(this._parent, this._typeParameters);
@override
+ // Coverage-ignore(suite): Not run.
ScopeKind get kind => ScopeKind.typeParameters;
@override
@@ -338,6 +332,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_parent.forEachExtension(f);
}
@@ -375,8 +370,12 @@
@override
Builder? lookupSetable(String name, int charOffset, Uri fileUri) {
Builder? builder = normalizeLookup(
- getable: _getables?[name],
- setable: _setables?[name],
+ getable: _getables
+ // Coverage-ignore(suite): Not run.
+ ?[name],
+ setable: _setables
+ // Coverage-ignore(suite): Not run.
+ ?[name],
name: name,
charOffset: charOffset,
fileUri: fileUri,
@@ -391,6 +390,7 @@
}
}
+// Coverage-ignore(suite): Not run.
// TODO(johnniwinther): Use this instead of [SourceLibraryBuilderScope].
class CompilationUnitScope extends BaseNameSpaceLookupScope {
final CompilationUnit _compilationUnit;
@@ -447,6 +447,7 @@
_local[name] = builder;
}
+ // Coverage-ignore(suite): Not run.
void addLocalMembers(Map<String, MemberBuilder> map) {
_local.addAll(map);
}
diff --git a/pkg/front_end/lib/src/dill/dill_class_builder.dart b/pkg/front_end/lib/src/dill/dill_class_builder.dart
index 133b6c2..bbf02a2 100644
--- a/pkg/front_end/lib/src/dill/dill_class_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_class_builder.dart
@@ -78,6 +78,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
LookupScope get scope => _scope;
@override
diff --git a/pkg/front_end/lib/src/dill/dill_extension_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
index f4f0980..47577d9 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
@@ -89,6 +89,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
LookupScope get scope => _scope;
@override
diff --git a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
index 1399162..6fe9fea 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
@@ -149,6 +149,7 @@
DillLibraryBuilder get libraryBuilder => parent as DillLibraryBuilder;
@override
+ // Coverage-ignore(suite): Not run.
LookupScope get scope => _scope;
@override
diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart
index 356a7a5..45fa913 100644
--- a/pkg/front_end/lib/src/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/kernel/body_builder.dart
@@ -1583,8 +1583,8 @@
// Coverage-ignore-block(suite): Not run.
temporaryParent = new ListLiteral(expressions);
}
- // Coverage-ignore(suite): Not run.
performBacklogComputations();
+ // Coverage-ignore(suite): Not run.
return temporaryParent != null ? temporaryParent.expressions : expressions;
}
@@ -3424,6 +3424,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void handleLiteralIntWithSeparators(Token token) {
debugEvent("LiteralIntWithSeparators");
@@ -4158,7 +4159,7 @@
assert(conditionStatement is EmptyStatement);
}
if (entry is MapLiteralEntry) {
- ForMapEntry result;
+ TreeNode result;
if (variableOrExpression is PatternVariableDeclaration) {
result = forest.createPatternForMapEntry(offsetForToken(forToken),
patternVariableDeclaration: variableOrExpression,
@@ -4174,7 +4175,7 @@
typeInferrer.assignedVariables.endNode(result);
push(result);
} else {
- ForElement result;
+ TreeNode result;
if (variableOrExpression is PatternVariableDeclaration) {
result = forest.createPatternForElement(offsetForToken(forToken),
patternVariableDeclaration: variableOrExpression,
@@ -4653,11 +4654,9 @@
for (int i = 0; i < setOrMapEntries.length; ++i) {
if (setOrMapEntries[i] is MapLiteralEntry) {
mapEntries[i] = setOrMapEntries[i];
- } else if (setOrMapEntries[i] is ControlFlowElement) {
+ } else {
mapEntries[i] = convertToMapEntry(setOrMapEntries[i], this,
typeInferrer.assignedVariables.reassignInfo);
- } else {
- mapEntries[i] = convertToErroneousMapEntry(setOrMapEntries[i], this);
}
}
buildLiteralMap(typeArguments, constKeyword, leftBrace, mapEntries);
@@ -4753,6 +4752,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void handleLiteralDoubleWithSeparators(Token token) {
debugEvent("LiteralDoubleWithSeparators");
@@ -10186,6 +10186,7 @@
_BodyBuilderCloner(this.bodyBuilder);
@override
+ // Coverage-ignore(suite): Not run.
TreeNode visitStaticInvocation(StaticInvocation node) {
if (node is FactoryConstructorInvocation) {
FactoryConstructorInvocation result = new FactoryConstructorInvocation(
@@ -10193,16 +10194,13 @@
isConst: node.isConst)
..hasBeenInferred = node.hasBeenInferred;
return result;
- }
- // Coverage-ignore(suite): Not run.
- else if (node is TypeAliasedFactoryInvocation) {
+ } else if (node is TypeAliasedFactoryInvocation) {
TypeAliasedFactoryInvocation result = new TypeAliasedFactoryInvocation(
node.typeAliasBuilder, node.target, clone(node.arguments),
isConst: node.isConst)
..hasBeenInferred = node.hasBeenInferred;
return result;
}
- // Coverage-ignore(suite): Not run.
return super.visitStaticInvocation(node);
}
@@ -10231,6 +10229,7 @@
}
}
+// Coverage-ignore(suite): Not run.
/// Returns `true` if [node] is not part of its parent member.
///
/// This computation is costly and should only be used in assertions to verify
@@ -10248,7 +10247,6 @@
if (member == null) {
return true;
}
- // Coverage-ignore-block(suite): Not run.
_FindChildVisitor visitor = new _FindChildVisitor(node);
member.accept(visitor);
return !visitor.foundNode;
diff --git a/pkg/front_end/lib/src/kernel/body_builder_context.dart b/pkg/front_end/lib/src/kernel/body_builder_context.dart
index 93c362d..3c42c95 100644
--- a/pkg/front_end/lib/src/kernel/body_builder_context.dart
+++ b/pkg/front_end/lib/src/kernel/body_builder_context.dart
@@ -683,6 +683,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
bool get hasFormalParameters => false;
}
@@ -790,6 +791,7 @@
inConstFields: inConstFields);
@override
+ // Coverage-ignore(suite): Not run.
bool get hasFormalParameters => true;
}
@@ -879,6 +881,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
bool get hasFormalParameters => true;
}
@@ -907,6 +910,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
bool get hasFormalParameters => true;
}
@@ -938,6 +942,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
bool get hasFormalParameters => true;
}
@@ -967,6 +972,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
bool get hasFormalParameters => true;
}
diff --git a/pkg/front_end/lib/src/kernel/collections.dart b/pkg/front_end/lib/src/kernel/collections.dart
index 92e9cba..bcbf67c 100644
--- a/pkg/front_end/lib/src/kernel/collections.dart
+++ b/pkg/front_end/lib/src/kernel/collections.dart
@@ -133,30 +133,35 @@
NullAwareElement(this.expression);
@override
+ // Coverage-ignore(suite): Not run.
MapLiteralEntry? toMapLiteralEntry(
void Function(TreeNode from, TreeNode to) onConvertElement) {
return unsupported("toMapLiteralEntry", fileOffset, getFileUri(this));
}
@override
+ // Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
printer.write('?');
printer.writeExpression(expression);
}
@override
+ // Coverage-ignore(suite): Not run.
void transformChildren(Transformer v) {
expression = v.transform(expression);
expression.parent = this;
}
@override
+ // Coverage-ignore(suite): Not run.
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression);
expression.parent = this;
}
@override
+ // Coverage-ignore(suite): Not run.
void visitChildren(Visitor v) {
expression.accept(v);
}
@@ -616,7 +621,10 @@
}
}
-mixin ControlFlowMapEntry implements MapLiteralEntry {
+/// Base class for all control-flow map entries.
+sealed class ControlFlowMapEntry implements MapLiteralEntry {}
+
+mixin ControlFlowMapEntryMixin implements MapLiteralEntry {
@override
Expression get key {
throw new UnsupportedError('ControlFlowMapEntry.key getter');
@@ -652,7 +660,9 @@
}
/// A null-aware entry in a map literal.
-class NullAwareMapEntry extends TreeNode with ControlFlowMapEntry {
+class NullAwareMapEntry extends TreeNode
+ with ControlFlowMapEntryMixin
+ implements ControlFlowMapEntry {
/// `true` if the key expression is null-aware, that is, marked with `?`.
bool isKeyNullAware;
@@ -672,6 +682,7 @@
required this.value});
@override
+ // Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
if (isKeyNullAware) {
printer.write('?');
@@ -685,6 +696,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void transformChildren(Transformer v) {
key = v.transform(key);
key.parent = this;
@@ -693,6 +705,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void transformOrRemoveChildren(RemovingTransformer v) {
key = v.transform(key);
key.parent = this;
@@ -701,6 +714,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void visitChildren(Visitor v) {
key.accept(v);
value.accept(v);
@@ -708,7 +722,9 @@
}
/// A spread element in a map literal.
-class SpreadMapEntry extends TreeNode with ControlFlowMapEntry {
+class SpreadMapEntry extends TreeNode
+ with ControlFlowMapEntryMixin
+ implements ControlFlowMapEntry {
Expression expression;
bool isNullAware;
@@ -756,7 +772,9 @@
}
/// An 'if' element in a map literal.
-class IfMapEntry extends TreeNode with ControlFlowMapEntry {
+class IfMapEntry extends TreeNode
+ with ControlFlowMapEntryMixin
+ implements ControlFlowMapEntry {
Expression condition;
MapLiteralEntry then;
MapLiteralEntry? otherwise;
@@ -820,11 +838,30 @@
}
}
+abstract interface class ForMapEntryBase implements TreeNode, MapLiteralEntry {
+ List<VariableDeclaration> get variables;
+
+ abstract Expression? condition;
+
+ List<Expression> get updates;
+
+ abstract MapLiteralEntry body;
+}
+
/// A 'for' element in a map literal.
-class ForMapEntry extends TreeNode with ControlFlowMapEntry {
+class ForMapEntry extends TreeNode
+ with ControlFlowMapEntryMixin
+ implements ForMapEntryBase, ControlFlowMapEntry {
+ @override
final List<VariableDeclaration> variables; // May be empty, but not null.
+
+ @override
Expression? condition; // May be null.
+
+ @override
final List<Expression> updates; // May be empty, but not null.
+
+ @override
MapLiteralEntry body;
ForMapEntry(this.variables, this.condition, this.updates, this.body) {
@@ -896,8 +933,64 @@
}
}
+class PatternForMapEntry extends TreeNode
+ with InternalTreeNode, ControlFlowMapEntryMixin
+ implements ForMapEntryBase, ControlFlowMapEntry {
+ PatternVariableDeclaration patternVariableDeclaration;
+ List<VariableDeclaration> intermediateVariables;
+
+ @override
+ final List<VariableDeclaration> variables;
+
+ @override
+ Expression? condition;
+
+ @override
+ final List<Expression> updates;
+
+ @override
+ MapLiteralEntry body;
+
+ PatternForMapEntry(
+ {required this.patternVariableDeclaration,
+ required this.intermediateVariables,
+ required this.variables,
+ required this.condition,
+ required this.updates,
+ required this.body});
+
+ @override
+ // Coverage-ignore(suite): Not run.
+ void toTextInternal(AstPrinter printer) {
+ patternVariableDeclaration.toTextInternal(printer);
+ printer.write('for (');
+ for (int index = 0; index < variables.length; index++) {
+ if (index > 0) {
+ printer.write(', ');
+ }
+ printer.writeVariableDeclaration(variables[index],
+ includeModifiersAndType: index == 0);
+ }
+ printer.write('; ');
+ if (condition != null) {
+ printer.writeExpression(condition!);
+ }
+ printer.write('; ');
+ printer.writeExpressions(updates);
+ printer.write(') ');
+ body.toTextInternal(printer);
+ }
+
+ @override
+ String toString() {
+ return "PatternForMapEntry(${toStringInternal()})";
+ }
+}
+
/// A 'for-in' element in a map literal.
-class ForInMapEntry extends TreeNode with ControlFlowMapEntry {
+class ForInMapEntry extends TreeNode
+ with ControlFlowMapEntryMixin
+ implements ControlFlowMapEntry {
VariableDeclaration variable; // Has no initializer.
Expression iterable;
Expression? syntheticAssignment; // May be null.
@@ -993,6 +1086,53 @@
}
}
+class IfCaseMapEntry extends TreeNode
+ with InternalTreeNode, ControlFlowMapEntryMixin
+ implements ControlFlowMapEntry {
+ Expression expression;
+ PatternGuard patternGuard;
+ MapLiteralEntry then;
+ MapLiteralEntry? otherwise;
+ List<Statement> prelude;
+
+ /// The type of the expression against which this pattern is matched.
+ ///
+ /// This is set during inference.
+ DartType? matchedValueType;
+
+ IfCaseMapEntry(
+ {required this.prelude,
+ required this.expression,
+ required this.patternGuard,
+ required this.then,
+ this.otherwise}) {
+ expression.parent = this;
+ patternGuard.parent = this;
+ then.parent = this;
+ otherwise?.parent = this;
+ }
+
+ @override
+ // Coverage-ignore(suite): Not run.
+ void toTextInternal(AstPrinter printer) {
+ printer.write('if (');
+ expression.toTextInternal(printer);
+ printer.write(' case ');
+ patternGuard.toTextInternal(printer);
+ printer.write(') ');
+ then.toTextInternal(printer);
+ if (otherwise != null) {
+ printer.write(' else ');
+ otherwise!.toTextInternal(printer);
+ }
+ }
+
+ @override
+ String toString() {
+ return "IfCaseMapEntry(${toStringInternal()})";
+ }
+}
+
/// Convert [entry] to an [Expression], if possible. If [entry] cannot be
/// converted an error reported through [helper] and an invalid expression is
/// returned.
@@ -1006,76 +1146,83 @@
void onConvertMapEntry(TreeNode from, TreeNode to), {
DartType? actualType,
}) {
- if (entry is SpreadMapEntry) {
- return new SpreadElement(entry.expression, isNullAware: entry.isNullAware)
- ..elementType = actualType
- ..fileOffset = entry.expression.fileOffset;
- }
- if (entry is IfMapEntry) {
- IfElement result = new IfElement(
- entry.condition,
- convertToElement(entry.then, helper, onConvertMapEntry),
- entry.otherwise == null
- ? null
- :
- // Coverage-ignore(suite): Not run.
- convertToElement(entry.otherwise!, helper, onConvertMapEntry))
- ..fileOffset = entry.fileOffset;
- onConvertMapEntry(entry, result);
- return result;
- }
- if (entry is IfCaseMapEntry) {
- IfCaseElement result = new IfCaseElement(
- prelude: entry.prelude,
- expression: entry.expression,
- patternGuard: entry.patternGuard,
- then: convertToElement(entry.then, helper, onConvertMapEntry),
- otherwise: entry.otherwise == null
- ? null
- :
- // Coverage-ignore(suite): Not run.
- convertToElement(entry.otherwise!, helper, onConvertMapEntry))
- ..matchedValueType = entry.matchedValueType
- ..fileOffset = entry.fileOffset;
- onConvertMapEntry(entry, result);
- return result;
- }
- if (entry is ForMapEntry) {
- if (entry is PatternForMapEntry) {
- PatternForElement result = new PatternForElement(
- patternVariableDeclaration: entry.patternVariableDeclaration,
- intermediateVariables: entry.intermediateVariables,
- variables: entry.variables,
- condition: entry.condition,
- updates: entry.updates,
- body: convertToElement(entry.body, helper, onConvertMapEntry))
- ..fileOffset = entry.fileOffset;
- onConvertMapEntry(entry, result);
- return result;
- } else {
- ForElement result = new ForElement(
- entry.variables,
- entry.condition,
- entry.updates,
- convertToElement(entry.body, helper, onConvertMapEntry))
- ..fileOffset = entry.fileOffset;
- onConvertMapEntry(entry, result);
- return result;
+ if (entry is ControlFlowMapEntry) {
+ switch (entry) {
+ case SpreadMapEntry():
+ return new SpreadElement(entry.expression,
+ isNullAware: entry.isNullAware)
+ ..elementType = actualType
+ ..fileOffset = entry.expression.fileOffset;
+ case IfMapEntry():
+ IfElement result = new IfElement(
+ entry.condition,
+ convertToElement(entry.then, helper, onConvertMapEntry),
+ entry.otherwise == null
+ ? null
+ :
+ // Coverage-ignore(suite): Not run.
+ convertToElement(entry.otherwise!, helper, onConvertMapEntry))
+ ..fileOffset = entry.fileOffset;
+ onConvertMapEntry(entry, result);
+ return result;
+ case NullAwareMapEntry():
+ // Coverage-ignore(suite): Not run.
+ return _convertToErroneousElement(entry, helper);
+ case IfCaseMapEntry():
+ IfCaseElement result = new IfCaseElement(
+ prelude: entry.prelude,
+ expression: entry.expression,
+ patternGuard: entry.patternGuard,
+ then: convertToElement(entry.then, helper, onConvertMapEntry),
+ otherwise: entry.otherwise == null
+ ? null
+ :
+ // Coverage-ignore(suite): Not run.
+ convertToElement(entry.otherwise!, helper, onConvertMapEntry))
+ ..matchedValueType = entry.matchedValueType
+ ..fileOffset = entry.fileOffset;
+ onConvertMapEntry(entry, result);
+ return result;
+ case PatternForMapEntry():
+ PatternForElement result = new PatternForElement(
+ patternVariableDeclaration: entry.patternVariableDeclaration,
+ intermediateVariables: entry.intermediateVariables,
+ variables: entry.variables,
+ condition: entry.condition,
+ updates: entry.updates,
+ body: convertToElement(entry.body, helper, onConvertMapEntry))
+ ..fileOffset = entry.fileOffset;
+ onConvertMapEntry(entry, result);
+ return result;
+ case ForMapEntry():
+ ForElement result = new ForElement(
+ entry.variables,
+ entry.condition,
+ entry.updates,
+ convertToElement(entry.body, helper, onConvertMapEntry))
+ ..fileOffset = entry.fileOffset;
+ onConvertMapEntry(entry, result);
+ return result;
+ case ForInMapEntry():
+ ForInElement result = new ForInElement(
+ entry.variable,
+ entry.iterable,
+ entry.syntheticAssignment,
+ entry.expressionEffects,
+ convertToElement(entry.body, helper, onConvertMapEntry),
+ entry.problem,
+ isAsync: entry.isAsync)
+ ..fileOffset = entry.fileOffset;
+ onConvertMapEntry(entry, result);
+ return result;
}
+ } else {
+ return _convertToErroneousElement(entry, helper);
}
- if (entry is ForInMapEntry) {
- ForInElement result = new ForInElement(
- entry.variable,
- entry.iterable,
- entry.syntheticAssignment,
- entry.expressionEffects,
- convertToElement(entry.body, helper, onConvertMapEntry),
- entry.problem,
- isAsync: entry.isAsync)
- ..fileOffset = entry.fileOffset;
- onConvertMapEntry(entry, result);
- return result;
- }
+}
+
+Expression _convertToErroneousElement(
+ MapLiteralEntry entry, InferenceHelper? helper) {
Expression key = entry.key;
if (key is InvalidExpression) {
Expression value = entry.value;
@@ -1108,7 +1255,6 @@
case IfCaseElement():
return isConvertibleToMapEntry(element.then) &&
(element.otherwise == null ||
- // Coverage-ignore(suite): Not run.
isConvertibleToMapEntry(element.otherwise!));
case ForElement():
return isConvertibleToMapEntry(element.body);
@@ -1127,95 +1273,89 @@
/// [onConvertElement] is called when a [ForElement], [ForInElement], or
/// [IfElement] is converted to a [ForMapEntry], [ForInMapEntry], or
/// [IfMapEntry], respectively.
-MapLiteralEntry convertToMapEntry(ControlFlowElement element,
- InferenceHelper helper, void onConvertElement(TreeNode from, TreeNode to)) {
- switch (element) {
- case SpreadElement():
- return new SpreadMapEntry(element.expression,
- isNullAware: element.isNullAware)
- ..fileOffset = element.expression.fileOffset;
-
- case NullAwareElement():
- return convertToErroneousMapEntry(element, helper);
-
- case IfElement():
- IfMapEntry result = new IfMapEntry(
- element.condition,
- _convertNestedToMapEntry(element.then, helper, onConvertElement),
- element.otherwise == null
- ? null
- : _convertNestedToMapEntry(
- element.otherwise!, helper, onConvertElement))
- ..fileOffset = element.fileOffset;
- onConvertElement(element, result);
- return result;
-
- case IfCaseElement():
- IfCaseMapEntry result = new IfCaseMapEntry(
- prelude: [],
- expression: element.expression,
- patternGuard: element.patternGuard,
- then:
- _convertNestedToMapEntry(element.then, helper, onConvertElement),
- otherwise: element.otherwise == null
- ? null
- :
- // Coverage-ignore(suite): Not run.
- _convertNestedToMapEntry(
- element.otherwise!, helper, onConvertElement))
- ..matchedValueType = element.matchedValueType
- ..fileOffset = element.fileOffset;
- onConvertElement(element, result);
- return result;
-
- case PatternForElement():
- PatternForMapEntry result = new PatternForMapEntry(
- patternVariableDeclaration: element.patternVariableDeclaration,
- intermediateVariables: element.intermediateVariables,
- variables: element.variables,
- condition: element.condition,
- updates: element.updates,
- body:
- _convertNestedToMapEntry(element.body, helper, onConvertElement))
- ..fileOffset = element.fileOffset;
- onConvertElement(element, result);
- return result;
-
- case ForElement():
- ForMapEntry result = new ForMapEntry(
- element.variables,
- element.condition,
- element.updates,
- _convertNestedToMapEntry(element.body, helper, onConvertElement))
- ..fileOffset = element.fileOffset;
- onConvertElement(element, result);
- return result;
-
- case ForInElement():
- ForInMapEntry result = new ForInMapEntry(
- element.variable,
- element.iterable,
- element.syntheticAssignment,
- element.expressionEffects,
- _convertNestedToMapEntry(element.body, helper, onConvertElement),
- element.problem,
- isAsync: element.isAsync)
- ..fileOffset = element.fileOffset;
- onConvertElement(element, result);
- return result;
- }
-}
-
-MapLiteralEntry _convertNestedToMapEntry(Expression element,
- InferenceHelper helper, void onConvertElement(TreeNode from, TreeNode to)) {
+MapLiteralEntry convertToMapEntry(Expression element, InferenceHelper helper,
+ void onConvertElement(TreeNode from, TreeNode to)) {
if (element is ControlFlowElement) {
- return convertToMapEntry(element, helper, onConvertElement);
+ switch (element) {
+ case SpreadElement():
+ return new SpreadMapEntry(element.expression,
+ isNullAware: element.isNullAware)
+ ..fileOffset = element.expression.fileOffset;
+
+ case NullAwareElement():
+ // Coverage-ignore(suite): Not run.
+ return _convertToErroneousMapEntry(element, helper);
+
+ case IfElement():
+ IfMapEntry result = new IfMapEntry(
+ element.condition,
+ convertToMapEntry(element.then, helper, onConvertElement),
+ element.otherwise == null
+ ? null
+ : convertToMapEntry(
+ element.otherwise!, helper, onConvertElement))
+ ..fileOffset = element.fileOffset;
+ onConvertElement(element, result);
+ return result;
+
+ case IfCaseElement():
+ IfCaseMapEntry result = new IfCaseMapEntry(
+ prelude: [],
+ expression: element.expression,
+ patternGuard: element.patternGuard,
+ then: convertToMapEntry(element.then, helper, onConvertElement),
+ otherwise: element.otherwise == null
+ ? null
+ : convertToMapEntry(
+ element.otherwise!, helper, onConvertElement))
+ ..matchedValueType = element.matchedValueType
+ ..fileOffset = element.fileOffset;
+ onConvertElement(element, result);
+ return result;
+
+ case PatternForElement():
+ PatternForMapEntry result = new PatternForMapEntry(
+ patternVariableDeclaration: element.patternVariableDeclaration,
+ intermediateVariables: element.intermediateVariables,
+ variables: element.variables,
+ condition: element.condition,
+ updates: element.updates,
+ body: convertToMapEntry(element.body, helper, onConvertElement))
+ ..fileOffset = element.fileOffset;
+ onConvertElement(element, result);
+ return result;
+
+ case ForElement():
+ ForMapEntry result = new ForMapEntry(
+ element.variables,
+ element.condition,
+ element.updates,
+ convertToMapEntry(element.body, helper, onConvertElement))
+ ..fileOffset = element.fileOffset;
+ onConvertElement(element, result);
+ return result;
+
+ case ForInElement():
+ ForInMapEntry result = new ForInMapEntry(
+ element.variable,
+ element.iterable,
+ element.syntheticAssignment,
+ element.expressionEffects,
+ convertToMapEntry(element.body, helper, onConvertElement),
+ element.problem,
+ isAsync: element.isAsync)
+ ..fileOffset = element.fileOffset;
+ onConvertElement(element, result);
+ return result;
+ }
} else {
- return convertToErroneousMapEntry(element, helper);
+ // Coverage-ignore-block(suite): Not run.
+ return _convertToErroneousMapEntry(element, helper);
}
}
-MapLiteralEntry convertToErroneousMapEntry(
+// Coverage-ignore(suite): Not run.
+MapLiteralEntry _convertToErroneousMapEntry(
Expression element, InferenceHelper helper) {
return new MapLiteralEntry(
helper.buildProblem(
diff --git a/pkg/front_end/lib/src/kernel/forest.dart b/pkg/front_end/lib/src/kernel/forest.dart
index ea1c766..f53374e 100644
--- a/pkg/front_end/lib/src/kernel/forest.dart
+++ b/pkg/front_end/lib/src/kernel/forest.dart
@@ -18,11 +18,13 @@
ForInMapEntry,
ForMapEntry,
IfCaseElement,
+ IfCaseMapEntry,
IfElement,
IfMapEntry,
NullAwareElement,
NullAwareMapEntry,
PatternForElement,
+ PatternForMapEntry,
SpreadElement;
import 'internal_ast.dart';
diff --git a/pkg/front_end/lib/src/kernel/internal_ast.dart b/pkg/front_end/lib/src/kernel/internal_ast.dart
index dec3e4f..f992118 100644
--- a/pkg/front_end/lib/src/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/kernel/internal_ast.dart
@@ -32,7 +32,6 @@
import '../type_inference/inference_results.dart';
import '../type_inference/inference_visitor.dart';
import '../type_inference/type_schema.dart' show UnknownType;
-import 'collections.dart';
typedef SharedMatchContext = shared
.MatchContext<TreeNode, Expression, Pattern, DartType, VariableDeclaration>;
@@ -3124,106 +3123,6 @@
}
}
-class IfCaseMapEntry extends TreeNode
- with InternalTreeNode, ControlFlowMapEntry {
- Expression expression;
- PatternGuard patternGuard;
- MapLiteralEntry then;
- MapLiteralEntry? otherwise;
- List<Statement> prelude;
-
- /// The type of the expression against which this pattern is matched.
- ///
- /// This is set during inference.
- DartType? matchedValueType;
-
- IfCaseMapEntry(
- {required this.prelude,
- required this.expression,
- required this.patternGuard,
- required this.then,
- this.otherwise}) {
- expression.parent = this;
- patternGuard.parent = this;
- then.parent = this;
- otherwise?.parent = this;
- }
-
- @override
- // Coverage-ignore(suite): Not run.
- void toTextInternal(AstPrinter printer) {
- printer.write('if (');
- expression.toTextInternal(printer);
- printer.write(' case ');
- patternGuard.toTextInternal(printer);
- printer.write(') ');
- then.toTextInternal(printer);
- if (otherwise != null) {
- printer.write(' else ');
- otherwise!.toTextInternal(printer);
- }
- }
-
- @override
- String toString() {
- return "IfCaseMapEntry(${toStringInternal()})";
- }
-}
-
-class PatternForMapEntry extends TreeNode
- with InternalTreeNode, ControlFlowMapEntry
- implements ForMapEntry {
- PatternVariableDeclaration patternVariableDeclaration;
- List<VariableDeclaration> intermediateVariables;
-
- @override
- final List<VariableDeclaration> variables;
-
- @override
- Expression? condition;
-
- @override
- final List<Expression> updates;
-
- @override
- MapLiteralEntry body;
-
- PatternForMapEntry(
- {required this.patternVariableDeclaration,
- required this.intermediateVariables,
- required this.variables,
- required this.condition,
- required this.updates,
- required this.body});
-
- @override
- // Coverage-ignore(suite): Not run.
- void toTextInternal(AstPrinter printer) {
- patternVariableDeclaration.toTextInternal(printer);
- printer.write('for (');
- for (int index = 0; index < variables.length; index++) {
- if (index > 0) {
- printer.write(', ');
- }
- printer.writeVariableDeclaration(variables[index],
- includeModifiersAndType: index == 0);
- }
- printer.write('; ');
- if (condition != null) {
- printer.writeExpression(condition!);
- }
- printer.write('; ');
- printer.writeExpressions(updates);
- printer.write(') ');
- body.toTextInternal(printer);
- }
-
- @override
- String toString() {
- return "PatternForMapEntry(${toStringInternal()})";
- }
-}
-
/// Data structure used by the body builder in place of [ObjectPattern], to
/// allow additional information to be captured that is needed during type
/// inference.
@@ -3297,6 +3196,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void transformChildren(Transformer v) {
value = v.transform(value)..parent = this;
}
diff --git a/pkg/front_end/lib/src/kernel/kernel_constants.dart b/pkg/front_end/lib/src/kernel/kernel_constants.dart
index 4acd403..d23ff515 100644
--- a/pkg/front_end/lib/src/kernel/kernel_constants.dart
+++ b/pkg/front_end/lib/src/kernel/kernel_constants.dart
@@ -15,6 +15,7 @@
KernelConstantErrorReporter(this.loader);
@override
+ // Coverage-ignore(suite): Not run.
bool get supportsTrackingReportedErrors => true;
@override
diff --git a/pkg/front_end/lib/src/kernel/kernel_target.dart b/pkg/front_end/lib/src/kernel/kernel_target.dart
index 526a03b..3572a2c 100644
--- a/pkg/front_end/lib/src/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/kernel/kernel_target.dart
@@ -1812,7 +1812,9 @@
void readPatchFiles(SourceLibraryBuilder libraryBuilder,
CompilationUnit compilationUnit, Uri originImportUri) {
- assert(originImportUri.isScheme("dart"),
+ assert(
+ originImportUri.isScheme("dart"),
+ // Coverage-ignore(suite): Not run.
"Unexpected origin import uri: $originImportUri");
List<Uri>? patches = uriTranslator.getDartPatches(originImportUri.path);
if (patches != null) {
diff --git a/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart b/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart
index 4cabaea..668e55b 100644
--- a/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart
+++ b/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart
@@ -104,9 +104,11 @@
_ErrorReporter(this._reportError);
@override
+ // Coverage-ignore(suite): Not run.
bool get supportsTrackingReportedErrors => false;
@override
+ // Coverage-ignore(suite): Not run.
bool get hasSeenError {
return unsupported("_ErrorReporter.hasSeenError", -1, null);
}
diff --git a/pkg/front_end/lib/src/kernel/verifier.dart b/pkg/front_end/lib/src/kernel/verifier.dart
index edc88d7..f7ff87c 100644
--- a/pkg/front_end/lib/src/kernel/verifier.dart
+++ b/pkg/front_end/lib/src/kernel/verifier.dart
@@ -33,7 +33,6 @@
return listener.errors;
}
-// Coverage-ignore(suite): Not run.
class FastaVerificationErrorListener implements VerificationErrorListener {
final CompilerContext compilerContext;
List<LocatedMessage> errors = [];
@@ -41,6 +40,7 @@
FastaVerificationErrorListener(this.compilerContext);
@override
+ // Coverage-ignore(suite): Not run.
void reportError(String details,
{required TreeNode? node,
required Uri? problemUri,
diff --git a/pkg/front_end/lib/src/kernel/wildcard_lowering.dart b/pkg/front_end/lib/src/kernel/wildcard_lowering.dart
index adf1d08..c37fdf4 100644
--- a/pkg/front_end/lib/src/kernel/wildcard_lowering.dart
+++ b/pkg/front_end/lib/src/kernel/wildcard_lowering.dart
@@ -27,12 +27,14 @@
return name.startsWith(wildcardPrefix) && name.endsWith(wildcardFormalSuffix);
}
+// Coverage-ignore(suite): Not run.
/// Whether the given [name] is a wildcard type variable.
bool isWildcardLoweredTypeVariable(String name) {
return name.startsWith(wildcardPrefix) &&
name.endsWith(wildcardTypeVariableSuffix);
}
+// Coverage-ignore(suite): Not run.
/// Whether the given [name] is a wildcard variable.
bool isWildcardLoweredVariable(String name) {
return name.startsWith(wildcardPrefix) &&
diff --git a/pkg/front_end/lib/src/macros/macro_injected_impl.dart b/pkg/front_end/lib/src/macros/macro_injected_impl.dart
index 7a34233..ed94989 100644
--- a/pkg/front_end/lib/src/macros/macro_injected_impl.dart
+++ b/pkg/front_end/lib/src/macros/macro_injected_impl.dart
@@ -13,6 +13,7 @@
/// If set, overrides the CFE's macro implementation.
MacroImplementation? macroImplementation;
+// Coverage-ignore(suite): Not run.
/// An injected macro implementation.
class MacroImplementation {
final MacroPackageConfigs packageConfigs;
diff --git a/pkg/front_end/lib/src/source/source_builder_mixins.dart b/pkg/front_end/lib/src/source/source_builder_mixins.dart
index 8201a79..3ed7c07 100644
--- a/pkg/front_end/lib/src/source/source_builder_mixins.dart
+++ b/pkg/front_end/lib/src/source/source_builder_mixins.dart
@@ -265,6 +265,7 @@
}
if (arguments != null && arguments.length != typeVariablesCount) {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"SourceDeclarationBuilderMixin.buildAliasedTypeArguments: "
"the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/source_class_builder.dart b/pkg/front_end/lib/src/source/source_class_builder.dart
index d2b4cf6..c073e9d 100644
--- a/pkg/front_end/lib/src/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/source/source_class_builder.dart
@@ -519,6 +519,7 @@
}
if (arguments != null && arguments.length != typeVariablesCount) {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"SourceClassBuilder.buildAliasedTypeArguments: "
"the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/source_compilation_unit.dart b/pkg/front_end/lib/src/source/source_compilation_unit.dart
index 55bcc86..b3af465 100644
--- a/pkg/front_end/lib/src/source/source_compilation_unit.dart
+++ b/pkg/front_end/lib/src/source/source_compilation_unit.dart
@@ -335,6 +335,7 @@
"Source library builder as already been created for $this.");
_libraryBuilder = _sourceLibraryBuilder;
if (isPart) {
+ // Coverage-ignore-block(suite): Not run.
// This is a part with no enclosing library.
addProblem(messagePartOrphan, 0, 1, fileUri);
_clearPartsAndReportExporters();
@@ -727,7 +728,9 @@
@override
void addImportsToScope() {
- bool hasCoreImport = originImportUri == dartCore && !forPatchLibrary;
+ bool hasCoreImport = originImportUri == dartCore &&
+ // Coverage-ignore(suite): Not run.
+ !forPatchLibrary;
for (Import import in _builderFactoryResult.imports) {
if (import.importedCompilationUnit?.isPart ?? false) {
// Coverage-ignore-block(suite): Not run.
diff --git a/pkg/front_end/lib/src/source/source_constructor_builder.dart b/pkg/front_end/lib/src/source/source_constructor_builder.dart
index 2572203..6e14f66 100644
--- a/pkg/front_end/lib/src/source/source_constructor_builder.dart
+++ b/pkg/front_end/lib/src/source/source_constructor_builder.dart
@@ -596,6 +596,7 @@
if (declaration is ClassBuilder) {
superclassBuilder = declaration;
} else {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
"Unaliased 'declaration' isn't a ClassBuilder.",
@@ -610,6 +611,7 @@
return null;
}
} else {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
"'supertype' isn't a NamedTypeBuilder.",
@@ -641,6 +643,7 @@
if (constructorBuilder is ConstructorBuilder) {
return constructorBuilder;
} else {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
"Can't find a constructor with name '${superTarget.name.text}' in "
@@ -775,6 +778,7 @@
(positionalSuperParameters ??= <int?>[]).add(null);
}
} else {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder"
".finalizeSuperInitializingFormals: "
diff --git a/pkg/front_end/lib/src/source/source_enum_builder.dart b/pkg/front_end/lib/src/source/source_enum_builder.dart
index 2abf5d9..c2cf43a 100644
--- a/pkg/front_end/lib/src/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/source/source_enum_builder.dart
@@ -134,6 +134,7 @@
ConstructorScope constructorScope) {
final int startCharOffsetComputed =
metadata == null ? startCharOffset : metadata.first.charOffset;
+ // Coverage-ignore(suite): Not run.
supertypeBuilder ??= new NamedTypeBuilderImpl(
const PredefinedTypeName("_Enum"), const NullabilityBuilder.omitted(),
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
@@ -186,7 +187,6 @@
objectType = new NamedTypeBuilderImpl(
const PredefinedTypeName("Object"), const NullabilityBuilder.omitted(),
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
- // Coverage-ignore(suite): Not run.
selfType = new NamedTypeBuilderImpl(new SyntheticTypeName(name, charOffset),
const NullabilityBuilder.omitted(),
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected,
diff --git a/pkg/front_end/lib/src/source/source_library_builder.dart b/pkg/front_end/lib/src/source/source_library_builder.dart
index 068f780..e87cf1f 100644
--- a/pkg/front_end/lib/src/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/source/source_library_builder.dart
@@ -2172,6 +2172,7 @@
if (typeArguments.isEmpty) return;
if (functionType.typeParameters.length != typeArguments.length) {
+ // Coverage-ignore-block(suite): Not run.
assert(loader.assertProblemReportedElsewhere(
"SourceLibraryBuilder.checkBoundsInInstantiation: "
"the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/source_loader.dart b/pkg/front_end/lib/src/source/source_loader.dart
index 48d8dd8..cdaee95 100644
--- a/pkg/front_end/lib/src/source/source_loader.dart
+++ b/pkg/front_end/lib/src/source/source_loader.dart
@@ -254,6 +254,7 @@
};
assert(
expectedFutureProblemsForCurrentPhase.isEmpty || hasSeenError,
+ // Coverage-ignore(suite): Not run.
"Expected problems to be reported, but there were none.\n"
"Current compilation phase: ${currentPhase}\n"
"Expected at these locations:\n"
@@ -1477,9 +1478,8 @@
}
_sourceLibraryBuilders = sourceLibraries;
assert(
- _compilationUnits.values.every(
- (compilationUnit) => !(compilationUnit is SourceCompilationUnit &&
- // Coverage-ignore(suite): Not run.
+ _compilationUnits.values.every((compilationUnit) =>
+ !(compilationUnit is SourceCompilationUnit &&
compilationUnit.isAugmenting)),
// Coverage-ignore(suite): Not run.
"Augmentation library found in libraryBuilders: " +
diff --git a/pkg/front_end/lib/src/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/source/source_type_alias_builder.dart
index 4325d2c..e25255f 100644
--- a/pkg/front_end/lib/src/source/source_type_alias_builder.dart
+++ b/pkg/front_end/lib/src/source/source_type_alias_builder.dart
@@ -303,6 +303,7 @@
}
if (arguments != null && arguments.length != typeVariablesCount) {
+ // Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"SourceTypeAliasBuilder.buildAliasedTypeArguments: "
"the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart b/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart
index bb0c6e4..59fe74d 100644
--- a/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart
+++ b/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart
@@ -429,11 +429,19 @@
void addLocalMember(String name, MemberBuilder builder,
{required bool setter}) {
- (setter ? _setables : _getables)![name] = builder;
+ (setter
+ ?
+ // Coverage-ignore(suite): Not run.
+ _setables
+ : _getables)![name] = builder;
}
MemberBuilder? lookupLocalMember(String name, {required bool setter}) {
- return (setter ? _setables : _getables)![name] as MemberBuilder?;
+ return (setter
+ ?
+ // Coverage-ignore(suite): Not run.
+ _setables
+ : _getables)![name] as MemberBuilder?;
}
NameSpace buildNameSpace(IDeclarationBuilder parent) {
@@ -448,7 +456,6 @@
if (_typeVariables != null) {
NominalVariableBuilder? tv = _typeVariables![name];
if (tv != null) {
- // Coverage-ignore-block(suite): Not run.
parent.addProblem(
templateConflictsWithTypeVariable.withArguments(name),
member.charOffset,
diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
index 1dab4ce..ada0c75 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
@@ -41,12 +41,15 @@
ForInElement,
ForInMapEntry,
ForMapEntry,
+ ForMapEntryBase,
IfCaseElement,
+ IfCaseMapEntry,
IfElement,
IfMapEntry,
NullAwareElement,
NullAwareMapEntry,
PatternForElement,
+ PatternForMapEntry,
SpreadElement,
SpreadMapEntry,
convertToElement;
@@ -2772,6 +2775,7 @@
}
case NullAwareElement(:Expression expression):
if (expression is ControlFlowElement) {
+ // Coverage-ignore-block(suite): Not run.
checkElement(expression, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
@@ -2790,6 +2794,7 @@
inferredConditionTypes);
}
if (otherwise is ControlFlowElement) {
+ // Coverage-ignore-block(suite): Not run.
checkElement(otherwise, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
@@ -3456,28 +3461,30 @@
DartType valueType,
VariableDeclaration result,
List<Statement> body) {
- if (entry is SpreadMapEntry) {
- _translateSpreadEntry(
- entry, receiverType, keyType, valueType, result, body);
- } else if (entry is NullAwareMapEntry) {
- _translateNullAwareMapEntry(
- entry, receiverType, keyType, valueType, result, body);
- } else if (entry is IfMapEntry) {
- _translateIfEntry(entry, receiverType, keyType, valueType, result, body);
- } else if (entry is IfCaseMapEntry) {
- _translateIfCaseEntry(
- entry, receiverType, keyType, valueType, result, body);
- } else if (entry is ForMapEntry) {
- if (entry is PatternForMapEntry) {
- _translatePatternForEntry(
- entry, receiverType, keyType, valueType, result, body);
- } else {
- _translateForEntry(
- entry, receiverType, keyType, valueType, result, body);
+ if (entry is ControlFlowMapEntry) {
+ switch (entry) {
+ case SpreadMapEntry():
+ _translateSpreadEntry(
+ entry, receiverType, keyType, valueType, result, body);
+ case NullAwareMapEntry():
+ _translateNullAwareMapEntry(
+ entry, receiverType, keyType, valueType, result, body);
+ case IfMapEntry():
+ _translateIfEntry(
+ entry, receiverType, keyType, valueType, result, body);
+ case IfCaseMapEntry():
+ _translateIfCaseEntry(
+ entry, receiverType, keyType, valueType, result, body);
+ case PatternForMapEntry():
+ _translatePatternForEntry(
+ entry, receiverType, keyType, valueType, result, body);
+ case ForMapEntry():
+ _translateForEntry(
+ entry, receiverType, keyType, valueType, result, body);
+ case ForInMapEntry():
+ _translateForInEntry(
+ entry, receiverType, keyType, valueType, result, body);
}
- } else if (entry is ForInMapEntry) {
- _translateForInEntry(
- entry, receiverType, keyType, valueType, result, body);
} else {
_addNormalEntry(entry, receiverType, result, body);
}
@@ -3899,6 +3906,7 @@
}
case NullAwareElement():
if (currentPart != null) {
+ // Coverage-ignore-block(suite): Not run.
parts.add(makeLiteral(node.fileOffset, currentPart));
currentPart = null;
}
@@ -3908,8 +3916,8 @@
makeLiteral(element.fileOffset, []), iterableType,
nullCheckedValue: makeLiteral(element.fileOffset,
[_createNullCheckedVariableGet(temp)])));
+ // Coverage-ignore(suite): Not run.
case IfElement():
- // Coverage-ignore-block(suite): Not run.
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
currentPart = null;
@@ -3923,10 +3931,10 @@
: makeLiteral(element.fileOffset, []);
parts.add(_createConditionalExpression(
element.fileOffset, condition, then, otherwise, iterableType));
+ // Coverage-ignore(suite): Not run.
case IfCaseElement():
case ForElement():
case ForInElement():
- // Coverage-ignore-block(suite): Not run.
// Rejected earlier.
problems.unhandled("${element.runtimeType}",
"_translateConstListOrSet", element.fileOffset, helper.uri);
@@ -3975,100 +3983,108 @@
for (; i < node.entries.length; ++i) {
MapLiteralEntry entry = node.entries[i];
- if (entry is SpreadMapEntry) {
- if (currentPart != null) {
- parts.add(makeLiteral(node.fileOffset, currentPart));
- currentPart = null;
+ if (entry is ControlFlowMapEntry) {
+ switch (entry) {
+ case SpreadMapEntry():
+ if (currentPart != null) {
+ parts.add(makeLiteral(node.fileOffset, currentPart));
+ currentPart = null;
+ }
+ Expression spreadExpression = entry.expression;
+ if (entry.isNullAware) {
+ VariableDeclaration temp = _createVariable(spreadExpression,
+ collectionType.withDeclaredNullability(Nullability.nullable));
+ parts.add(_createNullAwareGuard(entry.fileOffset, temp,
+ makeLiteral(entry.fileOffset, []), collectionType));
+ } else {
+ parts.add(spreadExpression);
+ }
+ case NullAwareMapEntry():
+ assert(entry.isKeyNullAware || entry.isValueNullAware);
+ if (currentPart != null) {
+ // Coverage-ignore-block(suite): Not run.
+ parts.add(makeLiteral(node.fileOffset, currentPart));
+ currentPart = null;
+ }
+
+ Expression keyExpression = entry.key;
+ Expression valueExpression = entry.value;
+
+ // Since the desugared map entry may include promotions of the key
+ // or the value expressions, we can't finalize it until the later
+ // stages of desugaring. To assign the promoted expressions as
+ // necessary, we keep track of the map entry node via
+ // [addedMapLiteralEntry].
+ MapLiteralEntry? addedMapLiteralEntry;
+
+ Expression desugaredExpression = new NullLiteral();
+
+ if (entry.isValueNullAware) {
+ VariableDeclaration valueTemp = _createVariable(valueExpression,
+ node.valueType.withDeclaredNullability(Nullability.nullable));
+ valueExpression = _createNullCheckedVariableGet(valueTemp);
+ Expression defaultValue = makeLiteral(entry.fileOffset, []);
+ addedMapLiteralEntry ??=
+ new MapLiteralEntry(keyExpression, valueExpression);
+ Expression nullCheckedValue =
+ makeLiteral(entry.value.fileOffset, [addedMapLiteralEntry]);
+ desugaredExpression = _createNullAwareGuard(
+ entry.fileOffset, valueTemp, defaultValue, collectionType,
+ nullCheckedValue: nullCheckedValue);
+ }
+
+ if (entry.isKeyNullAware) {
+ VariableDeclaration keyTemp = _createVariable(entry.key,
+ node.keyType.withDeclaredNullability(Nullability.nullable));
+ keyExpression = _createNullCheckedVariableGet(keyTemp);
+ Expression defaultValue = makeLiteral(entry.fileOffset, []);
+ Expression nullCheckedKey;
+ if (addedMapLiteralEntry == null) {
+ assert(!entry.isValueNullAware);
+ addedMapLiteralEntry =
+ new MapLiteralEntry(keyExpression, valueExpression);
+ nullCheckedKey =
+ makeLiteral(entry.key.fileOffset, [addedMapLiteralEntry]);
+ } else {
+ assert(entry.isValueNullAware);
+ addedMapLiteralEntry.key = keyExpression
+ ..parent = addedMapLiteralEntry;
+ nullCheckedKey = desugaredExpression;
+ }
+ desugaredExpression = _createNullAwareGuard(
+ entry.fileOffset, keyTemp, defaultValue, collectionType,
+ nullCheckedValue: nullCheckedKey);
+ }
+
+ // Since either the key or the value is null-aware,
+ // [desugaredExpression] should be replaced with a null-checking
+ // [Expression].
+ assert(addedMapLiteralEntry != null &&
+ desugaredExpression is! NullLiteral);
+
+ parts.add(desugaredExpression);
+ // Coverage-ignore(suite): Not run.
+ case IfMapEntry():
+ if (currentPart != null) {
+ parts.add(makeLiteral(node.fileOffset, currentPart));
+ currentPart = null;
+ }
+ Expression condition = entry.condition;
+ Expression then = makeLiteral(entry.then.fileOffset, [entry.then]);
+ Expression otherwise = entry.otherwise != null
+ ? makeLiteral(entry.otherwise!.fileOffset, [entry.otherwise!])
+ : makeLiteral(node.fileOffset, []);
+ parts.add(_createConditionalExpression(
+ entry.fileOffset, condition, then, otherwise, collectionType));
+ // Coverage-ignore(suite): Not run.
+ case IfCaseMapEntry():
+ case PatternForMapEntry():
+ case ForMapEntry():
+ case ForInMapEntry():
+ // Rejected earlier.
+ problems.unhandled("${entry.runtimeType}", "_translateConstMap",
+ entry.fileOffset, helper.uri);
}
- Expression spreadExpression = entry.expression;
- if (entry.isNullAware) {
- VariableDeclaration temp = _createVariable(spreadExpression,
- collectionType.withDeclaredNullability(Nullability.nullable));
- parts.add(_createNullAwareGuard(entry.fileOffset, temp,
- makeLiteral(entry.fileOffset, []), collectionType));
- } else {
- parts.add(spreadExpression);
- }
- } else if (entry is NullAwareMapEntry) {
- assert(entry.isKeyNullAware || entry.isValueNullAware);
- if (currentPart != null) {
- parts.add(makeLiteral(node.fileOffset, currentPart));
- currentPart = null;
- }
-
- Expression keyExpression = entry.key;
- Expression valueExpression = entry.value;
-
- // Since the desugared map entry may include promotions of the key or
- // the value expressions, we can't finalize it until the later stages of
- // desugaring. To assign the promoted expressions as necessary, we keep
- // track of the map entry node via [addedMapLiteralEntry].
- MapLiteralEntry? addedMapLiteralEntry;
-
- Expression desugaredExpression = new NullLiteral();
-
- if (entry.isValueNullAware) {
- VariableDeclaration valueTemp = _createVariable(valueExpression,
- node.valueType.withDeclaredNullability(Nullability.nullable));
- valueExpression = _createNullCheckedVariableGet(valueTemp);
- Expression defaultValue = makeLiteral(entry.fileOffset, []);
- addedMapLiteralEntry ??=
- new MapLiteralEntry(keyExpression, valueExpression);
- Expression nullCheckedValue =
- makeLiteral(entry.value.fileOffset, [addedMapLiteralEntry]);
- desugaredExpression = _createNullAwareGuard(
- entry.fileOffset, valueTemp, defaultValue, collectionType,
- nullCheckedValue: nullCheckedValue);
- }
-
- if (entry.isKeyNullAware) {
- VariableDeclaration keyTemp = _createVariable(entry.key,
- node.keyType.withDeclaredNullability(Nullability.nullable));
- keyExpression = _createNullCheckedVariableGet(keyTemp);
- Expression defaultValue = makeLiteral(entry.fileOffset, []);
- Expression nullCheckedKey;
- if (addedMapLiteralEntry == null) {
- assert(!entry.isValueNullAware);
- addedMapLiteralEntry =
- new MapLiteralEntry(keyExpression, valueExpression);
- nullCheckedKey =
- makeLiteral(entry.key.fileOffset, [addedMapLiteralEntry]);
- } else {
- assert(entry.isValueNullAware);
- addedMapLiteralEntry.key = keyExpression
- ..parent = addedMapLiteralEntry;
- nullCheckedKey = desugaredExpression;
- }
- desugaredExpression = _createNullAwareGuard(
- entry.fileOffset, keyTemp, defaultValue, collectionType,
- nullCheckedValue: nullCheckedKey);
- }
-
- // Since either the key or the value is null-aware,
- // [desugaredExpression] should be replaced with a null-checking
- // [Expression].
- assert(addedMapLiteralEntry != null &&
- desugaredExpression is! NullLiteral);
-
- parts.add(desugaredExpression);
- } else if (entry is IfMapEntry) {
- // Coverage-ignore-block(suite): Not run.
- if (currentPart != null) {
- parts.add(makeLiteral(node.fileOffset, currentPart));
- currentPart = null;
- }
- Expression condition = entry.condition;
- Expression then = makeLiteral(entry.then.fileOffset, [entry.then]);
- Expression otherwise = entry.otherwise != null
- ? makeLiteral(entry.otherwise!.fileOffset, [entry.otherwise!])
- : makeLiteral(node.fileOffset, []);
- parts.add(_createConditionalExpression(
- entry.fileOffset, condition, then, otherwise, collectionType));
- } else if (entry is ForMapEntry || entry is ForInMapEntry) {
- // Coverage-ignore-block(suite): Not run.
- // Rejected earlier.
- problems.unhandled("${entry.runtimeType}", "_translateConstMap",
- entry.fileOffset, helper.uri);
} else {
currentPart ??= <MapLiteralEntry>[];
currentPart.add(entry);
@@ -4106,7 +4122,6 @@
return new VariableGet(variable, promotedType)
..fileOffset = variable.fileOffset;
}
- // Coverage-ignore(suite): Not run.
return _createVariableGet(variable);
}
@@ -4757,6 +4772,72 @@
return entry;
}
+ MapLiteralEntry _inferPatternForMapEntry(
+ PatternForMapEntry entry,
+ TreeNode parent,
+ DartType inferredKeyType,
+ DartType inferredValueType,
+ DartType spreadContext,
+ List<DartType> actualTypes,
+ List<DartType> actualTypesForSet,
+ Map<TreeNode, DartType> inferredSpreadTypes,
+ Map<Expression, DartType> inferredConditionTypes,
+ _MapLiteralEntryOffsets offsets) {
+ int? stackBase;
+ assert(checkStackBase(entry, stackBase = stackHeight));
+
+ PatternVariableDeclaration patternVariableDeclaration =
+ entry.patternVariableDeclaration;
+ PatternVariableDeclarationAnalysisResult<DartType, DartType>
+ analysisResult = analyzePatternVariableDeclaration(
+ patternVariableDeclaration,
+ patternVariableDeclaration.pattern,
+ patternVariableDeclaration.initializer,
+ isFinal: patternVariableDeclaration.isFinal);
+ patternVariableDeclaration.matchedValueType =
+ analysisResult.initializerType;
+
+ assert(checkStack(entry, stackBase, [
+ /* pattern = */ ValueKinds.Pattern,
+ /* initializer = */ ValueKinds.Expression,
+ ]));
+
+ Object? rewrite = popRewrite(NullValues.Expression);
+ if (!identical(patternVariableDeclaration.pattern, rewrite)) {
+ // Coverage-ignore-block(suite): Not run.
+ patternVariableDeclaration.pattern = (rewrite as Pattern)
+ ..parent = patternVariableDeclaration;
+ }
+
+ rewrite = popRewrite();
+ if (!identical(patternVariableDeclaration.initializer, rewrite)) {
+ patternVariableDeclaration.initializer = (rewrite as Expression)
+ ..parent = patternVariableDeclaration;
+ }
+
+ List<VariableDeclaration> declaredVariables =
+ patternVariableDeclaration.pattern.declaredVariables;
+ assert(declaredVariables.length == entry.intermediateVariables.length);
+ assert(declaredVariables.length == entry.variables.length);
+ for (int i = 0; i < declaredVariables.length; i++) {
+ DartType type = declaredVariables[i].type;
+ entry.intermediateVariables[i].type = type;
+ entry.variables[i].type = type;
+ }
+
+ return _inferForMapEntryBase(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ }
+
MapLiteralEntry _inferForMapEntry(
ForMapEntry entry,
TreeNode parent,
@@ -4768,49 +4849,30 @@
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes,
_MapLiteralEntryOffsets offsets) {
- if (entry is PatternForMapEntry) {
- int? stackBase;
- assert(checkStackBase(entry, stackBase = stackHeight));
+ return _inferForMapEntryBase(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ }
- PatternVariableDeclaration patternVariableDeclaration =
- entry.patternVariableDeclaration;
- PatternVariableDeclarationAnalysisResult<DartType, DartType>
- analysisResult = analyzePatternVariableDeclaration(
- patternVariableDeclaration,
- patternVariableDeclaration.pattern,
- patternVariableDeclaration.initializer,
- isFinal: patternVariableDeclaration.isFinal);
- patternVariableDeclaration.matchedValueType =
- analysisResult.initializerType;
-
- assert(checkStack(entry, stackBase, [
- /* pattern = */ ValueKinds.Pattern,
- /* initializer = */ ValueKinds.Expression,
- ]));
-
- Object? rewrite = popRewrite(NullValues.Expression);
- if (!identical(patternVariableDeclaration.pattern, rewrite)) {
- // Coverage-ignore-block(suite): Not run.
- patternVariableDeclaration.pattern = (rewrite as Pattern)
- ..parent = patternVariableDeclaration;
- }
-
- rewrite = popRewrite();
- if (!identical(patternVariableDeclaration.initializer, rewrite)) {
- patternVariableDeclaration.initializer = (rewrite as Expression)
- ..parent = patternVariableDeclaration;
- }
-
- List<VariableDeclaration> declaredVariables =
- patternVariableDeclaration.pattern.declaredVariables;
- assert(declaredVariables.length == entry.intermediateVariables.length);
- assert(declaredVariables.length == entry.variables.length);
- for (int i = 0; i < declaredVariables.length; i++) {
- DartType type = declaredVariables[i].type;
- entry.intermediateVariables[i].type = type;
- entry.variables[i].type = type;
- }
- }
+ MapLiteralEntry _inferForMapEntryBase(
+ ForMapEntryBase entry,
+ TreeNode parent,
+ DartType inferredKeyType,
+ DartType inferredValueType,
+ DartType spreadContext,
+ List<DartType> actualTypes,
+ List<DartType> actualTypesForSet,
+ Map<TreeNode, DartType> inferredSpreadTypes,
+ Map<Expression, DartType> inferredConditionTypes,
+ _MapLiteralEntryOffsets offsets) {
// TODO(johnniwinther): Use _visitStatements instead.
List<VariableDeclaration>? variables;
for (int index = 0; index < entry.variables.length; index++) {
@@ -4957,78 +5019,93 @@
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes,
_MapLiteralEntryOffsets offsets) {
- if (entry is SpreadMapEntry) {
- return _inferSpreadMapEntry(
- entry,
- parent,
- inferredKeyType,
- inferredValueType,
- spreadContext,
- actualTypes,
- actualTypesForSet,
- inferredSpreadTypes,
- inferredConditionTypes,
- offsets);
- } else if (entry is NullAwareMapEntry) {
- return _inferNullAwareMapEntry(
- entry,
- parent,
- inferredKeyType,
- inferredValueType,
- spreadContext,
- actualTypes,
- actualTypesForSet,
- inferredSpreadTypes,
- inferredConditionTypes,
- offsets);
- } else if (entry is IfMapEntry) {
- return _inferIfMapEntry(
- entry,
- parent,
- inferredKeyType,
- inferredValueType,
- spreadContext,
- actualTypes,
- actualTypesForSet,
- inferredSpreadTypes,
- inferredConditionTypes,
- offsets);
- } else if (entry is IfCaseMapEntry) {
- return _inferIfCaseMapEntry(
- entry,
- parent,
- inferredKeyType,
- inferredValueType,
- spreadContext,
- actualTypes,
- actualTypesForSet,
- inferredSpreadTypes,
- inferredConditionTypes,
- offsets);
- } else if (entry is ForMapEntry) {
- return _inferForMapEntry(
- entry,
- parent,
- inferredKeyType,
- inferredValueType,
- spreadContext,
- actualTypes,
- actualTypesForSet,
- inferredSpreadTypes,
- inferredConditionTypes,
- offsets);
- } else if (entry is ForInMapEntry) {
- return _inferForInMapEntry(
- entry,
- parent,
- inferredKeyType,
- inferredValueType,
- spreadContext,
- actualTypes,
- actualTypesForSet,
- inferredSpreadTypes,
- inferredConditionTypes,
- offsets);
+ if (entry is ControlFlowMapEntry) {
+ switch (entry) {
+ case SpreadMapEntry():
+ return _inferSpreadMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ case NullAwareMapEntry():
+ return _inferNullAwareMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ case IfMapEntry():
+ return _inferIfMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ case IfCaseMapEntry():
+ return _inferIfCaseMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ case ForMapEntry():
+ return _inferForMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ case PatternForMapEntry():
+ return _inferPatternForMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ case ForInMapEntry():
+ return _inferForInMapEntry(
+ entry,
+ parent,
+ inferredKeyType,
+ inferredValueType,
+ spreadContext,
+ actualTypes,
+ actualTypesForSet,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ }
} else {
ExpressionInferenceResult keyResult =
inferExpression(entry.key, inferredKeyType, isVoidAllowed: true);
@@ -5070,42 +5147,78 @@
new NullLiteral())
..fileOffset = offsets.iterableSpreadOffset!;
}
- if (entry is SpreadMapEntry) {
- DartType? spreadType = inferredSpreadTypes[entry.expression];
- if (spreadType is DynamicType) {
- Expression expression = ensureAssignable(
- coreTypes.mapRawType(entry.isNullAware
- ? Nullability.nullable
- : Nullability.nonNullable),
- spreadType,
- entry.expression);
- entry.expression = expression..parent = entry;
+ if (entry is ControlFlowMapEntry) {
+ switch (entry) {
+ case SpreadMapEntry():
+ DartType? spreadType = inferredSpreadTypes[entry.expression];
+ if (spreadType is DynamicType) {
+ Expression expression = ensureAssignable(
+ coreTypes.mapRawType(entry.isNullAware
+ ? Nullability.nullable
+ : Nullability.nonNullable),
+ spreadType,
+ entry.expression);
+ entry.expression = expression..parent = entry;
+ }
+ case IfMapEntry():
+ MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
+ inferredSpreadTypes, inferredConditionTypes, offsets);
+ entry.then = then..parent = entry;
+ if (entry.otherwise != null) {
+ MapLiteralEntry otherwise = checkMapEntry(
+ entry.otherwise!,
+ keyType,
+ valueType,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ entry.otherwise = otherwise..parent = entry;
+ }
+ case ForMapEntry():
+ if (entry.condition != null) {
+ DartType conditionType = inferredConditionTypes[entry.condition]!;
+ Expression condition = ensureAssignable(
+ coreTypes.boolRawType(Nullability.nonNullable),
+ conditionType,
+ entry.condition!);
+ entry.condition = condition..parent = entry;
+ }
+ MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
+ inferredSpreadTypes, inferredConditionTypes, offsets);
+ entry.body = body..parent = entry;
+ case PatternForMapEntry():
+ if (entry.condition != null) {
+ DartType conditionType = inferredConditionTypes[entry.condition]!;
+ Expression condition = ensureAssignable(
+ coreTypes.boolRawType(Nullability.nonNullable),
+ conditionType,
+ entry.condition!);
+ entry.condition = condition..parent = entry;
+ }
+ MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
+ inferredSpreadTypes, inferredConditionTypes, offsets);
+ entry.body = body..parent = entry;
+ case ForInMapEntry():
+ MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
+ inferredSpreadTypes, inferredConditionTypes, offsets);
+ entry.body = body..parent = entry;
+ case IfCaseMapEntry():
+ MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
+ inferredSpreadTypes, inferredConditionTypes, offsets);
+ entry.then = then..parent = entry;
+ if (entry.otherwise != null) {
+ MapLiteralEntry otherwise = checkMapEntry(
+ entry.otherwise!,
+ keyType,
+ valueType,
+ inferredSpreadTypes,
+ inferredConditionTypes,
+ offsets);
+ entry.otherwise = otherwise..parent = entry;
+ }
+ case NullAwareMapEntry():
+ // Do nothing. Assignability checks are done during type inference.
}
- } else if (entry is IfMapEntry) {
- MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
- inferredSpreadTypes, inferredConditionTypes, offsets);
- entry.then = then..parent = entry;
- if (entry.otherwise != null) {
- MapLiteralEntry otherwise = checkMapEntry(entry.otherwise!, keyType,
- valueType, inferredSpreadTypes, inferredConditionTypes, offsets);
- entry.otherwise = otherwise..parent = entry;
- }
- } else if (entry is ForMapEntry) {
- if (entry.condition != null) {
- DartType conditionType = inferredConditionTypes[entry.condition]!;
- Expression condition = ensureAssignable(
- coreTypes.boolRawType(Nullability.nonNullable),
- conditionType,
- entry.condition!);
- entry.condition = condition..parent = entry;
- }
- MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
- inferredSpreadTypes, inferredConditionTypes, offsets);
- entry.body = body..parent = entry;
- } else if (entry is ForInMapEntry) {
- MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
- inferredSpreadTypes, inferredConditionTypes, offsets);
- entry.body = body..parent = entry;
} else {
// Do nothing. Assignability checks are done during type inference.
}
diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
index a76a137..de33a56 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
@@ -3448,6 +3448,7 @@
.withDeclaredNullability(Nullability.nullable)
], functionType.returnType, functionType.declaredNullability));
}
+ // Coverage-ignore(suite): Not run.
case InvocationTargetDynamicType():
case InvocationTargetNeverType():
case InvocationTargetInvalidType():
diff --git a/pkg/front_end/lib/src/type_inference/object_access_target.dart b/pkg/front_end/lib/src/type_inference/object_access_target.dart
index fceb24b..9c2edde 100644
--- a/pkg/front_end/lib/src/type_inference/object_access_target.dart
+++ b/pkg/front_end/lib/src/type_inference/object_access_target.dart
@@ -197,52 +197,62 @@
const InvocationTargetNonFunctionType();
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get equalsFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get sublistFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get minusFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get indexGetFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get greaterThanOrEqualsFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get lessThanOrEqualsFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get relationalFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get containsKeyFunctionType {
return _oneParameterFunctionApproximation;
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get indexSetFunctionType {
return new FunctionType(
[const DynamicType()], returnType, Nullability.nonNullable);
}
@override
+ // Coverage-ignore(suite): Not run.
FunctionType get binaryOperationFunctionType {
return _oneParameterFunctionApproximation;
}
@@ -254,6 +264,7 @@
return FunctionAccessKind.Function;
}
+ // Coverage-ignore(suite): Not run.
FunctionType get _oneParameterFunctionApproximation {
return new FunctionType(
[const DynamicType()], returnType, Nullability.nonNullable);
@@ -540,6 +551,7 @@
calleeType is DynamicType) {
return const InvocationTargetDynamicType();
} else if (calleeType is NeverType) {
+ // Coverage-ignore-block(suite): Not run.
// TODO(cstefantsova): Should we insert the nullability assert somewhere
// earlier?
assert(calleeType.nullability == Nullability.nonNullable);
@@ -769,6 +781,7 @@
} else {
return const DynamicType();
}
+ // Coverage-ignore(suite): Not run.
case InvocationTargetDynamicType():
case InvocationTargetInvalidType():
case InvocationTargetNeverType():
@@ -790,6 +803,7 @@
} else {
return const DynamicType();
}
+ // Coverage-ignore(suite): Not run.
case InvocationTargetDynamicType():
case InvocationTargetInvalidType():
case InvocationTargetNeverType():
@@ -816,6 +830,7 @@
} else {
return const DynamicType();
}
+ // Coverage-ignore(suite): Not run.
case InvocationTargetDynamicType():
case InvocationTargetInvalidType():
case InvocationTargetNeverType():
diff --git a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
index 7141a8a..17420c0 100644
--- a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
@@ -985,6 +985,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
NullabilitySuffix typeSchemaNullabilitySuffix(DartType typeSchema) {
return typeSchema.nullabilitySuffix;
}
diff --git a/pkg/front_end/lib/src/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
index 8decdbf..207282b 100644
--- a/pkg/front_end/lib/src/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
@@ -218,7 +218,6 @@
List<NamedExpression> namedArguments = <NamedExpression>[];
for (VariableDeclaration parameter
in redirectingFactoryFunction.namedParameters) {
- // Coverage-ignore-block(suite): Not run.
flowAnalysis.declare(parameter, parameter.type, initialized: true);
namedArguments.add(new NamedExpression(parameter.name!,
new VariableGetImpl(parameter, forNullGuardedAccess: false)));
diff --git a/pkg/front_end/test/coverage_suite.dart b/pkg/front_end/test/coverage_suite.dart
index efdc435..fe0f726 100644
--- a/pkg/front_end/test/coverage_suite.dart
+++ b/pkg/front_end/test/coverage_suite.dart
@@ -64,12 +64,13 @@
print("Run finished.");
Map<Uri, coverageMerger.CoverageInfo>? coverageData =
- coverageMerger.mergeFromDirUri(
+ await coverageMerger.mergeFromDirUri(
Uri.base.resolve(".dart_tool/package_config.json"),
coverageTmpDir.uri,
silent: true,
extraCoverageIgnores: ["coverage-ignore(suite):"],
extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
+ addAndRemoveCommentsInFiles: options.addAndRemoveCommentsInFiles,
);
if (coverageData == null) throw "Failure in coverage.";
@@ -167,7 +168,9 @@
sb.write("\n\n Or update the expectations directly via");
sb.write(
"\n\n dart$extraFlags pkg/front_end/test/coverage_suite.dart "
- "--tasks=${options.numberOfWorkers} --updateExpectations");
+ "--tasks=${options.numberOfWorkers} "
+ "--add-and-remove-comments "
+ "--update-expectations");
}
}
addResult(coverageEntry.key.toString(), pass,
@@ -229,6 +232,7 @@
final bool verbose;
final bool debug;
final bool updateExpectations;
+ final bool addAndRemoveCommentsInFiles;
final Uri outputDirectory;
final int numberOfWorkers;
@@ -237,6 +241,7 @@
this.verbose,
this.debug,
this.updateExpectations,
+ this.addAndRemoveCommentsInFiles,
this.outputDirectory, {
required this.numberOfWorkers,
});
@@ -255,8 +260,12 @@
abbr: "j",
help: "The number of parallel tasks to run.",
defaultsTo: "${getDefaultThreads()}")
- ..addFlag("updateExpectations",
+ ..addFlag("update-expectations",
help: "update expectations", defaultsTo: false)
+ ..addFlag("add-and-remove-comments",
+ help: "Automatically remove old and then "
+ "re-add ignore comments in files",
+ defaultsTo: false)
// These are not used but are here for compatibility with the test system.
..addOption("shards", help: "(Ignored) Number of shards", defaultsTo: "1")
..addOption("shard",
@@ -273,7 +282,8 @@
if (tasks == null || tasks < 1) {
throw "--tasks (-j) has to be an integer >= 1";
}
- bool updateExpectations = parsedOptions["updateExpectations"];
+ bool updateExpectations = parsedOptions["update-expectations"];
+ bool addAndRemoveCommentsInFiles = parsedOptions["add-and-remove-comments"];
if (verbose) {
print("NOTE: Created with options\n "
@@ -282,7 +292,8 @@
"debug = ${debug},\n "
"${outputDirectory},\n "
"numberOfWorkers: ${tasks},\n "
- "updateExpectations = ${updateExpectations}");
+ "updateExpectations = ${updateExpectations}\n "
+ "addAndRemoveCommentsInFiles = ${addAndRemoveCommentsInFiles}");
}
return Options(
@@ -290,6 +301,7 @@
verbose,
debug,
updateExpectations,
+ addAndRemoveCommentsInFiles,
outputDirectory,
numberOfWorkers: tasks,
);
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index 9e7a7a3..667bdc0 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -168,10 +168,10 @@
hitCount: 25,
missCount: 0,
),
- // 86.76470588235294%.
+ // 100.0%.
"package:front_end/src/base/local_scope.dart": (
hitCount: 59,
- missCount: 9,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/base/messages.dart": (
@@ -183,10 +183,10 @@
hitCount: 29,
missCount: 0,
),
- // 97.87234042553192%.
+ // 100.0%.
"package:front_end/src/base/name_space.dart": (
hitCount: 138,
- missCount: 3,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/base/operator.dart": (
@@ -203,10 +203,10 @@
hitCount: 246,
missCount: 0,
),
- // 97.94117647058823%.
+ // 100.0%.
"package:front_end/src/base/scope.dart": (
hitCount: 666,
- missCount: 14,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/base/ticker.dart": (
@@ -418,25 +418,25 @@
hitCount: 16,
missCount: 0,
),
- // 98.85714285714286%.
+ // 100.0%.
"package:front_end/src/dill/dill_class_builder.dart": (
hitCount: 173,
- missCount: 2,
+ missCount: 0,
),
- // 97.5%.
+ // 100.0%.
"package:front_end/src/dill/dill_extension_builder.dart": (
hitCount: 78,
- missCount: 2,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/dill/dill_extension_member_builder.dart": (
hitCount: 71,
missCount: 0,
),
- // 98.68421052631578%.
+ // 100.0%.
"package:front_end/src/dill/dill_extension_type_declaration_builder.dart": (
hitCount: 150,
- missCount: 2,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/dill/dill_extension_type_member_builder.dart": (
@@ -478,20 +478,20 @@
hitCount: 0,
missCount: 0,
),
- // 99.14393499709809%.
+ // 100.0%.
"package:front_end/src/kernel/body_builder.dart": (
- hitCount: 6833,
- missCount: 59,
+ hitCount: 6831,
+ missCount: 0,
),
- // 98.26086956521739%.
+ // 100.0%.
"package:front_end/src/kernel/body_builder_context.dart": (
hitCount: 339,
- missCount: 6,
+ missCount: 0,
),
- // 81.57894736842105%.
+ // 100.0%.
"package:front_end/src/kernel/collections.dart": (
- hitCount: 310,
- missCount: 70,
+ hitCount: 326,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/combined_member_signature.dart": (
@@ -598,30 +598,30 @@
hitCount: 1,
missCount: 0,
),
- // 99.12126537785588%.
+ // 100.0%.
"package:front_end/src/kernel/internal_ast.dart": (
- hitCount: 564,
- missCount: 5,
+ hitCount: 554,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/invalid_type.dart": (
hitCount: 43,
missCount: 0,
),
- // 92.85714285714286%.
+ // 100.0%.
"package:front_end/src/kernel/kernel_constants.dart": (
hitCount: 13,
- missCount: 1,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/kernel_helper.dart": (
hitCount: 285,
missCount: 0,
),
- // 99.90566037735849%.
+ // 100.0%.
"package:front_end/src/kernel/kernel_target.dart": (
hitCount: 1059,
- missCount: 1,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/kernel_variable_builder.dart": (
@@ -683,10 +683,10 @@
hitCount: 16,
missCount: 0,
),
- // 88.88888888888889%.
+ // 100.0%.
"package:front_end/src/kernel/try_constant_evaluator.dart": (
hitCount: 16,
- missCount: 2,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/type_algorithms.dart": (
@@ -708,10 +708,10 @@
hitCount: 21,
missCount: 0,
),
- // 60.0%.
+ // 100.0%.
"package:front_end/src/kernel/wildcard_lowering.dart": (
hitCount: 9,
- missCount: 6,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel_generator_impl.dart": (
@@ -723,10 +723,10 @@
hitCount: 0,
missCount: 0,
),
- // 0.0%.
+ // 100.0%.
"package:front_end/src/macros/macro_injected_impl.dart": (
hitCount: 0,
- missCount: 1,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/macros/macro_serializer.dart": (
@@ -793,30 +793,30 @@
hitCount: 1167,
missCount: 0,
),
- // 98.13664596273291%.
+ // 100.0%.
"package:front_end/src/source/source_builder_mixins.dart": (
hitCount: 158,
- missCount: 3,
+ missCount: 0,
),
- // 99.7584541062802%.
+ // 100.0%.
"package:front_end/src/source/source_class_builder.dart": (
hitCount: 1239,
- missCount: 3,
+ missCount: 0,
),
- // 99.46308724832214%.
+ // 100.0%.
"package:front_end/src/source/source_compilation_unit.dart": (
hitCount: 741,
- missCount: 4,
+ missCount: 0,
),
- // 98.31649831649831%.
+ // 100.0%.
"package:front_end/src/source/source_constructor_builder.dart": (
hitCount: 876,
- missCount: 15,
+ missCount: 0,
),
- // 99.81024667931689%.
+ // 100.0%.
"package:front_end/src/source/source_enum_builder.dart": (
hitCount: 526,
- missCount: 1,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/source/source_extension_builder.dart": (
@@ -844,15 +844,15 @@
hitCount: 302,
missCount: 0,
),
- // 99.85925404644617%.
+ // 100.0%.
"package:front_end/src/source/source_library_builder.dart": (
hitCount: 1419,
- missCount: 2,
+ missCount: 0,
),
- // 99.89253089736701%.
+ // 100.0%.
"package:front_end/src/source/source_loader.dart": (
hitCount: 1859,
- missCount: 2,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/source/source_member_builder.dart": (
@@ -864,20 +864,20 @@
hitCount: 518,
missCount: 0,
),
- // 99.10714285714286%.
+ // 100.0%.
"package:front_end/src/source/source_type_alias_builder.dart": (
hitCount: 333,
- missCount: 3,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/source/stack_listener_impl.dart": (
hitCount: 20,
missCount: 0,
),
- // 99.00497512437812%.
+ // 100.0%.
"package:front_end/src/source/type_parameter_scope_builder.dart": (
hitCount: 199,
- missCount: 2,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/type_inference/closure_context.dart": (
@@ -909,15 +909,15 @@
hitCount: 166,
missCount: 0,
),
- // 99.51564828614009%.
+ // 100.0%.
"package:front_end/src/type_inference/inference_visitor.dart": (
- hitCount: 8013,
- missCount: 39,
+ hitCount: 8044,
+ missCount: 0,
),
- // 99.875%.
+ // 100.0%.
"package:front_end/src/type_inference/inference_visitor_base.dart": (
hitCount: 2397,
- missCount: 3,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/type_inference/matching_cache.dart": (
@@ -929,10 +929,10 @@
hitCount: 519,
missCount: 0,
),
- // 92.11409395973155%.
+ // 100.0%.
"package:front_end/src/type_inference/object_access_target.dart": (
hitCount: 549,
- missCount: 47,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/type_inference/shared_type_analyzer.dart": (
@@ -954,10 +954,10 @@
hitCount: 19,
missCount: 0,
),
- // 99.5959595959596%.
+ // 100.0%.
"package:front_end/src/type_inference/type_inference_engine.dart": (
hitCount: 493,
- missCount: 2,
+ missCount: 0,
),
// 100.0%.
"package:front_end/src/type_inference/type_inferrer.dart": (
diff --git a/pkg/front_end/test/dartdoc_test_test.dart b/pkg/front_end/test/dartdoc_test_test.dart
index 978514b..339898e 100644
--- a/pkg/front_end/test/dartdoc_test_test.dart
+++ b/pkg/front_end/test/dartdoc_test_test.dart
@@ -46,7 +46,7 @@
memoryFileSystem.entityForUri(test1).writeAsStringSync(test);
expect(await dartDocTest.process(test1), expected);
-// Mixed good/bad.
+ // Mixed good/bad.
Uri test2 = new Uri(scheme: "darttest", path: "/test2.dart");
test = """
// DartDocTest(1+1, 3)
@@ -72,7 +72,7 @@
memoryFileSystem.entityForUri(test2).writeAsStringSync(test);
expect(await dartDocTest.process(test2), expected);
-// Good case using await.
+ // Good case using await.
Uri test3 = new Uri(scheme: "darttest", path: "/test3.dart");
test = """
// DartDocTest(await _internal(), 42)
@@ -120,7 +120,7 @@
return;
}
""";
- tests = extractTests(test);
+ tests = extractTests(test, test5);
expect(tests.length, 2);
expected = [
new impl.TestResult(null, impl.TestOutcome.CompilationError)
@@ -145,7 +145,7 @@
return "hello";
}
""";
- tests = extractTests(test);
+ tests = extractTests(test, test6);
expect(tests.length, 2);
expected = [
new impl.TestResult(tests[0], impl.TestOutcome.Crash)
@@ -183,8 +183,12 @@
expect(extractTests(""), <impl.Test>[]);
// One test.
- expect(extractTests("// DartDocTest(1+1, 2)"), <impl.Test>[
- new impl.ExpectTest("1+1", "2"),
+ expect(extractTests("""
+ // not a test comment
+ void foo_bar_long_name() {}
+
+ // DartDocTest(1+1, 2)"""), <impl.Test>[
+ new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:4:20"),
]);
// Two tests.
@@ -192,8 +196,8 @@
// DartDocTest(1+1, 2)
// DartDocTest(2+40, 42)
"""), <impl.Test>[
- new impl.ExpectTest("1+1", "2"),
- new impl.ExpectTest("2+40", "42"),
+ new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:1:16"),
+ new impl.ExpectTest("2+40", "42", "darttest:/foo.dart:2:16"),
]);
// Two valid tests. Four invalid ones.
@@ -205,26 +209,48 @@
// DartDocTest(2+40, 42)
// DartDocTest(2+40, 42+)
"""), <impl.Test>[
- new impl.ExpectTest("1+1", "2"),
+ new impl.ExpectTest(
+ "1+1",
+ "2",
+ "darttest:/foo.dart:1:16",
+ ),
new impl.TestParseError(
- """darttest:/foo.dart:2:20: Expected ',' before this.
+ """darttest:/foo.dart:2:20: Expected ',' before this.
// DartDocTest(2+40; 42]
- ^""", 42),
- new impl.TestParseError("""Parse error(s):
+ ^""",
+ 42,
+ "darttest:/foo.dart:2:20",
+ ),
+ new impl.TestParseError(
+ """Parse error(s):
darttest:/foo.dart:3:21: Expected an identifier, but got ','.
// DartDocTest(2+40+, 42]
- ^""", 68),
+ ^""",
+ 68,
+ "darttest:/foo.dart:3:21",
+ ),
new impl.TestParseError(
- """darttest:/foo.dart:4:24: Expected ')' before this.
+ """darttest:/foo.dart:4:24: Expected ')' before this.
// DartDocTest(2+40, 42]
- ^""", 97),
- new impl.ExpectTest("2+40", "42"),
- new impl.TestParseError("""Parse error(s):
+ ^""",
+ 97,
+ "darttest:/foo.dart:4:24",
+ ),
+ new impl.ExpectTest(
+ "2+40",
+ "42",
+ "darttest:/foo.dart:5:16",
+ ),
+ new impl.TestParseError(
+ """Parse error(s):
darttest:/foo.dart:6:25: Expected an identifier, but got ')'.
// DartDocTest(2+40, 42+)
- ^""", 148),
+ ^""",
+ 148,
+ "darttest:/foo.dart:6:25",
+ ),
]);
// Two tests in block comments with back-ticks around tests.
@@ -240,8 +266,8 @@
* ```
*/
"""), <impl.Test>[
- new impl.ExpectTest("1+1", "2"),
- new impl.ExpectTest("2+40", "42"),
+ new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:4:16"),
+ new impl.ExpectTest("2+40", "42", "darttest:/foo.dart:8:16"),
]);
// Two tests --- include linebreaks.
@@ -256,9 +282,9 @@
42)
*/
"""), <impl.Test>[
- new impl.ExpectTest("1+1", "2"),
+ new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:3:15"),
// The linebreak etc here is not stripped (at the moment at least)
- new impl.ExpectTest("2+\n 40", "42"),
+ new impl.ExpectTest("2+\n 40", "42", "darttest:/foo.dart:6:15"),
]);
// Two tests --- with parens and commas as string...
@@ -267,15 +293,15 @@
// and so is this:
// DartDocTest(",)", ",)")
"""), <impl.Test>[
- new impl.ExpectTest('"("', '"("'),
- new impl.ExpectTest('",)"', '",)"'),
+ new impl.ExpectTest('"("', '"("', "darttest:/foo.dart:1:16"),
+ new impl.ExpectTest('",)"', '",)"', "darttest:/foo.dart:3:16"),
]);
// Await expression.
expect(extractTests("""
// DartDocTest(await foo(), 42)
"""), <impl.Test>[
- new impl.ExpectTest('await foo()', '42'),
+ new impl.ExpectTest('await foo()', '42', "darttest:/foo.dart:1:16"),
]);
}
diff --git a/pkg/front_end/test/run_all_coverage.dart b/pkg/front_end/test/run_all_coverage.dart
index 5dea1b2..8748b13 100644
--- a/pkg/front_end/test/run_all_coverage.dart
+++ b/pkg/front_end/test/run_all_coverage.dart
@@ -112,7 +112,7 @@
// force compile everything and that the remaining stuff is (mostly) mixins
// and const classes etc that shouldn't (necessarily) be compiled but is
// potentially covered in other ways.
- coverageMerger.mergeFromDirUri(
+ await coverageMerger.mergeFromDirUri(
repoDirUri.resolve(".dart_tool/package_config.json"),
coverageTmpDir.uri,
silent: false,
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 23cfea6..cc95db5 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -868,6 +868,7 @@
verbatim
versioned
vf
+virtually
visualization
vp
vt
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart
new file mode 100644
index 0000000..fd4e966
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2024, 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.
+
+test(dynamic x, dynamic list) {
+ return {1: 1, if (list case [int _]) ...x else ...x};
+}
+
+main() {
+ test({0: 0}, [0]);
+ expectThrows<TypeError>(() {test(null, [0]);});
+}
+
+expectThrows<Exception>(void Function() f) {
+ String? message;
+ try {
+ f();
+ message = "Expected the function to throw an exception, but it didn't.";
+ } on Exception catch (_) {
+ // Ok.
+ } on dynamic catch (e) {
+ message = "Expected the function to throw an exception of type '${Exception}', but got '${e.runtimeType}'.";
+ }
+ if (message != null) {
+ throw message;
+ }
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.expect
new file mode 100644
index 0000000..a4d3c35
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic {
+ return block {
+ final core::Map<dynamic, dynamic> #t1 = <dynamic, dynamic>{};
+ #t1.{core::Map::[]=}{Invariant}(1, 1){(dynamic, dynamic) → void};
+ {
+ final synthesized dynamic #0#0 = list;
+ if(#0#0 is core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic} is core::int)
+ #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+ else
+ #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+ }
+ } =>#t1;
+}
+static method main() → dynamic {
+ self::test(<core::int, core::int>{0: 0}, <core::int>[0]);
+ self::expectThrows<core::TypeError>(() → void {
+ self::test(null, <core::int>[0]);
+ });
+}
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic {
+ core::String? message;
+ try {
+ f(){() → void};
+ message = "Expected the function to throw an exception, but it didn't.";
+ }
+ on self::expectThrows::Exception% catch(final self::expectThrows::Exception% _) {
+ }
+ on dynamic catch(final dynamic e) {
+ message = "Expected the function to throw an exception of type '${self::expectThrows::Exception%}', but got '${e.{core::Object::runtimeType}{<object>}.{core::Type}}'.";
+ }
+ if(!(message == null)) {
+ throw message{core::String};
+ }
+}
+
+constants {
+ #C1 = 1
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.modular.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.modular.expect
new file mode 100644
index 0000000..a4d3c35
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.modular.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic {
+ return block {
+ final core::Map<dynamic, dynamic> #t1 = <dynamic, dynamic>{};
+ #t1.{core::Map::[]=}{Invariant}(1, 1){(dynamic, dynamic) → void};
+ {
+ final synthesized dynamic #0#0 = list;
+ if(#0#0 is core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic} is core::int)
+ #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+ else
+ #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+ }
+ } =>#t1;
+}
+static method main() → dynamic {
+ self::test(<core::int, core::int>{0: 0}, <core::int>[0]);
+ self::expectThrows<core::TypeError>(() → void {
+ self::test(null, <core::int>[0]);
+ });
+}
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic {
+ core::String? message;
+ try {
+ f(){() → void};
+ message = "Expected the function to throw an exception, but it didn't.";
+ }
+ on self::expectThrows::Exception% catch(final self::expectThrows::Exception% _) {
+ }
+ on dynamic catch(final dynamic e) {
+ message = "Expected the function to throw an exception of type '${self::expectThrows::Exception%}', but got '${e.{core::Object::runtimeType}{<object>}.{core::Type}}'.";
+ }
+ if(!(message == null)) {
+ throw message{core::String};
+ }
+}
+
+constants {
+ #C1 = 1
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.outline.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.outline.expect
new file mode 100644
index 0000000..47a0d8c
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.outline.expect
@@ -0,0 +1,10 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic
+ ;
+static method main() → dynamic
+ ;
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic
+ ;
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.transformed.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.transformed.expect
new file mode 100644
index 0000000..6a23afa
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.transformed.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic {
+ return block {
+ final core::Map<dynamic, dynamic> #t1 = <dynamic, dynamic>{};
+ #t1.{core::Map::[]=}{Invariant}(1, 1){(dynamic, dynamic) → void};
+ {
+ final synthesized dynamic #0#0 = list;
+ if(#0#0 is core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic} is core::int)
+ #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+ else
+ #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+ }
+ } =>#t1;
+}
+static method main() → dynamic {
+ self::test(<core::int, core::int>{0: 0}, core::_GrowableList::_literal1<core::int>(0));
+ self::expectThrows<core::TypeError>(() → void {
+ self::test(null, core::_GrowableList::_literal1<core::int>(0));
+ });
+}
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic {
+ core::String? message;
+ try {
+ f(){() → void};
+ message = "Expected the function to throw an exception, but it didn't.";
+ }
+ on self::expectThrows::Exception% catch(final self::expectThrows::Exception% _) {
+ }
+ on dynamic catch(final dynamic e) {
+ message = "Expected the function to throw an exception of type '${self::expectThrows::Exception%}', but got '${e.{core::Object::runtimeType}{<object>}.{core::Type}}'.";
+ }
+ if(!(message == null)) {
+ throw message{core::String};
+ }
+}
+
+constants {
+ #C1 = 1
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline.expect
new file mode 100644
index 0000000..970a117
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+test(dynamic x, dynamic list) {}
+
+main() {}
+
+expectThrows<Exception>(void Function() f) {}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..97f6f2f
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+expectThrows<Exception>(void Function() f) {}
+
+main() {}
+
+test(dynamic x, dynamic list) {}
diff --git a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect
index 26ef4c1..d0edd96 100644
--- a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect
+++ b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect
@@ -13,7 +13,7 @@
hoisted core::String y;
if(#0#0 is core::String) {
y = #0#0{core::String};
- #t1.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t1.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
@@ -30,7 +30,7 @@
hoisted core::String y;
if(#0#0 is core::String) {
y = #0#0{core::String};
- #t2.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t2.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
@@ -55,7 +55,7 @@
hoisted core::int y2;
if(#1#0 is core::int) {
y2 = #1#0{core::int};
- #t3.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t3.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
diff --git a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect
index 26ef4c1..d0edd96 100644
--- a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect
+++ b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect
@@ -13,7 +13,7 @@
hoisted core::String y;
if(#0#0 is core::String) {
y = #0#0{core::String};
- #t1.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t1.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
@@ -30,7 +30,7 @@
hoisted core::String y;
if(#0#0 is core::String) {
y = #0#0{core::String};
- #t2.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t2.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
@@ -55,7 +55,7 @@
hoisted core::int y2;
if(#1#0 is core::int) {
y2 = #1#0{core::int};
- #t3.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t3.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
diff --git a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect
index 46341de..bdab0f0 100644
--- a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect
@@ -13,7 +13,7 @@
hoisted core::String y;
if(#0#0 is core::String) {
y = #0#0{core::String};
- #t1.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t1.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
@@ -30,7 +30,7 @@
hoisted core::String y;
if(#0#0 is core::String) {
y = #0#0{core::String};
- #t2.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t2.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
@@ -55,7 +55,7 @@
hoisted core::int y2;
if(#1#0 is core::int) {
y2 = #1#0{core::int};
- #t3.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+ #t3.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
}
}
}
diff --git a/pkg/front_end/tool/coverage_merger.dart b/pkg/front_end/tool/coverage_merger.dart
index 57544ae..d4d390a 100644
--- a/pkg/front_end/tool/coverage_merger.dart
+++ b/pkg/front_end/tool/coverage_merger.dart
@@ -2,8 +2,9 @@
// 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:convert' show LineSplitter, utf8;
import 'dart:io';
-import 'dart:typed_data';
+import 'dart:typed_data' show Uint8List;
import 'package:_fe_analyzer_shared/src/scanner/characters.dart'
show $SPACE, $CARET, $LF, $CR;
@@ -23,12 +24,14 @@
Uri? packagesUri;
bool addCommentsToFiles = false;
bool removeCommentsFromFiles = false;
+ bool addAndRemoveCommentsInFiles = false;
for (String argument in arguments) {
const String coverage = "--coverage=";
const String packages = "--packages=";
const String comment = "--comment";
const String removeComments = "--remove-comments";
+ const String addAndRemoveComments = "--add-and-remove-comments";
if (argument.startsWith(coverage)) {
coverageUri =
Uri.base.resolveUri(Uri.file(argument.substring(coverage.length)));
@@ -39,6 +42,8 @@
addCommentsToFiles = true;
} else if (argument == removeComments) {
removeCommentsFromFiles = true;
+ } else if (argument == addAndRemoveComments) {
+ addAndRemoveCommentsInFiles = true;
} else {
throw "Unsupported argument: $argument";
}
@@ -59,11 +64,12 @@
extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
addCommentsToFiles: addCommentsToFiles,
removeCommentsFromFiles: removeCommentsFromFiles,
+ addAndRemoveCommentsInFiles: addAndRemoveCommentsInFiles,
);
print("Done in ${stopwatch.elapsed}");
}
-Map<Uri, CoverageInfo>? mergeFromDirUri(
+Future<Map<Uri, CoverageInfo>?> mergeFromDirUri(
Uri packagesUri,
Uri coverageUri, {
required bool silent,
@@ -71,7 +77,8 @@
required List<String> extraCoverageBlockIgnores,
bool addCommentsToFiles = false,
bool removeCommentsFromFiles = false,
-}) {
+ bool addAndRemoveCommentsInFiles = false,
+}) async {
void output(Object? object) {
if (silent) return;
print(object);
@@ -134,6 +141,7 @@
Map<Uri, CoverageInfo> result = {};
+ List<Uri> needsFormatting = [];
for (Uri uri in knownUris.toList()
..sort(((a, b) => a.toString().compareTo(b.toString())))) {
// Don't care about coverage for testing stuff.
@@ -151,7 +159,7 @@
List<int> hitsSorted =
hit == null ? const [] : (hit._data.keys.toList()..sort());
- CoverageInfo processInfo = process(
+ CoverageInfo processInfo = _process(
packageConfig,
uri,
miss ?? const {},
@@ -160,6 +168,8 @@
extraCoverageBlockIgnores,
addCommentsToFiles: addCommentsToFiles,
removeCommentsFromFiles: removeCommentsFromFiles,
+ addAndRemoveCommentsInFiles: addAndRemoveCommentsInFiles,
+ needsFormatting: needsFormatting,
);
if (processInfo.visualization.trim().isNotEmpty) {
output(processInfo.visualization);
@@ -178,6 +188,26 @@
}
}
+ if (needsFormatting.isNotEmpty) {
+ Process p = await Process.start(Platform.resolvedExecutable, [
+ "format",
+ ...needsFormatting.map((uri) => new File.fromUri(uri).path)
+ ]);
+
+ p.stderr
+ .transform(utf8.decoder)
+ .transform(const LineSplitter())
+ .listen((String line) {
+ stderr.writeln("stderr> $line");
+ });
+ p.stdout
+ .transform(utf8.decoder)
+ .transform(const LineSplitter())
+ .listen((String line) {
+ stdout.writeln("stdout> $line");
+ });
+ }
+
output("Processed $filesCount files with $errorsCount error(s) and "
"$allCoveredCount files being covered 100%.");
int percentHit = (hitsTotal * 100) ~/ (hitsTotal + missesTotal);
@@ -207,7 +237,7 @@
: error = false;
}
-CoverageInfo process(
+CoverageInfo _process(
PackageConfig packageConfig,
Uri uri,
Set<int> untrimmedMisses,
@@ -216,6 +246,8 @@
List<String> extraCoverageBlockIgnores, {
bool addCommentsToFiles = false,
bool removeCommentsFromFiles = false,
+ bool addAndRemoveCommentsInFiles = false,
+ List<Uri>? needsFormatting,
}) {
Uri? fileUri = packageConfig.resolve(uri);
if (fileUri == null) {
@@ -248,40 +280,15 @@
CompilationUnitBegin unitBegin =
ast.children!.first as CompilationUnitBegin;
Token? token = unitBegin.token;
- List<Token> removeComments = [];
- while (token != null && !token.isEof) {
- Token? comment = token.precedingComments;
- while (comment != null) {
- String message = comment.lexeme.trim().toLowerCase();
- while (message.startsWith("//") || message.startsWith("/*")) {
- message = message.substring(2).trim();
- }
- for (String coverageIgnoreString in const [
- "coverage-ignore(suite): not run.",
- "coverage-ignore-block(suite): not run.",
- ]) {
- if (message.startsWith(coverageIgnoreString)) {
- removeComments.add(comment);
- }
- }
- comment = comment.next;
- }
- token = token.next;
- }
+ List<Token> removeComments =
+ _findTokensWithCoverageIgnoreSuiteComments(token);
String sourceText = source.text;
StringBuffer sb = new StringBuffer();
int from = 0;
for (Token token in removeComments) {
String substring = sourceText.substring(from, token.charOffset);
sb.write(substring);
- from = token.charEnd;
- // Remove whitespace after too.
- while (sourceText.length > from &&
- (sourceText.codeUnitAt(from) == $SPACE ||
- sourceText.codeUnitAt(from) == $LF ||
- sourceText.codeUnitAt(from) == $CR)) {
- from++;
- }
+ from = _includeWhitespaceAfter(sourceText, token.charEnd);
}
sb.write(sourceText.substring(from));
f.writeAsStringSync(sb.toString());
@@ -293,15 +300,36 @@
}
List<int> allSorted = [...hitsSorted, ...untrimmedMisses]..sort();
- AstIndexerAndIgnoreCollector astIndexer =
- AstIndexerAndIgnoreCollector.collect(
- ast, extraCoverageIgnores, extraCoverageBlockIgnores,
- hitsSorted: hitsSorted, allSorted: allSorted);
+ AstIndexerAndIgnoreCollector astIndexer;
+
+ if (addAndRemoveCommentsInFiles) {
+ // 1) Pretend like the comments that would be removed by the comment remover
+ // aren't there (virtually removing them).
+ // 2) Now add the comments as when adding comments, but also removing the
+ // old comments.
+ // 3) Add the new comments to the ignoredIntervals, allowing the below to
+ // proceed as if was run on the newly commented stuff, thus returning the
+ // (now) correct hit and miss count.
+ astIndexer = AstIndexerAndIgnoreCollector.collect(
+ ast, extraCoverageIgnores, extraCoverageBlockIgnores,
+ ignoresToIgnore: _commentTextToRemove,
+ hitsSorted: hitsSorted,
+ allSorted: allSorted);
+ } else {
+ astIndexer = AstIndexerAndIgnoreCollector.collect(
+ ast, extraCoverageIgnores, extraCoverageBlockIgnores,
+ hitsSorted: hitsSorted, allSorted: allSorted);
+ }
+
+ IntervalListBuilder? ignoredIntervalsBuilder;
+ if (addAndRemoveCommentsInFiles) {
+ ignoredIntervalsBuilder = astIndexer.ignoredStartEnd.clone();
+ }
IntervalList ignoredIntervals =
astIndexer.ignoredStartEnd.buildIntervalList();
- if (addCommentsToFiles) {
+ if (addCommentsToFiles || addAndRemoveCommentsInFiles) {
String sourceText = source.text;
StringBuffer sb = new StringBuffer();
int from = 0;
@@ -354,6 +382,53 @@
processed.add(commentOn);
}
}
+
+ List<int> removeFrom = [];
+ List<int> removeTo = [];
+ if (addAndRemoveCommentsInFiles) {
+ CompilationUnitBegin unitBegin =
+ ast.children!.first as CompilationUnitBegin;
+ Token? token = unitBegin.token;
+ List<Token> removeComments =
+ _findTokensWithCoverageIgnoreSuiteComments(token);
+ for (Token token in removeComments) {
+ if (removeTo.isNotEmpty && removeTo.last == token.charOffset) {
+ // Not sure if this can happen, but if it does we extend the previous
+ // added to (i.e. end of what has to be removed).
+ removeTo[removeTo.length - 1] =
+ _includeWhitespaceAfter(sourceText, token.charEnd);
+ } else {
+ removeFrom.add(token.charOffset);
+ removeTo.add(_includeWhitespaceAfter(sourceText, token.charEnd));
+ }
+ }
+ }
+
+ void addChunk(int from, final int to) {
+ if (addAndRemoveCommentsInFiles && removeFrom.isNotEmpty) {
+ int fromIndex = binarySearch(removeFrom, from);
+ if (removeFrom[fromIndex] < from && removeTo[fromIndex] < from) {
+ fromIndex++;
+ }
+ // We have to add from `from` to `token.charOffset`, but possibly remove
+ // from `removeFrom[fromIndex]` to `removeTo[fromIndex]`,
+ // `removeFrom[fromIndex+1]` to `removeTo[fromIndex+1]` etc.
+ while (fromIndex < removeFrom.length && removeFrom[fromIndex] < to) {
+ if (from < removeFrom[fromIndex]) {
+ sb.write(sourceText.substring(from, removeFrom[fromIndex]));
+ }
+ from = removeTo[fromIndex];
+ fromIndex++;
+ }
+ // Now add any remaining text until the token.
+ if (from < to) {
+ sb.write(sourceText.substring(from, to));
+ }
+ } else {
+ sb.write(sourceText.substring(from, to));
+ }
+ }
+
for (_CommentOn entry in processed) {
// If - on a file without ignore comments - an ignore comment is
// pushed down (say inside an if instead of outside it), on a subsequent
@@ -391,11 +466,21 @@
token.previous?.lexeme == ":" ||
token.previous?.lexeme == ";" ||
token.previous?.lexeme == "=" ||
- token.lexeme == "?.") {
+ token.lexeme == "?." ||
+ token.lexeme == "?") {
extra = "\n ";
// If adding an extra linebreak would introduce an empty line we won't
// add it.
for (int i = token.charOffset - 1; i >= token.previous!.charEnd; i--) {
+ if (removeFrom.isNotEmpty) {
+ int fromIndex = binarySearch(removeFrom, i);
+ if (removeFrom[fromIndex] <= i && removeTo[fromIndex] >= i) {
+ // This is being removed. Change i to skip the rest.
+ i = removeFrom[fromIndex];
+ continue;
+ }
+ }
+
int codeUnit = sourceText.codeUnitAt(i);
if (codeUnit == $SPACE) {
// We ignore spaces.
@@ -413,8 +498,7 @@
if (token.precedingComments != null) {
token = token.precedingComments!;
}
- String substring = sourceText.substring(from, token.charOffset);
- sb.write(substring);
+ addChunk(from, token.charOffset);
// The extra spaces at the end makes the formatter format better if for
// instance there's comments after this.
@@ -423,14 +507,21 @@
} else {
sb.write("$extra// Coverage-ignore(suite): Not run.\n ");
}
+ ignoredIntervalsBuilder?.addIntervalIncludingEnd(
+ entry.beginToken.charOffset, entry.endToken.charEnd);
from = token.charOffset;
}
- sb.write(sourceText.substring(from));
+ addChunk(from, sourceText.length);
f.writeAsStringSync(sb.toString());
- // Return a fake result.
- return new CoverageInfo(
- allCovered: true, missCount: -1, hitCount: -1, visualization: "fake");
+ if (addAndRemoveCommentsInFiles) {
+ ignoredIntervals = ignoredIntervalsBuilder!.buildIntervalList();
+ needsFormatting?.add(fileUri);
+ } else {
+ // Return a fake result.
+ return new CoverageInfo(
+ allCovered: true, missCount: -1, hitCount: -1, visualization: "fake");
+ }
}
// TODO(jensj): Extract all comments and use those as well here.
@@ -569,6 +660,42 @@
visualization: visualization.toString());
}
+int _includeWhitespaceAfter(String sourceText, int from) {
+ while (sourceText.length > from &&
+ (sourceText.codeUnitAt(from) == $SPACE ||
+ sourceText.codeUnitAt(from) == $LF ||
+ sourceText.codeUnitAt(from) == $CR)) {
+ from++;
+ }
+ return from;
+}
+
+const List<String> _commentTextToRemove = const [
+ "coverage-ignore(suite): not run.",
+ "coverage-ignore-block(suite): not run.",
+];
+
+List<Token> _findTokensWithCoverageIgnoreSuiteComments(Token? token) {
+ List<Token> comments = [];
+ while (token != null && !token.isEof) {
+ Token? comment = token.precedingComments;
+ while (comment != null) {
+ String message = comment.lexeme.trim().toLowerCase();
+ while (message.startsWith("//") || message.startsWith("/*")) {
+ message = message.substring(2).trim();
+ }
+ for (String coverageIgnoreString in _commentTextToRemove) {
+ if (message.startsWith(coverageIgnoreString)) {
+ comments.add(comment);
+ }
+ }
+ comment = comment.next;
+ }
+ token = token.next;
+ }
+ return comments;
+}
+
({bool allCovered, Set<int> trimmedMisses}) _trimIgnoredAndPrintPercentages(
StringBuffer visualization,
IntervalList ignoredIntervals,
@@ -662,6 +789,7 @@
final List<String> _coverageBlockIgnores = [
"coverage-ignore-block:",
];
+ final List<String> _ignoresToIgnore = [];
final IntervalListBuilder ignoredStartEnd = new IntervalListBuilder();
@@ -675,13 +803,19 @@
late final _AstIndexerAndIgnoreCollectorBody _collectorBody =
new _AstIndexerAndIgnoreCollectorBody(this);
- static AstIndexerAndIgnoreCollector collect(ParserAstNode ast,
- List<String> extraCoverageIgnores, List<String> extraCoverageBlockIgnores,
- {required List<int> hitsSorted, required List<int> allSorted}) {
+ static AstIndexerAndIgnoreCollector collect(
+ ParserAstNode ast,
+ List<String> extraCoverageIgnores,
+ List<String> extraCoverageBlockIgnores, {
+ required List<int> hitsSorted,
+ required List<int> allSorted,
+ List<String> ignoresToIgnore = const [],
+ }) {
AstIndexerAndIgnoreCollector collector =
new AstIndexerAndIgnoreCollector._(hitsSorted, allSorted);
collector._coverageIgnores.addAll(extraCoverageIgnores);
collector._coverageBlockIgnores.addAll(extraCoverageBlockIgnores);
+ collector._ignoresToIgnore.addAll(ignoresToIgnore);
ast.accept(collector);
assert(collector.positionNodeIndex.length ==
@@ -706,9 +840,18 @@
while (message.startsWith("//") || message.startsWith("/*")) {
message = message.substring(2).trim();
}
- for (String coverageIgnoreString in coverageIgnores) {
+ bool skipComment = false;
+ for (String coverageIgnoreString in _ignoresToIgnore) {
if (message.startsWith(coverageIgnoreString)) {
- return true;
+ skipComment = true;
+ break;
+ }
+ }
+ if (!skipComment) {
+ for (String coverageIgnoreString in coverageIgnores) {
+ if (message.startsWith(coverageIgnoreString)) {
+ return true;
+ }
}
}
comment = comment.next;
@@ -735,7 +878,9 @@
/// If there is not it will add a note to add one if that makes sense (in that
/// there is possible coverage but no actual coverage).
bool _checkCommentAndIgnoreCoverageWithBeginAndEnd(
- Token tokenWithPossibleComment, Token beginToken, Token endToken,
+ final Token tokenWithPossibleComment,
+ final Token beginToken,
+ final Token endToken,
{required bool allowReplace,
bool isBlock = false,
bool allowOnBraceStart = false}) {
@@ -1176,6 +1321,19 @@
}
@override
+ void visitIndexedExpressionHandle(IndexedExpressionHandle node) {
+ // Given `a?[b]` if `a` is null `b` won't execute.
+ // Having the comment before the `?` looks better.
+ if (node.question != null &&
+ _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+ node.question!, node.openSquareBracket, node.closeSquareBracket,
+ allowReplace: true)) {
+ return;
+ }
+ super.visitIndexedExpressionHandle(node);
+ }
+
+ @override
void visitThenControlFlowHandle(ThenControlFlowHandle node) {
ParserAstNode? parent = node.parent;
if (parent is IfControlFlowEnd) {
diff --git a/pkg/front_end/tool/dart_doctest_impl.dart b/pkg/front_end/tool/dart_doctest_impl.dart
index a0de469..cf32984 100644
--- a/pkg/front_end/tool/dart_doctest_impl.dart
+++ b/pkg/front_end/tool/dart_doctest_impl.dart
@@ -237,7 +237,9 @@
message.toString().substring("$_portMessageBad: ".length);
result.add(new TestResult(currentTest!, TestOutcome.Failed)
..message = strippedMessage);
- _print(strippedMessage);
+ _print("Failure:\n"
+ "Test from ${currentTest!.location} failed with this message:\n"
+ "$strippedMessage\n");
} else if (message.toString().startsWith("$_portMessageCrash: ")) {
String strippedMessage =
message.toString().substring("$_portMessageCrash: ".length);
@@ -457,39 +459,47 @@
const int $SPACE = 32;
const int $STAR = 42;
-class Test {}
+sealed class Test {
+ String get location;
+}
class ExpectTest implements Test {
final String call;
final String result;
+ @override
+ final String location;
- ExpectTest(this.call, this.result);
+ ExpectTest(this.call, this.result, this.location);
@override
bool operator ==(Object other) {
if (other is! ExpectTest) return false;
if (other.call != call) return false;
if (other.result != result) return false;
+ if (other.location != location) return false;
return true;
}
@override
String toString() {
- return "ExpectTest[$call, $result]";
+ return "ExpectTest[$call, $result, $location]";
}
}
class TestParseError implements Test {
final String message;
final int position;
+ @override
+ final String location;
- TestParseError(this.message, this.position);
+ TestParseError(this.message, this.position, this.location);
@override
bool operator ==(Object other) {
if (other is! TestParseError) return false;
if (other.message != message) return false;
if (other.position != position) return false;
+ if (other.location != location) return false;
return true;
}
@@ -544,6 +554,12 @@
return result;
}
+ String getLocation(int offset) {
+ return source
+ .getLocation(source.importUri ?? source.fileUri!, offset)
+ .toString();
+ }
+
Test scanDartDoc(int scanOffset) {
final Token firstToken =
scanRawBytes(utf8.encode(comments.substring(scanOffset)));
@@ -569,13 +585,18 @@
StringBuffer sb = new StringBuffer();
int firstPosition = _createParseErrorMessages(
listener, sb, commentsData, scanOffset, source);
- return new TestParseError(sb.toString(), firstPosition);
+ return new TestParseError(
+ sb.toString(),
+ firstPosition,
+ getLocation(firstPosition),
+ );
} else if (!identical(",", comma.stringValue)) {
int position = commentsData.charOffset + scanOffset + comma.charOffset;
Message message = codes.templateExpectedButGot.withArguments(',');
return new TestParseError(
_createParseErrorMessage(source, position, comma, comma, message),
position,
+ getLocation(position),
);
}
@@ -586,13 +607,18 @@
StringBuffer sb = new StringBuffer();
int firstPosition = _createParseErrorMessages(
listener, sb, commentsData, scanOffset, source);
- return new TestParseError(sb.toString(), firstPosition);
+ return new TestParseError(
+ sb.toString(),
+ firstPosition,
+ getLocation(firstPosition),
+ );
} else if (!identical(")", endParen.stringValue)) {
int position = commentsData.charOffset + scanOffset + endParen.charOffset;
Message message = codes.templateExpectedButGot.withArguments(')');
return new TestParseError(
_createParseErrorMessage(source, position, comma, comma, message),
position,
+ getLocation(position),
);
}
@@ -606,6 +632,7 @@
return new ExpectTest(
comments.substring(startPos, midEndPos),
comments.substring(midStartPos, endPos),
+ getLocation(commentsData.charOffset + startPos),
);
}
diff --git a/pkg/front_end/tool/interval_list.dart b/pkg/front_end/tool/interval_list.dart
index 3ff3f1d..65c659a 100644
--- a/pkg/front_end/tool/interval_list.dart
+++ b/pkg/front_end/tool/interval_list.dart
@@ -7,8 +7,16 @@
// Based on (as in mostly a copy from) the _IntervalListBuilder in
// package:kernel/class_hierarchy.dart.
class IntervalListBuilder {
+ bool _finished = false;
final List<int> _events = <int>[];
+ IntervalListBuilder clone() {
+ if (_finished) throw "Can't clone an already finished builder.";
+ IntervalListBuilder clone = new IntervalListBuilder();
+ clone._events.addAll(_events);
+ return clone;
+ }
+
void addIntervalIncludingEnd(int start, int end) {
// Add an event point for each interval end point, using the low bit to
// distinguish opening from closing end points. Closing end points should
@@ -31,6 +39,7 @@
}
IntervalList buildIntervalList() {
+ if (_finished) throw "Can't build an already finished builder.";
// Sort the event points and sweep left to right while tracking how many
// intervals we are currently inside. Record an interval end point when the
// number of intervals drop to zero or increase from zero to one.
@@ -61,6 +70,8 @@
for (int i = 0; i < storeIndex; ++i) {
result[i] = _events[i];
}
+
+ _finished = true;
return new IntervalList._(result);
}
}
diff --git a/tools/VERSION b/tools/VERSION
index f4f3383..5b019ed 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 3
MINOR 6
PATCH 0
-PRERELEASE 108
+PRERELEASE 109
PRERELEASE_PATCH 0