| // 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> { | 
 |   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); | 
 |  | 
 |   /// Add an element to the constant list being built by this builder. | 
 |   /// | 
 |   /// Returns [null] on success and an error-"constant" on failure, as such the | 
 |   /// return value should be checked. | 
 |   AbortConstant add(Expression element) { | 
 |     Constant constant = evaluator._evaluateSubexpression(element); | 
 |     if (constant is AbortConstant) return constant; | 
 |     if (evaluator.shouldBeUnevaluated) { | 
 |       parts.add(evaluator.unevaluated( | 
 |           element, makeLiteral([evaluator.extract(constant)]))); | 
 |       return null; | 
 |     } else { | 
 |       return addConstant(constant, element); | 
 |     } | 
 |   } | 
 |  | 
 |   /// Returns [null] on success and an error-"constant" on failure, as such the | 
 |   /// return value should be checked. | 
 |   AbortConstant addSpread(Expression spreadExpression) { | 
 |     Constant constant = evaluator._evaluateSubexpression(spreadExpression); | 
 |     if (constant is AbortConstant) return constant; | 
 |     Constant spread = evaluator.unlower(constant); | 
 |     if (evaluator.shouldBeUnevaluated) { | 
 |       // Unevaluated spread | 
 |       parts.add(spread); | 
 |     } else if (spread == evaluator.nullConstant) { | 
 |       // Null spread | 
 |       return evaluator.createErrorConstant( | 
 |           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.createErrorConstant( | 
 |             spreadExpression, messageConstEvalNotListOrSetInSpread); | 
 |       } | 
 |       for (Constant entry in entries) { | 
 |         AbortConstant error = addConstant(entry, spreadExpression); | 
 |         if (error != null) return error; | 
 |       } | 
 |     } | 
 |     return null; | 
 |   } | 
 |  | 
 |   /// Returns [null] on success and an error-"constant" on failure, as such the | 
 |   /// return value should be checked. | 
 |   AbortConstant addConstant(Constant constant, TreeNode context); | 
 |  | 
 |   Constant build(); | 
 | } | 
 |  | 
 | class ListConstantBuilder extends _ListOrSetConstantBuilder<ListLiteral> { | 
 |   ListConstantBuilder( | 
 |       Expression original, DartType elementType, ConstantEvaluator evaluator) | 
 |       : super(original, elementType, evaluator); | 
 |  | 
 |   @override | 
 |   ListLiteral makeLiteral(List<Expression> elements) => | 
 |       new ListLiteral(elements, isConst: true); | 
 |  | 
 |   @override | 
 |   AbortConstant addConstant(Constant constant, TreeNode context) { | 
 |     List<Constant> lastPart; | 
 |     if (parts.last is List<Constant>) { | 
 |       lastPart = parts.last; | 
 |     } else { | 
 |       // Probably unreachable. | 
 |       parts.add(lastPart = <Constant>[]); | 
 |     } | 
 |     Constant value = evaluator.ensureIsSubtype(constant, elementType, context); | 
 |     if (value is AbortConstant) return value; | 
 |     lastPart.add(value); | 
 |     return null; | 
 |   } | 
 |  | 
 |   @override | 
 |   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> { | 
 |   final Set<Constant> seen = new Set<Constant>.identity(); | 
 |   final Set<Constant> weakSeen = 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 | 
 |   AbortConstant addConstant(Constant constant, TreeNode context) { | 
 |     if (!evaluator.hasPrimitiveEqual(constant)) { | 
 |       return evaluator.createErrorConstant( | 
 |           context, | 
 |           templateConstEvalElementImplementsEqual.withArguments( | 
 |               constant, evaluator.isNonNullableByDefault)); | 
 |     } | 
 |     bool unseen = seen.add(constant); | 
 |     if (!unseen) { | 
 |       return evaluator.createErrorConstant( | 
 |           context, | 
 |           templateConstEvalDuplicateElement.withArguments( | 
 |               constant, evaluator.isNonNullableByDefault)); | 
 |     } | 
 |     if (evaluator.evaluationMode == EvaluationMode.agnostic) { | 
 |       Constant weakConstant = | 
 |           evaluator._weakener.visitConstant(constant) ?? constant; | 
 |       bool weakUnseen = weakSeen.add(weakConstant); | 
 |       if (unseen != weakUnseen) { | 
 |         return evaluator.createErrorConstant( | 
 |             context, messageNonAgnosticConstant); | 
 |       } | 
 |     } | 
 |  | 
 |     List<Constant> lastPart; | 
 |     if (parts.last is List<Constant>) { | 
 |       lastPart = parts.last; | 
 |     } else { | 
 |       // Probably unreachable. | 
 |       parts.add(lastPart = <Constant>[]); | 
 |     } | 
 |     Constant value = evaluator.ensureIsSubtype(constant, elementType, context); | 
 |     if (value is AbortConstant) return value; | 
 |     lastPart.add(value); | 
 |     return null; | 
 |   } | 
 |  | 
 |   @override | 
 |   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(); | 
 |   final Set<Constant> weakSeenKeys = new Set<Constant>.identity(); | 
 |  | 
 |   MapConstantBuilder( | 
 |       this.original, this.keyType, this.valueType, this.evaluator); | 
 |  | 
 |   /// Add a map entry to the constant map being built by this builder | 
 |   /// | 
 |   /// Returns [null] on success and an error-"constant" on failure, as such the | 
 |   /// return value should be checked. | 
 |   AbortConstant add(MapEntry element) { | 
 |     Constant key = evaluator._evaluateSubexpression(element.key); | 
 |     if (key is AbortConstant) return key; | 
 |     Constant value = evaluator._evaluateSubexpression(element.value); | 
 |     if (value is AbortConstant) return value; | 
 |     if (evaluator.shouldBeUnevaluated) { | 
 |       parts.add(evaluator.unevaluated( | 
 |           element.key, | 
 |           new MapLiteral( | 
 |               [new MapEntry(evaluator.extract(key), evaluator.extract(value))], | 
 |               isConst: true))); | 
 |       return null; | 
 |     } else { | 
 |       return addConstant(key, value, element.key, element.value); | 
 |     } | 
 |   } | 
 |  | 
 |   /// Returns [null] on success and an error-"constant" on failure, as such the | 
 |   /// return value should be checked. | 
 |   AbortConstant addSpread(Expression spreadExpression) { | 
 |     Constant constant = evaluator._evaluateSubexpression(spreadExpression); | 
 |     if (constant is AbortConstant) return constant; | 
 |     Constant spread = evaluator.unlower(constant); | 
 |     if (evaluator.shouldBeUnevaluated) { | 
 |       // Unevaluated spread | 
 |       parts.add(spread); | 
 |     } else if (spread == evaluator.nullConstant) { | 
 |       // Null spread | 
 |       return evaluator.createErrorConstant( | 
 |           spreadExpression, messageConstEvalNullValue); | 
 |     } else { | 
 |       // Fully evaluated spread | 
 |       if (spread is MapConstant) { | 
 |         for (ConstantMapEntry entry in spread.entries) { | 
 |           AbortConstant error = addConstant( | 
 |               entry.key, entry.value, spreadExpression, spreadExpression); | 
 |           if (error != null) return error; | 
 |         } | 
 |       } else { | 
 |         // Not map in spread | 
 |         return evaluator.createErrorConstant( | 
 |             spreadExpression, messageConstEvalNotMapInSpread); | 
 |       } | 
 |     } | 
 |     return null; | 
 |   } | 
 |  | 
 |   /// Returns [null] on success and an error-"constant" on failure, as such the | 
 |   /// return value should be checked. | 
 |   AbortConstant addConstant(Constant key, Constant value, TreeNode keyContext, | 
 |       TreeNode valueContext) { | 
 |     List<ConstantMapEntry> lastPart; | 
 |     if (parts.last is List<ConstantMapEntry>) { | 
 |       lastPart = parts.last; | 
 |     } else { | 
 |       // Probably unreachable. | 
 |       parts.add(lastPart = <ConstantMapEntry>[]); | 
 |     } | 
 |     if (!evaluator.hasPrimitiveEqual(key)) { | 
 |       return evaluator.createErrorConstant( | 
 |           keyContext, | 
 |           templateConstEvalKeyImplementsEqual.withArguments( | 
 |               key, evaluator.isNonNullableByDefault)); | 
 |     } | 
 |     bool unseenKey = seenKeys.add(key); | 
 |     if (!unseenKey) { | 
 |       return evaluator.createErrorConstant( | 
 |           keyContext, | 
 |           templateConstEvalDuplicateKey.withArguments( | 
 |               key, evaluator.isNonNullableByDefault)); | 
 |     } | 
 |     if (evaluator.evaluationMode == EvaluationMode.agnostic) { | 
 |       Constant weakKey = evaluator._weakener.visitConstant(key) ?? key; | 
 |       bool weakUnseenKey = weakSeenKeys.add(weakKey); | 
 |       if (unseenKey != weakUnseenKey) { | 
 |         return evaluator.createErrorConstant( | 
 |             keyContext, messageNonAgnosticConstant); | 
 |       } | 
 |     } | 
 |     Constant key2 = evaluator.ensureIsSubtype(key, keyType, keyContext); | 
 |     if (key2 is AbortConstant) return key2; | 
 |     Constant value2 = evaluator.ensureIsSubtype(value, valueType, valueContext); | 
 |     if (value2 is AbortConstant) return value2; | 
 |     lastPart.add(new ConstantMapEntry(key2, value2)); | 
 |     return null; | 
 |   } | 
 |  | 
 |   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)); | 
 |   } | 
 | } |