[CFE] Refactor const collection construction into builders.
This refactoring does not change the behavior of the constant
evaluator, but is paves the way for the fix of
https://github.com/dart-lang/sdk/issues/36812
Change-Id: Ie621a352ce6b69ce7e9bd97ffdc688b327e3a7dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101260
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart b/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
new file mode 100644
index 0000000..7d83bc2
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_collection_builders.dart
@@ -0,0 +1,427 @@
+// Copyright (c) 2019, 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.
+
+part of 'constant_evaluator.dart';
+
+abstract class _ListOrSetConstantBuilder<L extends Expression,
+ C extends Constant> {
+ final ConstantEvaluator evaluator;
+ final Expression original;
+ final DartType elementType;
+
+ // Each element of [parts] is either a `List<Constant>` (containing fully
+ // evaluated constants) or a `Constant` (potentially unevaluated).
+ List<Object> parts = <Object>[<Constant>[]];
+
+ _ListOrSetConstantBuilder(this.original, this.elementType, this.evaluator);
+
+ L makeLiteral(List<Expression> elements);
+
+ C makeConstant(List<Constant> elements);
+
+ Message get messageForIteration;
+
+ /// Add an element (which is possibly a spread or an if element) to the
+ /// constant list being built by this builder.
+ void add(Expression element) {
+ if (element is SpreadElement) {
+ addSpread(element.expression, isNullAware: element.isNullAware);
+ } else if (element is IfElement) {
+ Constant condition = evaluator._evaluateSubexpression(element.condition);
+ if (evaluator.shouldBeUnevaluated) {
+ // Unevaluated if
+ evaluator.enterLazy();
+ Constant then = evaluator._evaluateSubexpression(
+ makeLiteral([evaluator.cloner.clone(element.then)]));
+ Constant otherwise;
+ if (element.otherwise != null) {
+ otherwise = evaluator._evaluateSubexpression(
+ makeLiteral([evaluator.cloner.clone(element.otherwise)]));
+ } else {
+ otherwise = makeConstant([]);
+ }
+ evaluator.leaveLazy();
+ parts.add(evaluator.unevaluated(
+ element.condition,
+ new ConditionalExpression(
+ evaluator.extract(condition),
+ evaluator.extract(then),
+ evaluator.extract(otherwise),
+ const DynamicType())));
+ } else {
+ // Fully evaluated if
+ if (condition == evaluator.trueConstant) {
+ add(element.then);
+ } else if (condition == evaluator.falseConstant) {
+ if (element.otherwise != null) {
+ add(element.otherwise);
+ }
+ } else if (condition == evaluator.nullConstant) {
+ evaluator.report(element.condition, messageConstEvalNullValue);
+ } else {
+ evaluator.report(
+ element.condition,
+ templateConstEvalInvalidType.withArguments(
+ condition,
+ evaluator.typeEnvironment.boolType,
+ condition.getType(evaluator.typeEnvironment)));
+ }
+ }
+ } else if (element is ForElement || element is ForInElement) {
+ // For or for-in
+ evaluator.report(element, messageForIteration);
+ } else {
+ // Ordinary expression element
+ Constant constant = evaluator._evaluateSubexpression(element);
+ if (evaluator.shouldBeUnevaluated) {
+ parts.add(evaluator.unevaluated(
+ element, makeLiteral([evaluator.extract(constant)])));
+ } else {
+ addConstant(constant, element);
+ }
+ }
+ }
+
+ void addSpread(Expression spreadExpression, {bool isNullAware}) {
+ Constant spread =
+ evaluator.unlower(evaluator._evaluateSubexpression(spreadExpression));
+ if (evaluator.shouldBeUnevaluated) {
+ // Unevaluated spread
+ if (isNullAware) {
+ VariableDeclaration temp = new VariableDeclaration(null,
+ initializer: evaluator.extract(spread));
+ parts.add(evaluator.unevaluated(
+ spreadExpression,
+ new Let(
+ temp,
+ new ConditionalExpression(
+ new MethodInvocation(new VariableGet(temp), new Name('=='),
+ new Arguments([new NullLiteral()])),
+ new ListLiteral([], isConst: true),
+ new VariableGet(temp),
+ const DynamicType()))));
+ } else {
+ parts.add(spread);
+ }
+ } else if (spread == evaluator.nullConstant) {
+ // Null spread
+ if (!isNullAware) {
+ evaluator.report(spreadExpression, messageConstEvalNullValue);
+ }
+ } else {
+ // Fully evaluated spread
+ List<Constant> entries;
+ if (spread is ListConstant) {
+ entries = spread.entries;
+ } else if (spread is SetConstant) {
+ entries = spread.entries;
+ } else {
+ // Not list or set in spread
+ return evaluator.report(
+ spreadExpression, messageConstEvalNotListOrSetInSpread);
+ }
+ for (Constant entry in entries) {
+ addConstant(entry, spreadExpression);
+ }
+ }
+ }
+
+ void addConstant(Constant constant, TreeNode context);
+}
+
+class ListConstantBuilder
+ extends _ListOrSetConstantBuilder<ListLiteral, ListConstant> {
+ ListConstantBuilder(
+ Expression original, DartType elementType, ConstantEvaluator evaluator)
+ : super(original, elementType, evaluator);
+
+ @override
+ ListLiteral makeLiteral(List<Expression> elements) =>
+ new ListLiteral(elements, isConst: true);
+
+ @override
+ ListConstant makeConstant(List<Constant> elements) =>
+ new ListConstant(const DynamicType(), elements);
+
+ @override
+ Message get messageForIteration => messageConstEvalIterationInConstList;
+
+ @override
+ void addConstant(Constant constant, TreeNode context) {
+ List<Constant> lastPart;
+ if (parts.last is List<Constant>) {
+ lastPart = parts.last;
+ } else {
+ parts.add(lastPart = <Constant>[]);
+ }
+ lastPart.add(evaluator.ensureIsSubtype(constant, elementType, context));
+ }
+
+ Constant build() {
+ if (parts.length == 1) {
+ // Fully evaluated
+ return evaluator
+ .lowerListConstant(new ListConstant(elementType, parts.single));
+ }
+ List<Expression> lists = <Expression>[];
+ for (Object part in parts) {
+ if (part is List<Constant>) {
+ lists.add(new ConstantExpression(new ListConstant(elementType, part)));
+ } else if (part is Constant) {
+ lists.add(evaluator.extract(part));
+ } else {
+ throw 'Non-constant in constant list';
+ }
+ }
+ return evaluator.unevaluated(
+ original, new ListConcatenation(lists, typeArgument: elementType));
+ }
+}
+
+class SetConstantBuilder
+ extends _ListOrSetConstantBuilder<SetLiteral, SetConstant> {
+ final Set<Constant> seen = new Set<Constant>.identity();
+
+ SetConstantBuilder(
+ Expression original, DartType elementType, ConstantEvaluator evaluator)
+ : super(original, elementType, evaluator);
+
+ @override
+ SetLiteral makeLiteral(List<Expression> elements) =>
+ new SetLiteral(elements, isConst: true);
+
+ @override
+ SetConstant makeConstant(List<Constant> elements) =>
+ new SetConstant(const DynamicType(), elements);
+
+ @override
+ Message get messageForIteration => messageConstEvalIterationInConstSet;
+
+ @override
+ void addConstant(Constant constant, TreeNode context) {
+ if (!evaluator.hasPrimitiveEqual(constant)) {
+ evaluator.report(context,
+ templateConstEvalElementImplementsEqual.withArguments(constant));
+ }
+ if (!seen.add(constant)) {
+ evaluator.report(
+ context, templateConstEvalDuplicateElement.withArguments(constant));
+ }
+
+ List<Constant> lastPart;
+ if (parts.last is List<Constant>) {
+ lastPart = parts.last;
+ } else {
+ parts.add(lastPart = <Constant>[]);
+ }
+ lastPart.add(evaluator.ensureIsSubtype(constant, elementType, context));
+ }
+
+ Constant build() {
+ if (parts.length == 1) {
+ // Fully evaluated
+ List<Constant> entries = parts.single;
+ SetConstant result = new SetConstant(elementType, entries);
+ if (evaluator.desugarSets) {
+ final List<ConstantMapEntry> mapEntries =
+ new List<ConstantMapEntry>(entries.length);
+ for (int i = 0; i < entries.length; ++i) {
+ mapEntries[i] =
+ new ConstantMapEntry(entries[i], evaluator.nullConstant);
+ }
+ Constant map = evaluator.lowerMapConstant(new MapConstant(
+ elementType, evaluator.typeEnvironment.nullType, mapEntries));
+ return evaluator.lower(
+ result,
+ new InstanceConstant(
+ evaluator.unmodifiableSetMap.enclosingClass.reference, [
+ elementType
+ ], <Reference, Constant>{
+ evaluator.unmodifiableSetMap.reference: map
+ }));
+ } else {
+ return evaluator.lowerSetConstant(result);
+ }
+ }
+ List<Expression> sets = <Expression>[];
+ for (Object part in parts) {
+ if (part is List<Constant>) {
+ sets.add(new ConstantExpression(new SetConstant(elementType, part)));
+ } else if (part is Constant) {
+ sets.add(evaluator.extract(part));
+ } else {
+ throw 'Non-constant in constant set';
+ }
+ }
+ return evaluator.unevaluated(
+ original, new SetConcatenation(sets, typeArgument: elementType));
+ }
+}
+
+class MapConstantBuilder {
+ final ConstantEvaluator evaluator;
+ final Expression original;
+ final DartType keyType;
+ final DartType valueType;
+
+ /// Each element of [parts] is either a `List<ConstantMapEntry>` (containing
+ /// fully evaluated map entries) or a `Constant` (potentially unevaluated).
+ List<Object> parts = <Object>[<ConstantMapEntry>[]];
+
+ final Set<Constant> seenKeys = new Set<Constant>.identity();
+
+ MapConstantBuilder(
+ this.original, this.keyType, this.valueType, this.evaluator);
+
+ /// Add a map entry (which is possibly a spread or an if map entry) to the
+ /// constant map being built by this builder
+ void add(MapEntry element) {
+ if (element is SpreadMapEntry) {
+ addSpread(element.expression, isNullAware: element.isNullAware);
+ } else if (element is IfMapEntry) {
+ Constant condition = evaluator._evaluateSubexpression(element.condition);
+ if (evaluator.shouldBeUnevaluated) {
+ // Unevaluated if
+ evaluator.enterLazy();
+ Constant then = evaluator._evaluateSubexpression(new MapLiteral(
+ [evaluator.cloner.clone(element.then)],
+ isConst: true));
+ Constant otherwise;
+ if (element.otherwise != null) {
+ otherwise = evaluator._evaluateSubexpression(new MapLiteral(
+ [evaluator.cloner.clone(element.otherwise)],
+ isConst: true));
+ } else {
+ otherwise =
+ new MapConstant(const DynamicType(), const DynamicType(), []);
+ }
+ evaluator.leaveLazy();
+ parts.add(evaluator.unevaluated(
+ element.condition,
+ new ConditionalExpression(
+ evaluator.extract(condition),
+ evaluator.extract(then),
+ evaluator.extract(otherwise),
+ const DynamicType())));
+ } else {
+ // Fully evaluated if
+ if (condition == evaluator.trueConstant) {
+ add(element.then);
+ } else if (condition == evaluator.falseConstant) {
+ if (element.otherwise != null) {
+ add(element.otherwise);
+ }
+ } else if (condition == evaluator.nullConstant) {
+ evaluator.report(element.condition, messageConstEvalNullValue);
+ } else {
+ evaluator.report(
+ element.condition,
+ templateConstEvalInvalidType.withArguments(
+ condition,
+ evaluator.typeEnvironment.boolType,
+ condition.getType(evaluator.typeEnvironment)));
+ }
+ }
+ } else if (element is ForMapEntry || element is ForInMapEntry) {
+ // For or for-in
+ evaluator.report(element, messageConstEvalIterationInConstMap);
+ } else {
+ // Ordinary map entry
+ Constant key = evaluator._evaluateSubexpression(element.key);
+ Constant value = evaluator._evaluateSubexpression(element.value);
+ if (evaluator.shouldBeUnevaluated) {
+ parts.add(evaluator.unevaluated(
+ element.key,
+ new MapLiteral([
+ new MapEntry(evaluator.extract(key), evaluator.extract(value))
+ ], isConst: true)));
+ } else {
+ addConstant(key, value, element.key, element.value);
+ }
+ }
+ }
+
+ void addSpread(Expression spreadExpression, {bool isNullAware}) {
+ Constant spread =
+ evaluator.unlower(evaluator._evaluateSubexpression(spreadExpression));
+ if (evaluator.shouldBeUnevaluated) {
+ // Unevaluated spread
+ if (isNullAware) {
+ VariableDeclaration temp = new VariableDeclaration(null,
+ initializer: evaluator.extract(spread));
+ parts.add(evaluator.unevaluated(
+ spreadExpression,
+ new Let(
+ temp,
+ new ConditionalExpression(
+ new MethodInvocation(new VariableGet(temp), new Name('=='),
+ new Arguments([new NullLiteral()])),
+ new MapLiteral([], isConst: true),
+ new VariableGet(temp),
+ const DynamicType()))));
+ } else {
+ parts.add(spread);
+ }
+ } else if (spread == evaluator.nullConstant) {
+ // Null spread
+ if (!isNullAware) {
+ evaluator.report(spreadExpression, messageConstEvalNullValue);
+ }
+ } else {
+ // Fully evaluated spread
+ if (spread is MapConstant) {
+ for (ConstantMapEntry entry in spread.entries) {
+ addConstant(
+ entry.key, entry.value, spreadExpression, spreadExpression);
+ }
+ } else {
+ // Not map in spread
+ return evaluator.report(
+ spreadExpression, messageConstEvalNotMapInSpread);
+ }
+ }
+ }
+
+ void addConstant(Constant key, Constant value, TreeNode keyContext,
+ TreeNode valueContext) {
+ List<ConstantMapEntry> lastPart;
+ if (parts.last is List<ConstantMapEntry>) {
+ lastPart = parts.last;
+ } else {
+ parts.add(lastPart = <ConstantMapEntry>[]);
+ }
+ if (!evaluator.hasPrimitiveEqual(key)) {
+ evaluator.report(
+ keyContext, templateConstEvalKeyImplementsEqual.withArguments(key));
+ }
+ if (!seenKeys.add(key)) {
+ evaluator.report(
+ keyContext, templateConstEvalDuplicateKey.withArguments(key));
+ }
+ lastPart.add(new ConstantMapEntry(
+ evaluator.ensureIsSubtype(key, keyType, keyContext),
+ evaluator.ensureIsSubtype(value, valueType, valueContext)));
+ }
+
+ Constant build() {
+ if (parts.length == 1) {
+ // Fully evaluated
+ return evaluator
+ .lowerMapConstant(new MapConstant(keyType, valueType, parts.single));
+ }
+ List<Expression> maps = <Expression>[];
+ for (Object part in parts) {
+ if (part is List<ConstantMapEntry>) {
+ maps.add(
+ new ConstantExpression(new MapConstant(keyType, valueType, part)));
+ } else if (part is Constant) {
+ maps.add(evaluator.extract(part));
+ } else {
+ throw 'Non-constant in constant map';
+ }
+ }
+ return evaluator.unevaluated(original,
+ new MapConcatenation(maps, keyType: keyType, valueType: valueType));
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 510b60b..d50ce8d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -78,6 +78,8 @@
IfMapEntry,
SpreadMapEntry;
+part 'constant_collection_builders.dart';
+
Component transformComponent(Component component, ConstantsBackend backend,
Map<String, String> environmentDefines, ErrorReporter errorReporter,
{bool keepFields: true,
@@ -765,220 +767,26 @@
return canonicalize(result);
}
- /// Add an element (which is possibly a spread or an if element) to a
- /// constant list or set represented as a list of (possibly unevaluated)
- /// lists or sets to be concatenated.
- /// Each element of [parts] is either a `List<Constant>` (containing fully
- /// evaluated constants) or a `Constant` (potentially unevaluated).
- /// Pass an identity set as [seen] for sets and omit it for lists.
- void addToListOrSetConstant(
- List<Object> parts, Expression element, DartType elementType,
- [Set<Constant> seen]) {
- bool isSet = seen != null;
- if (element is SpreadElement) {
- Constant spread = unlower(_evaluateSubexpression(element.expression));
- if (shouldBeUnevaluated) {
- // Unevaluated spread
- if (element.isNullAware) {
- VariableDeclaration temp =
- new VariableDeclaration(null, initializer: extract(spread));
- parts.add(unevaluated(
- element.expression,
- new Let(
- temp,
- new ConditionalExpression(
- new MethodInvocation(new VariableGet(temp),
- new Name('=='), new Arguments([new NullLiteral()])),
- new ListLiteral([], isConst: true),
- new VariableGet(temp),
- const DynamicType()))));
- } else {
- parts.add(spread);
- }
- } else if (spread == nullConstant) {
- // Null spread
- if (!element.isNullAware) {
- report(element.expression, messageConstEvalNullValue);
- }
- } else {
- // Fully evaluated spread
- List<Constant> entries;
- if (spread is ListConstant) {
- entries = spread.entries;
- } else if (spread is SetConstant) {
- entries = spread.entries;
- } else {
- // Not list or set in spread
- return report(
- element.expression, messageConstEvalNotListOrSetInSpread);
- }
- for (Constant entry in entries) {
- addToListOrSetConstant(
- parts, new ConstantExpression(entry), elementType, seen);
- }
- }
- } else if (element is IfElement) {
- Constant condition = _evaluateSubexpression(element.condition);
- if (shouldBeUnevaluated) {
- // Unevaluated if
- enterLazy();
- Constant then = _evaluateSubexpression(isSet
- ? new SetLiteral([cloner.clone(element.then)], isConst: true)
- : new ListLiteral([cloner.clone(element.then)], isConst: true));
- Constant otherwise;
- if (element.otherwise != null) {
- otherwise = _evaluateSubexpression(isSet
- ? new SetLiteral([cloner.clone(element.otherwise)], isConst: true)
- : new ListLiteral([cloner.clone(element.otherwise)],
- isConst: true));
- } else {
- otherwise = isSet
- ? new SetConstant(const DynamicType(), [])
- : new ListConstant(const DynamicType(), []);
- }
- leaveLazy();
- parts.add(unevaluated(
- element.condition,
- new ConditionalExpression(extract(condition), extract(then),
- extract(otherwise), const DynamicType())));
- } else {
- // Fully evaluated if
- if (condition == trueConstant) {
- addToListOrSetConstant(parts, element.then, elementType, seen);
- } else if (condition == falseConstant) {
- if (element.otherwise != null) {
- addToListOrSetConstant(parts, element.otherwise, elementType, seen);
- }
- } else if (condition == nullConstant) {
- report(element.condition, messageConstEvalNullValue);
- } else {
- report(
- element.condition,
- templateConstEvalInvalidType.withArguments(
- condition,
- typeEnvironment.boolType,
- condition.getType(typeEnvironment)));
- }
- }
- } else if (element is ForElement || element is ForInElement) {
- // For or for-in
- report(
- element,
- isSet
- ? messageConstEvalIterationInConstSet
- : messageConstEvalIterationInConstList);
- } else {
- // Ordinary expresion element
- Constant constant = _evaluateSubexpression(element);
- if (shouldBeUnevaluated) {
- parts.add(unevaluated(
- element,
- isSet
- ? new SetLiteral([extract(constant)],
- typeArgument: elementType, isConst: true)
- : new ListLiteral([extract(constant)],
- typeArgument: elementType, isConst: true)));
- } else {
- List<Constant> listOrSet;
- if (parts.last is List<Constant>) {
- listOrSet = parts.last;
- } else {
- parts.add(listOrSet = <Constant>[]);
- }
- if (isSet) {
- if (!hasPrimitiveEqual(constant)) {
- report(
- element,
- templateConstEvalElementImplementsEqual
- .withArguments(constant));
- }
- if (!seen.add(constant)) {
- report(element,
- templateConstEvalDuplicateElement.withArguments(constant));
- }
- }
- listOrSet.add(ensureIsSubtype(constant, elementType, element));
- }
- }
- }
-
- Constant makeListConstantFromParts(
- List<Object> parts, Expression node, DartType elementType) {
- if (parts.length == 1) {
- // Fully evaluated
- return lowerListConstant(new ListConstant(elementType, parts.single));
- }
- List<Expression> lists = <Expression>[];
- for (Object part in parts) {
- if (part is List<Constant>) {
- lists.add(new ConstantExpression(new ListConstant(elementType, part)));
- } else if (part is Constant) {
- lists.add(extract(part));
- } else {
- throw 'Non-constant in constant list';
- }
- }
- return unevaluated(
- node, new ListConcatenation(lists, typeArgument: elementType));
- }
-
visitListLiteral(ListLiteral node) {
if (!node.isConst) {
return report(
node, templateConstEvalNonConstantLiteral.withArguments('List'));
}
- final List<Object> parts = <Object>[<Constant>[]];
+ final ListConstantBuilder builder =
+ new ListConstantBuilder(node, node.typeArgument, this);
for (Expression element in node.expressions) {
- addToListOrSetConstant(parts, element, node.typeArgument);
+ builder.add(element);
}
- return makeListConstantFromParts(parts, node, node.typeArgument);
+ return builder.build();
}
visitListConcatenation(ListConcatenation node) {
- final List<Object> parts = <Object>[<Constant>[]];
+ final ListConstantBuilder builder =
+ new ListConstantBuilder(node, node.typeArgument, this);
for (Expression list in node.lists) {
- addToListOrSetConstant(parts,
- new SpreadElement(cloner.clone(list), false), node.typeArgument);
+ builder.addSpread(list, isNullAware: false);
}
- return makeListConstantFromParts(parts, node, node.typeArgument);
- }
-
- Constant makeSetConstantFromParts(
- List<Object> parts, Expression node, DartType elementType) {
- if (parts.length == 1) {
- // Fully evaluated
- List<Constant> entries = parts.single;
- SetConstant result = new SetConstant(elementType, entries);
- if (desugarSets) {
- final List<ConstantMapEntry> mapEntries =
- new List<ConstantMapEntry>(entries.length);
- for (int i = 0; i < entries.length; ++i) {
- mapEntries[i] = new ConstantMapEntry(entries[i], nullConstant);
- }
- Constant map = lowerMapConstant(
- new MapConstant(elementType, typeEnvironment.nullType, mapEntries));
- return lower(
- result,
- new InstanceConstant(
- unmodifiableSetMap.enclosingClass.reference,
- [elementType],
- <Reference, Constant>{unmodifiableSetMap.reference: map}));
- } else {
- return lowerSetConstant(result);
- }
- }
- List<Expression> sets = <Expression>[];
- for (Object part in parts) {
- if (part is List<Constant>) {
- sets.add(new ConstantExpression(new SetConstant(elementType, part)));
- } else if (part is Constant) {
- sets.add(extract(part));
- } else {
- throw 'Non-constant in constant set';
- }
- }
- return unevaluated(
- node, new SetConcatenation(sets, typeArgument: elementType));
+ return builder.build();
}
visitSetLiteral(SetLiteral node) {
@@ -986,169 +794,21 @@
return report(
node, templateConstEvalNonConstantLiteral.withArguments('Set'));
}
- final Set<Constant> seen = new Set<Constant>.identity();
- final List<Object> parts = <Object>[<Constant>[]];
+ final SetConstantBuilder builder =
+ new SetConstantBuilder(node, node.typeArgument, this);
for (Expression element in node.expressions) {
- addToListOrSetConstant(parts, element, node.typeArgument, seen);
+ builder.add(element);
}
- return makeSetConstantFromParts(parts, node, node.typeArgument);
+ return builder.build();
}
visitSetConcatenation(SetConcatenation node) {
- final Set<Constant> seen = new Set<Constant>.identity();
- final List<Object> parts = <Object>[<Constant>[]];
+ final SetConstantBuilder builder =
+ new SetConstantBuilder(node, node.typeArgument, this);
for (Expression set_ in node.sets) {
- addToListOrSetConstant(
- parts,
- new SpreadElement(cloner.clone(set_), false),
- node.typeArgument,
- seen);
+ builder.addSpread(set_, isNullAware: false);
}
- return makeSetConstantFromParts(parts, node, node.typeArgument);
- }
-
- /// Add a map entry (which is possibly a spread or an if map entry) to a
- /// constant map represented as a list of (possibly unevaluated)
- /// maps to be concatenated.
- /// Each element of [parts] is either a `List<ConstantMapEntry>` (containing
- /// fully evaluated map entries) or a `Constant` (potentially unevaluated).
- void addToMapConstant(List<Object> parts, MapEntry element, DartType keyType,
- DartType valueType, Set<Constant> seenKeys) {
- if (element is SpreadMapEntry) {
- Constant spread = unlower(_evaluateSubexpression(element.expression));
- if (shouldBeUnevaluated) {
- // Unevaluated spread
- if (element.isNullAware) {
- VariableDeclaration temp =
- new VariableDeclaration(null, initializer: extract(spread));
- parts.add(unevaluated(
- element.expression,
- new Let(
- temp,
- new ConditionalExpression(
- new MethodInvocation(new VariableGet(temp),
- new Name('=='), new Arguments([new NullLiteral()])),
- new MapLiteral([], isConst: true),
- new VariableGet(temp),
- const DynamicType()))));
- } else {
- parts.add(spread);
- }
- } else if (spread == nullConstant) {
- // Null spread
- if (!element.isNullAware) {
- report(element.expression, messageConstEvalNullValue);
- }
- } else {
- // Fully evaluated spread
- if (spread is MapConstant) {
- for (ConstantMapEntry entry in spread.entries) {
- addToMapConstant(
- parts,
- new MapEntry(new ConstantExpression(entry.key),
- new ConstantExpression(entry.value)),
- keyType,
- valueType,
- seenKeys);
- }
- } else {
- // Not map in spread
- return report(element.expression, messageConstEvalNotMapInSpread);
- }
- }
- } else if (element is IfMapEntry) {
- Constant condition = _evaluateSubexpression(element.condition);
- if (shouldBeUnevaluated) {
- // Unevaluated if
- enterLazy();
- Constant then = _evaluateSubexpression(
- new MapLiteral([cloner.clone(element.then)], isConst: true));
- Constant otherwise;
- if (element.otherwise != null) {
- otherwise = _evaluateSubexpression(
- new MapLiteral([cloner.clone(element.otherwise)], isConst: true));
- } else {
- otherwise =
- new MapConstant(const DynamicType(), const DynamicType(), []);
- }
- leaveLazy();
- parts.add(unevaluated(
- element.condition,
- new ConditionalExpression(extract(condition), extract(then),
- extract(otherwise), const DynamicType())));
- } else {
- // Fully evaluated if
- if (condition == trueConstant) {
- addToMapConstant(parts, element.then, keyType, valueType, seenKeys);
- } else if (condition == falseConstant) {
- if (element.otherwise != null) {
- addToMapConstant(
- parts, element.otherwise, keyType, valueType, seenKeys);
- }
- } else if (condition == nullConstant) {
- report(element.condition, messageConstEvalNullValue);
- } else {
- report(
- element.condition,
- templateConstEvalInvalidType.withArguments(
- condition,
- typeEnvironment.boolType,
- condition.getType(typeEnvironment)));
- }
- }
- } else if (element is ForMapEntry || element is ForInMapEntry) {
- // For or for-in
- report(element, messageConstEvalIterationInConstMap);
- } else {
- // Ordinary map entry
- Constant key = _evaluateSubexpression(element.key);
- Constant value = _evaluateSubexpression(element.value);
- if (shouldBeUnevaluated) {
- parts.add(unevaluated(
- element.key,
- new MapLiteral([new MapEntry(extract(key), extract(value))],
- isConst: true)));
- } else {
- List<ConstantMapEntry> entries;
- if (parts.last is List<ConstantMapEntry>) {
- entries = parts.last;
- } else {
- parts.add(entries = <ConstantMapEntry>[]);
- }
- if (!hasPrimitiveEqual(key)) {
- report(
- element, templateConstEvalKeyImplementsEqual.withArguments(key));
- }
- if (!seenKeys.add(key)) {
- report(element.key, templateConstEvalDuplicateKey.withArguments(key));
- }
- entries.add(new ConstantMapEntry(
- ensureIsSubtype(key, keyType, element.key),
- ensureIsSubtype(value, valueType, element.value)));
- }
- }
- }
-
- Constant makeMapConstantFromParts(List<Object> parts, Expression node,
- DartType keyType, DartType valueType) {
- if (parts.length == 1) {
- // Fully evaluated
- return lowerMapConstant(
- new MapConstant(keyType, valueType, parts.single));
- }
- List<Expression> maps = <Expression>[];
- for (Object part in parts) {
- if (part is List<ConstantMapEntry>) {
- maps.add(
- new ConstantExpression(new MapConstant(keyType, valueType, part)));
- } else if (part is Constant) {
- maps.add(extract(part));
- } else {
- throw 'Non-constant in constant map';
- }
- }
- return unevaluated(node,
- new MapConcatenation(maps, keyType: keyType, valueType: valueType));
+ return builder.build();
}
visitMapLiteral(MapLiteral node) {
@@ -1156,22 +816,21 @@
return report(
node, templateConstEvalNonConstantLiteral.withArguments('Map'));
}
- final Set<Constant> seen = new Set<Constant>.identity();
- final List<Object> parts = <Object>[<ConstantMapEntry>[]];
+ final MapConstantBuilder builder =
+ new MapConstantBuilder(node, node.keyType, node.valueType, this);
for (MapEntry element in node.entries) {
- addToMapConstant(parts, element, node.keyType, node.valueType, seen);
+ builder.add(element);
}
- return makeMapConstantFromParts(parts, node, node.keyType, node.valueType);
+ return builder.build();
}
visitMapConcatenation(MapConcatenation node) {
- final Set<Constant> seen = new Set<Constant>.identity();
- final List<Object> parts = <Object>[<ConstantMapEntry>[]];
+ final MapConstantBuilder builder =
+ new MapConstantBuilder(node, node.keyType, node.valueType, this);
for (Expression map in node.maps) {
- addToMapConstant(parts, new SpreadMapEntry(cloner.clone(map), false),
- node.keyType, node.valueType, seen);
+ builder.addSpread(map, isNullAware: false);
}
- return makeMapConstantFromParts(parts, node, node.keyType, node.valueType);
+ return builder.build();
}
visitFunctionExpression(FunctionExpression node) {