Chromium Code Reviews| Index: pkg/compiler/lib/src/resolution/members.dart |
| diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart |
| index feb8df12e7c7f208b98aa8f1dbadbea0192321ea..7c1bdf96f2a6df8cb5f84d4f9b3aad3898edabe4 100644 |
| --- a/pkg/compiler/lib/src/resolution/members.dart |
| +++ b/pkg/compiler/lib/src/resolution/members.dart |
| @@ -4,6 +4,24 @@ |
| part of resolution; |
| +/// The state of constants in resolutions. |
| +enum ConstantState { |
| + /// Expressions are not required to be constants. |
| + NON_CONSTANT, |
|
sigurdm
2015/06/29 10:06:10
Are we still using all caps for constnat values?
Johnni Winther
2015/06/29 10:47:50
For now, at least.
|
| + |
| + /// Expressions are required to be constants. |
| + /// |
| + /// For instance the values of a constant list literal. |
| + CONSTANT, |
| + |
| + /// Expressions are required to be constants and parameter references are |
| + /// also considered constant. |
| + /// |
| + /// This is used for resolving constructor initializers of constant |
| + /// constructors. |
| + CONSTANT_INITIALIZER, |
| +} |
| + |
| /** |
| * Core implementation of resolution. |
| * |
| @@ -23,6 +41,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| bool inInstanceContext; |
| bool inCheckContext; |
| bool inCatchBlock; |
| + ConstantState constantState; |
| Scope scope; |
| ClassElement currentClass; |
| @@ -100,6 +119,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| !element.isTypedef && |
| !element.enclosingElement.isTypedef, |
| inCatchBlock = false, |
| + constantState = element.isConst |
| + ? ConstantState.CONSTANT : ConstantState.NON_CONSTANT, |
| super(compiler, registry); |
| CoreTypes get coreTypes => compiler.coreTypes; |
| @@ -153,23 +174,54 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| return result; |
| } |
| - inStaticContext(action()) { |
| + doInPromotionScope(Node node, action()) { |
| + promotionScope = promotionScope.prepend(node); |
| + var result = action(); |
| + promotionScope = promotionScope.tail; |
| + return result; |
| + } |
| + |
| + inStaticContext(action(), |
| + {bool inConstantInitializer: false}) { |
| bool wasInstanceContext = inInstanceContext; |
| + ConstantState oldConstantState = constantState; |
| + constantState = inConstantInitializer |
| + ? ConstantState.CONSTANT_INITIALIZER |
| + : constantState; |
| inInstanceContext = false; |
| var result = action(); |
| inInstanceContext = wasInstanceContext; |
| + constantState = oldConstantState; |
| return result; |
| } |
| - doInPromotionScope(Node node, action()) { |
| - promotionScope = promotionScope.prepend(node); |
| + ResolutionResult visitInStaticContext(Node node, |
| + {bool inConstantInitializer: false}) { |
| + return inStaticContext( |
| + () => visit(node), |
| + inConstantInitializer: inConstantInitializer); |
| + } |
| + |
| + /// Execute [action] where the constant state is `ConstantState.CONSTANT` if |
| + /// not already `ConstantState.CONSTANT_INITIALIZER`. |
| + inConstantContext(action()) { |
| + ConstantState oldConstantState = constantState; |
| + if (constantState != ConstantState.CONSTANT_INITIALIZER) { |
| + constantState = ConstantState.CONSTANT; |
| + } |
| var result = action(); |
| - promotionScope = promotionScope.tail; |
| + constantState = oldConstantState; |
| return result; |
| } |
| - ResolutionResult visitInStaticContext(Node node) { |
| - return inStaticContext(() => visit(node)); |
| + /// Visit [node] where the constant state is `ConstantState.CONSTANT` if |
| + /// not already `ConstantState.CONSTANT_INITIALIZER`. |
| + ResolutionResult visitInConstantContext(Node node) { |
| + ResolutionResult result = inConstantContext(() => visit(node)); |
| + assert(invariant(node, result != null, |
| + message: "No resolution result for $node.")); |
| + |
| + return result; |
| } |
| ErroneousElement reportAndCreateErroneousElement( |
| @@ -324,7 +376,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| FunctionSignature functionParameters = function.functionSignature; |
| Link<Node> parameterNodes = (node.parameters == null) |
| ? const Link<Node>() : node.parameters.nodes; |
| - functionParameters.forEachParameter((ParameterElement element) { |
| + functionParameters.forEachParameter((ParameterElementX element) { |
| // TODO(karlklose): should be a list of [FormalElement]s, but the actual |
| // implementation uses [Element]. |
| List<Element> optionals = functionParameters.optionalParameters; |
| @@ -332,7 +384,16 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| NodeList nodes = parameterNodes.head; |
| parameterNodes = nodes.nodes; |
| } |
| - visit(element.initializer); |
| + if (element.isOptional) { |
| + if (element.initializer != null) { |
| + ResolutionResult result = visitInConstantContext(element.initializer); |
| + if (result.isConstant) { |
| + element.constant = result.constant; |
| + } |
| + } else { |
| + element.constant = new NullConstantExpression(); |
| + } |
| + } |
| VariableDefinitions variableDefinitions = parameterNodes.head; |
| Node parameterNode = variableDefinitions.definitions.nodes.head; |
| // Field parameters (this.x) are not visible inside the constructor. The |
| @@ -340,7 +401,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| if (element.isInitializingFormal) { |
| registry.useElement(parameterNode, element); |
| } else { |
| - LocalParameterElement parameterElement = element; |
| + LocalParameterElementX parameterElement = element; |
| defineLocalVariable(parameterNode, parameterElement); |
| addToScope(parameterElement); |
| } |
| @@ -715,8 +776,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| return selector; |
| } |
| - CallStructure resolveArguments(NodeList list) { |
| + ArgumentsResult resolveArguments(NodeList list) { |
| if (list == null) return null; |
| + bool isValidAsConstant = true; |
| + List<ResolutionResult> argumentResults = <ResolutionResult>[]; |
| bool oldSendIsMemberAccess = sendIsMemberAccess; |
| sendIsMemberAccess = false; |
| Map<String, Node> seenNamedArguments = new Map<String, Node>(); |
| @@ -724,7 +787,12 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| List<String> namedArguments = <String>[]; |
| for (Link<Node> link = list.nodes; !link.isEmpty; link = link.tail) { |
| Expression argument = link.head; |
| - visit(argument); |
| + ResolutionResult result = visit(argument); |
| + if (!result.isConstant) { |
| + isValidAsConstant = false; |
| + } |
| + argumentResults.add(result); |
| + |
| NamedArgument namedArgument = argument.asNamedArgument(); |
| if (namedArgument != null) { |
| String source = namedArgument.name.source; |
| @@ -734,16 +802,21 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| source, |
| argument, |
| seenNamedArguments[source]); |
| + isValidAsConstant = false; |
| } else { |
| seenNamedArguments[source] = namedArgument; |
| } |
| } else if (!seenNamedArguments.isEmpty) { |
| error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED); |
| + isValidAsConstant = false; |
| } |
| argumentCount++; |
| } |
| sendIsMemberAccess = oldSendIsMemberAccess; |
| - return new CallStructure(argumentCount, namedArguments); |
| + return new ArgumentsResult( |
| + new CallStructure(argumentCount, namedArguments), |
| + argumentResults, |
| + isValidAsConstant: isValidAsConstant); |
| } |
| void registerTypeLiteralAccess(Send node, Element target) { |
| @@ -1380,7 +1453,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| message: "Unexpected expression: $node")); |
| Node expression = node.selector; |
| visitExpression(expression); |
| - CallStructure callStructure = resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| Selector selector = callStructure.callSelector; |
| // TODO(johnniwinther): Remove this when all information goes through the |
| // [SendStructure]. |
| @@ -1398,7 +1472,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| // If this send is of the form "assert(expr);", then |
| // this is an assertion. |
| - CallStructure callStructure = resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| SendStructure sendStructure = const AssertStructure(); |
| if (callStructure.argumentCount != 1) { |
| compiler.reportError( |
| @@ -1430,7 +1505,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| ResolutionResult handleThisAccess(Send node) { |
| AccessSemantics accessSemantics = new AccessSemantics.thisAccess(); |
| if (node.isCall) { |
| - CallStructure callStructure = resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| Selector selector = callStructure.callSelector; |
| // TODO(johnniwinther): Handle invalid this access as an |
| // [AccessSemantics]. |
| @@ -1456,7 +1532,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| Selector selector; |
| CallStructure callStructure = CallStructure.NO_ARGS; |
| if (node.isCall) { |
| - callStructure = resolveArguments(node.argumentsNode); |
| + callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| selector = new Selector(SelectorKind.CALL, name, callStructure); |
| } else { |
| selector = new Selector(SelectorKind.GETTER, name, callStructure); |
| @@ -1711,7 +1788,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| SendStructure sendStructure; |
| Selector selector; |
| if (node.isCall) { |
| - CallStructure callStructure = resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| selector = new Selector(SelectorKind.CALL, name, callStructure); |
| registry.registerDynamicInvocation( |
| new UniverseSelector(selector, null)); |
| @@ -1803,7 +1881,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| SendStructure sendStructure; |
| Selector selector; |
| if (node.isCall) { |
| - CallStructure callStructure = resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| selector = new Selector(SelectorKind.CALL, name, callStructure); |
| registry.registerDynamicInvocation( |
| new UniverseSelector(selector, null)); |
| @@ -1864,16 +1943,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| /// Handle access of a parameter, local variable or local function. |
| ResolutionResult handleLocalAccess(Send node, Name name, Element element) { |
| + ResolutionResult result = const NoneResult(); |
| AccessSemantics semantics = computeLocalAccessSemantics(node, element); |
| Selector selector; |
| - CallStructure callStructure = CallStructure.NO_ARGS; |
| if (node.isCall) { |
| - callStructure = resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = |
| + resolveArguments(node.argumentsNode).callStructure; |
| selector = new Selector(SelectorKind.CALL, name, callStructure); |
| - } else { |
| - selector = new Selector(SelectorKind.GETTER, name, callStructure); |
| - } |
| - if (node.isCall) { |
| bool isIncompatibleInvoke = false; |
| switch (semantics.kind) { |
| case AccessKind.LOCAL_FUNCTION: |
| @@ -1904,6 +1980,48 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| ? new IncompatibleInvokeStructure(semantics, selector) |
| : new InvokeStructure(semantics, selector)); |
| } else { |
| + switch (semantics.kind) { |
| + case AccessKind.LOCAL_VARIABLE: |
| + case AccessKind.LOCAL_FUNCTION: |
| + result = new ElementResult(element); |
| + break; |
| + case AccessKind.PARAMETER: |
| + case AccessKind.FINAL_PARAMETER: |
| + if (constantState == ConstantState.CONSTANT_INITIALIZER) { |
| + ParameterElement parameter = element; |
| + if (parameter.isNamed) { |
| + result = new ConstantResult( |
| + node, |
| + new NamedArgumentReference(parameter.name), |
| + element: element); |
| + } else { |
| + result = new ConstantResult( |
| + node, |
| + new PositionalArgumentReference( |
| + parameter.functionDeclaration.parameters.indexOf( |
| + parameter)), |
| + element: element); |
| + } |
| + } else { |
| + result = new ElementResult(element); |
| + } |
| + break; |
| + case AccessKind.FINAL_LOCAL_VARIABLE: |
| + if (element.isConst) { |
| + result = new ConstantResult( |
| + node, |
| + new VariableConstantExpression(element), |
| + element: element); |
| + } else { |
| + result = new ElementResult(element); |
| + } |
| + break; |
| + default: |
| + internalError(node, |
| + "Unexpected local access $semantics."); |
| + break; |
| + } |
| + selector = new Selector(SelectorKind.GETTER, name, CallStructure.NO_ARGS); |
| registry.registerSendStructure(node, |
| new GetStructure(semantics, selector)); |
| } |
| @@ -1915,14 +2033,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| registerPotentialAccessInClosure(node, element); |
| - return node.isPropertyAccess |
| - ? new ElementResult(element) : const NoneResult(); |
| + return result; |
| } |
| /// Handle access of a static or top level [element]. |
| ResolutionResult handleStaticOrTopLevelAccess( |
| Send node, Name name, Element element) { |
| - |
| + ResolutionResult result = const NoneResult(); |
| MemberElement member; |
| if (element.isAbstractField) { |
| AbstractFieldElement abstractField = element; |
| @@ -1939,16 +2056,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| member.computeType(compiler); |
| Selector selector; |
| - CallStructure callStructure = CallStructure.NO_ARGS; |
| - if (node.isCall) { |
| - callStructure = resolveArguments(node.argumentsNode); |
| - selector = new Selector(SelectorKind.CALL, name, callStructure); |
| - } else { |
| - selector = new Selector(SelectorKind.GETTER, name, callStructure); |
| - } |
| AccessSemantics semantics = |
| computeStaticOrTopLevelAccessSemantics(node, member); |
| if (node.isCall) { |
| + ArgumentsResult argumentsResult = |
| + resolveArguments(node.argumentsNode); |
| + CallStructure callStructure = argumentsResult.callStructure; |
| + selector = new Selector(SelectorKind.CALL, name, callStructure); |
| + |
| bool isIncompatibleInvoke = false; |
| switch (semantics.kind) { |
| case AccessKind.STATIC_METHOD: |
| @@ -1963,6 +2078,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } else { |
| registry.registerStaticUse(semantics.element); |
| handleForeignCall(node, semantics.element, selector); |
| + if (method == compiler.identicalFunction && |
| + argumentsResult.isValidAsConstant) { |
| + result = new ConstantResult(node, |
| + new IdenticalConstantExpression( |
| + argumentsResult.argumentResults[0].constant, |
| + argumentsResult.argumentResults[1].constant)); |
| + } |
| } |
| break; |
| case AccessKind.STATIC_FIELD: |
| @@ -1994,6 +2116,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| ? new IncompatibleInvokeStructure(semantics, selector) |
| : new InvokeStructure(semantics, selector)); |
| } else { |
| + selector = new Selector(SelectorKind.GETTER, name, CallStructure.NO_ARGS); |
| switch (semantics.kind) { |
| case AccessKind.STATIC_METHOD: |
| case AccessKind.TOPLEVEL_METHOD: |
| @@ -2025,6 +2148,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| registry.registerSendStructure(node, |
| new GetStructure(semantics, selector)); |
| + if (member.isConst) { |
| + FieldElement field = member; |
| + result = new ConstantResult( |
| + node, new VariableConstantExpression(field), element: field); |
| + } else { |
| + result = new ElementResult(member); |
| + } |
| } |
| // TODO(johnniwinther): Remove these when all information goes through |
| @@ -2032,8 +2162,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| registry.useElement(node, member); |
| registry.setSelector(node, selector); |
| - return node.isPropertyAccess |
| - ? new ElementResult(member) : const NoneResult(); |
| + return result; |
| } |
| /// Handle access to resolved [element]. |
| @@ -2535,6 +2664,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| ConstructorElementX constructor = enclosingElement; |
| bool isConstConstructor = constructor.isConst; |
| + bool isValidAsConstant = isConstConstructor; |
| ConstructorElement redirectionTarget = resolveRedirectingFactory( |
| node, inConstContext: isConstConstructor); |
| constructor.immediateRedirectionTarget = redirectionTarget; |
| @@ -2554,9 +2684,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| if (isConstConstructor && |
| !redirectionTarget.isConst) { |
| compiler.reportError(node, MessageKind.CONSTRUCTOR_IS_NOT_CONST); |
| + isValidAsConstant = false; |
| } |
| if (redirectionTarget == constructor) { |
| compiler.reportError(node, MessageKind.CYCLIC_REDIRECTING_FACTORY); |
| + // TODO(johnniwinther): Create constant constructor for this case and |
| + // let evaluation detect the cyclicity. |
| + isValidAsConstant = false; |
| } |
| } |
| @@ -2571,6 +2705,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| if (!isSubtype) { |
| warning(node, MessageKind.NOT_ASSIGNABLE, |
| {'fromType': targetType, 'toType': constructorType}); |
| + // TODO(johnniwinther): Handle this (potentially) erroneous case. |
| + isValidAsConstant = false; |
| } |
| redirectionTarget.computeType(compiler); |
| @@ -2580,6 +2716,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| if (!targetSignature.isCompatibleWith(constructorSignature)) { |
| assert(!isSubtype); |
| registry.registerThrowNoSuchMethod(); |
| + isValidAsConstant = false; |
| } |
| // Register a post process to check for cycles in the redirection chain and |
| @@ -2595,6 +2732,30 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| if (isSymbolConstructor) { |
| registry.registerSymbolConstructor(); |
| } |
| + if (isValidAsConstant) { |
| + List<String> names = <String>[]; |
| + List<ConstantExpression> arguments = <ConstantExpression>[]; |
| + int index = 0; |
| + constructorSignature.forEachParameter((ParameterElement parameter) { |
| + if (parameter.isNamed) { |
| + String name = parameter.name; |
| + names.add(name); |
| + arguments.add(new NamedArgumentReference(name)); |
| + } else { |
| + arguments.add(new PositionalArgumentReference(index)); |
| + } |
| + index++; |
| + }); |
| + CallStructure callStructure = |
| + new CallStructure(constructorSignature.parameterCount, names); |
| + constructor.constantConstructor = |
| + new RedirectingFactoryConstantConstructor( |
| + new ConstructedConstantExpression( |
| + type, |
| + redirectionTarget, |
| + callStructure, |
| + arguments)); |
| + } |
| return const NoneResult(); |
| } |
| @@ -2682,12 +2843,19 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| ResolutionResult visitNewExpression(NewExpression node) { |
| + bool isValidAsConstant = true; |
| FunctionElement constructor = resolveConstructor(node); |
| final bool isSymbolConstructor = constructor == compiler.symbolConstructor; |
| final bool isMirrorsUsedConstant = |
| node.isConst && (constructor == compiler.mirrorsUsedConstructor); |
| Selector callSelector = resolveSelector(node.send, constructor); |
| - resolveArguments(node.send.argumentsNode); |
| + ArgumentsResult argumentsResult; |
| + if (node.isConst) { |
| + argumentsResult = |
| + inConstantContext(() => resolveArguments(node.send.argumentsNode)); |
| + } else { |
| + argumentsResult = resolveArguments(node.send.argumentsNode); |
| + } |
| registry.useElement(node.send, constructor); |
| if (Elements.isUnresolved(constructor)) { |
| return new ResolutionResult.forElement(constructor); |
| @@ -2705,12 +2873,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| compiler.reportError(node, |
| MessageKind.CANNOT_INSTANTIATE_ENUM, |
| {'enumName': cls.name}); |
| + isValidAsConstant = false; |
| } |
| InterfaceType type = registry.getType(node); |
| if (node.isConst && type.containsTypeVariables) { |
| compiler.reportError(node.send.selector, |
| MessageKind.TYPE_VARIABLE_IN_CONSTANT); |
| + isValidAsConstant = false; |
| } |
| // TODO(johniwinther): Avoid registration of `type` in face of redirecting |
| // factory constructors. |
| @@ -2718,6 +2888,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| if (constructor.isGenerativeConstructor && cls.isAbstract) { |
| warning(node, MessageKind.ABSTRACT_CLASS_INSTANTIATION); |
| registry.registerAbstractClassInstantiation(); |
| + isValidAsConstant = false; |
| } |
| if (isSymbolConstructor) { |
| @@ -2752,6 +2923,19 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| if (node.isConst) { |
| analyzeConstantDeferred(node); |
| + if (isValidAsConstant && |
| + constructor.isConst && |
| + argumentsResult.isValidAsConstant) { |
| + CallStructure callStructure = argumentsResult.callStructure; |
| + List<ConstantExpression> arguments = argumentsResult.constantArguments; |
| + ConstructedConstantExpression constant = |
| + new ConstructedConstantExpression( |
| + type, |
| + constructor, |
| + callStructure, |
| + arguments); |
| + return new ConstantResult(node, constant); |
| + } |
| } |
| return const NoneResult(); |
| @@ -2898,14 +3082,16 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| registry.registerRequiredType(listType, enclosingElement); |
| if (node.isConst) { |
| List<ConstantExpression> constantExpressions = <ConstantExpression>[]; |
| - for (Node element in node.elements) { |
| - ResolutionResult elementResult = visit(element); |
| - if (isValidAsConstant && elementResult.isConstant) { |
| - constantExpressions.add(elementResult.constant); |
| - } else { |
| - isValidAsConstant = false; |
| + inConstantContext(() { |
| + for (Node element in node.elements) { |
| + ResolutionResult elementResult = visit(element); |
| + if (isValidAsConstant && elementResult.isConstant) { |
| + constantExpressions.add(elementResult.constant); |
| + } else { |
| + isValidAsConstant = false; |
| + } |
| } |
| - } |
| + }); |
| analyzeConstantDeferred(node); |
| sendIsMemberAccess = false; |
| if (isValidAsConstant) { |
| @@ -3197,20 +3383,23 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| registry.registerMapLiteral(node, mapType, node.isConst); |
| registry.registerRequiredType(mapType, enclosingElement); |
| if (node.isConst) { |
| + |
| List<ConstantExpression> keyExpressions = <ConstantExpression>[]; |
| List<ConstantExpression> valueExpressions = <ConstantExpression>[]; |
| - for (LiteralMapEntry entry in node.entries) { |
| - ResolutionResult keyResult = visit(entry.key); |
| - ResolutionResult valueResult = visit(entry.value); |
| - if (isValidAsConstant && |
| - keyResult.isConstant && |
| - valueResult.isConstant) { |
| - keyExpressions.add(keyResult.constant); |
| - valueExpressions.add(valueResult.constant); |
| - } else { |
| - isValidAsConstant = false; |
| + inConstantContext(() { |
| + for (LiteralMapEntry entry in node.entries) { |
| + ResolutionResult keyResult = visit(entry.key); |
| + ResolutionResult valueResult = visit(entry.value); |
| + if (isValidAsConstant && |
| + keyResult.isConstant && |
| + valueResult.isConstant) { |
| + keyExpressions.add(keyResult.constant); |
| + valueExpressions.add(valueResult.constant); |
| + } else { |
| + isValidAsConstant = false; |
| + } |
| } |
| - } |
| + }); |
| analyzeConstantDeferred(node); |
| sendIsMemberAccess = false; |
| if (isValidAsConstant) { |