Compute constant constructors in resolution.

BUG=
R=sigurdm@google.com

Review URL: https://codereview.chromium.org//1218793002.
This commit is contained in:
Johnni Winther 2015-06-29 12:56:06 +02:00
parent 3b34c17909
commit 74d04a6a48
7 changed files with 522 additions and 124 deletions

View file

@ -18,6 +18,7 @@ import '../elements/elements.dart' show
import '../resolution/operators.dart'; import '../resolution/operators.dart';
import '../tree/tree.dart' show DartString; import '../tree/tree.dart' show DartString;
import '../universe/universe.dart' show CallStructure; import '../universe/universe.dart' show CallStructure;
import '../util/util.dart';
import 'values.dart'; import 'values.dart';
enum ConstantExpressionKind { enum ConstantExpressionKind {
@ -141,6 +142,23 @@ class GenerativeConstantConstructor implements ConstantConstructor{
return appliedFieldMap; return appliedFieldMap;
} }
int get hashCode {
int hash = Hashing.objectHash(type);
hash = Hashing.mapHash(defaultValues, hash);
hash = Hashing.mapHash(fieldMap, hash);
return Hashing.objectHash(superConstructorInvocation, hash);
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! GenerativeConstantConstructor) return false;
return
type == other.type &&
superConstructorInvocation == other.superConstructorInvocation &&
mapEquals(defaultValues, other.defaultValues) &&
mapEquals(fieldMap, other.fieldMap);
}
String toString() { String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.write("{'type': $type"); sb.write("{'type': $type");
@ -157,6 +175,16 @@ class GenerativeConstantConstructor implements ConstantConstructor{
return sb.toString(); return sb.toString();
} }
static bool mapEquals(Map map1, Map map2) {
if (map1.length != map1.length) return false;
for (var key in map1.keys) {
if (map1[key] != map2[key]) {
return false;
}
}
return true;
}
/// Creates the field-to-constant map from applying [args] to /// Creates the field-to-constant map from applying [args] to
/// [constructorInvocation]. If [constructorInvocation] is `null`, an empty /// [constructorInvocation]. If [constructorInvocation] is `null`, an empty
/// map is created. /// map is created.
@ -205,6 +233,20 @@ class RedirectingGenerativeConstantConstructor implements ConstantConstructor {
return appliedFieldMap; return appliedFieldMap;
} }
int get hashCode {
int hash = Hashing.objectHash(thisConstructorInvocation);
return Hashing.mapHash(defaultValues, hash);
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! RedirectingGenerativeConstantConstructor) return false;
return
thisConstructorInvocation == other.thisConstructorInvocation &&
GenerativeConstantConstructor.mapEquals(
defaultValues, other.defaultValues);
}
String toString() { String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.write("{'type': ${thisConstructorInvocation.type}"); sb.write("{'type': ${thisConstructorInvocation.type}");
@ -240,6 +282,16 @@ class RedirectingFactoryConstantConstructor implements ConstantConstructor {
return constantConstructor.computeInstanceFields(arguments, callStructure); return constantConstructor.computeInstanceFields(arguments, callStructure);
} }
int get hashCode {
return Hashing.objectHash(targetConstructorInvocation);
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! RedirectingFactoryConstantConstructor) return false;
return targetConstructorInvocation == other.targetConstructorInvocation;
}
String toString() { String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.write("{"); sb.write("{");

View file

@ -8,6 +8,7 @@ import 'common.dart';
import 'elements.dart'; import 'elements.dart';
import '../constants/expressions.dart'; import '../constants/expressions.dart';
import '../constants/constructors.dart'; import '../constants/constructors.dart';
import '../helpers/helpers.dart';
import '../tree/tree.dart'; import '../tree/tree.dart';
import '../util/util.dart'; import '../util/util.dart';
import '../resolution/resolution.dart'; import '../resolution/resolution.dart';
@ -1248,16 +1249,16 @@ class VariableList implements DeclarationSite {
} }
abstract class ConstantVariableMixin implements VariableElement { abstract class ConstantVariableMixin implements VariableElement {
ConstantExpression _constant; ConstantExpression constantCache;
ConstantExpression get constant { ConstantExpression get constant {
if (isPatch) { if (isPatch) {
ConstantVariableMixin originVariable = origin; ConstantVariableMixin originVariable = origin;
return originVariable.constant; return originVariable.constant;
} }
assert(invariant(this, _constant != null, assert(invariant(this, constantCache != null,
message: "Constant has not been computed for $this.")); message: "Constant has not been computed for $this."));
return _constant; return constantCache;
} }
void set constant(ConstantExpression value) { void set constant(ConstantExpression value) {
@ -1266,9 +1267,9 @@ abstract class ConstantVariableMixin implements VariableElement {
originVariable.constant = value; originVariable.constant = value;
return null; return null;
} }
assert(invariant(this, _constant == null || _constant == value, assert(invariant(this, constantCache == null || constantCache == value,
message: "Constant has already been computed for $this.")); message: "Constant has already been computed for $this."));
_constant = value; constantCache = value;
} }
} }
@ -1966,6 +1967,25 @@ abstract class ConstantConstructorMixin implements ConstructorElement {
return _constantConstructor; return _constantConstructor;
} }
void set constantConstructor(ConstantConstructor value) {
if (isPatch) {
ConstantConstructorMixin originConstructor = origin;
originConstructor.constantConstructor = value;
} else {
assert(invariant(this, isConst,
message: "Constant constructor set on non-constant "
"constructor $this."));
assert(invariant(this, !isFromEnvironmentConstructor,
message: "Constant constructor set on fromEnvironment "
"constructor: $this."));
assert(invariant(this,
_constantConstructor == null || _constantConstructor == value,
message: "Constant constructor already computed for $this:"
"Existing: $_constantConstructor, new: $value"));
_constantConstructor = value;
}
}
bool get isFromEnvironmentConstructor { bool get isFromEnvironmentConstructor {
return name == 'fromEnvironment' && return name == 'fromEnvironment' &&
library.isDartCore && library.isDartCore &&

View file

@ -6,12 +6,18 @@ part of resolution;
class InitializerResolver { class InitializerResolver {
final ResolverVisitor visitor; final ResolverVisitor visitor;
final Map<Element, Node> initialized; final ConstructorElementX constructor;
final FunctionExpression functionNode;
final Map<FieldElement, Node> initialized = <FieldElement, Node>{};
final Map<FieldElement, ConstantExpression> fieldInitializers =
<FieldElement, ConstantExpression>{};
Link<Node> initializers; Link<Node> initializers;
bool hasSuper; bool hasSuper = false;
bool isValidAsConstant = true;
InitializerResolver(this.visitor) bool get isConst => constructor.isConst;
: initialized = new Map<Element, Node>(), hasSuper = false;
InitializerResolver(this.visitor, this.constructor, this.functionNode);
ResolutionRegistry get registry => visitor.registry; ResolutionRegistry get registry => visitor.registry;
@ -37,6 +43,7 @@ class InitializerResolver {
visitor.compiler.reportInfo( visitor.compiler.reportInfo(
existing, existing,
MessageKind.ALREADY_INITIALIZED, {'fieldName': field.name}); MessageKind.ALREADY_INITIALIZED, {'fieldName': field.name});
isValidAsConstant = false;
} }
void checkForDuplicateInitializers(FieldElementX field, Node init) { void checkForDuplicateInitializers(FieldElementX field, Node init) {
@ -54,12 +61,13 @@ class InitializerResolver {
initialized[field] = init; initialized[field] = init;
} }
void resolveFieldInitializer(FunctionElement constructor, SendSet init) { void resolveFieldInitializer(SendSet init) {
// init is of the form [this.]field = value. // init is of the form [this.]field = value.
final Node selector = init.selector; final Node selector = init.selector;
final String name = selector.asIdentifier().source; final String name = selector.asIdentifier().source;
// Lookup target field. // Lookup target field.
Element target; Element target;
FieldElement field;
if (isFieldInitializer(init)) { if (isFieldInitializer(init)) {
target = constructor.enclosingClass.lookupLocalMember(name); target = constructor.enclosingClass.lookupLocalMember(name);
if (target == null) { if (target == null) {
@ -72,6 +80,8 @@ class InitializerResolver {
selector.asIdentifier(), constructor.enclosingClass); selector.asIdentifier(), constructor.enclosingClass);
} else if (!target.isInstanceMember) { } else if (!target.isInstanceMember) {
error(selector, MessageKind.INIT_STATIC_FIELD, {'fieldName': name}); error(selector, MessageKind.INIT_STATIC_FIELD, {'fieldName': name});
} else {
field = target;
} }
} else { } else {
error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER); error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER);
@ -80,38 +90,44 @@ class InitializerResolver {
registry.registerStaticUse(target); registry.registerStaticUse(target);
checkForDuplicateInitializers(target, init); checkForDuplicateInitializers(target, init);
// Resolve initializing value. // Resolve initializing value.
visitor.visitInStaticContext(init.arguments.head); ResolutionResult result = visitor.visitInStaticContext(
} init.arguments.head,
inConstantInitializer: isConst);
ClassElement getSuperOrThisLookupTarget(FunctionElement constructor, if (isConst) {
bool isSuperCall, if (result.isConstant && field != null) {
Node diagnosticNode) { // TODO(johnniwinther): Report error if `result.constant` is `null`.
ClassElement lookupTarget = constructor.enclosingClass; fieldInitializers[field] = result.constant;
if (isSuperCall) {
// Calculate correct lookup target and constructor name.
if (identical(lookupTarget, visitor.compiler.objectClass)) {
error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
} else { } else {
return lookupTarget.supertype.element; isValidAsConstant = false;
} }
} }
return lookupTarget;
} }
Element resolveSuperOrThisForSend(FunctionElement constructor, InterfaceType getSuperOrThisLookupTarget(Node diagnosticNode,
FunctionExpression functionNode, {bool isSuperCall}) {
Send call) { if (isSuperCall) {
// Resolve the selector and the arguments. // Calculate correct lookup target and constructor name.
visitor.inStaticContext(() { if (identical(constructor.enclosingClass, visitor.compiler.objectClass)) {
visitor.resolveSelector(call, null); error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
visitor.resolveArguments(call.argumentsNode); isValidAsConstant = false;
}); } else {
Selector selector = registry.getSelector(call); return constructor.enclosingClass.supertype;
bool isSuperCall = Initializers.isSuperConstructorCall(call); }
}
return constructor.enclosingClass.thisType;
}
ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor, ResolutionResult resolveSuperOrThisForSend(Send call) {
isSuperCall, // Resolve the selector and the arguments.
call); ArgumentsResult argumentsResult = visitor.inStaticContext(() {
visitor.resolveSelector(call, null);
return visitor.resolveArguments(call.argumentsNode);
}, inConstantInitializer: isConst);
bool isSuperCall = Initializers.isSuperConstructorCall(call);
InterfaceType targetType =
getSuperOrThisLookupTarget(call, isSuperCall: isSuperCall);
ClassElement lookupTarget = targetType.element;
Selector constructorSelector = Selector constructorSelector =
visitor.getRedirectingThisOrSuperConstructorSelector(call); visitor.getRedirectingThisOrSuperConstructorSelector(call);
FunctionElement calledConstructor = FunctionElement calledConstructor =
@ -119,9 +135,8 @@ class InitializerResolver {
final bool isImplicitSuperCall = false; final bool isImplicitSuperCall = false;
final String className = lookupTarget.name; final String className = lookupTarget.name;
verifyThatConstructorMatchesCall(constructor, verifyThatConstructorMatchesCall(calledConstructor,
calledConstructor, argumentsResult.callStructure,
selector.callStructure,
isImplicitSuperCall, isImplicitSuperCall,
call, call,
className, className,
@ -129,11 +144,28 @@ class InitializerResolver {
registry.useElement(call, calledConstructor); registry.useElement(call, calledConstructor);
registry.registerStaticUse(calledConstructor); registry.registerStaticUse(calledConstructor);
return calledConstructor; if (isConst) {
if (isValidAsConstant &&
calledConstructor.isConst &&
argumentsResult.isValidAsConstant) {
CallStructure callStructure = argumentsResult.callStructure;
List<ConstantExpression> arguments = argumentsResult.constantArguments;
return new ConstantResult(
call,
new ConstructedConstantExpression(
targetType,
calledConstructor,
callStructure,
arguments),
element: calledConstructor);
} else {
isValidAsConstant = false;
}
}
return new ResolutionResult.forElement(calledConstructor);
} }
void resolveImplicitSuperConstructorSend(FunctionElement constructor, ConstructedConstantExpression resolveImplicitSuperConstructorSend() {
FunctionExpression functionNode) {
// If the class has a super resolve the implicit super call. // If the class has a super resolve the implicit super call.
ClassElement classElement = constructor.enclosingClass; ClassElement classElement = constructor.enclosingClass;
ClassElement superClass = classElement.superclass; ClassElement superClass = classElement.superclass;
@ -141,18 +173,16 @@ class InitializerResolver {
assert(superClass != null); assert(superClass != null);
assert(superClass.isResolved); assert(superClass.isResolved);
final bool isSuperCall = true; InterfaceType targetType =
ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor, getSuperOrThisLookupTarget(functionNode, isSuperCall: true);
isSuperCall, ClassElement lookupTarget = targetType.element;
functionNode);
Selector constructorSelector = new Selector.callDefaultConstructor(); Selector constructorSelector = new Selector.callDefaultConstructor();
Element calledConstructor = lookupTarget.lookupConstructor( Element calledConstructor = lookupTarget.lookupConstructor(
constructorSelector.name); constructorSelector.name);
final String className = lookupTarget.name; final String className = lookupTarget.name;
final bool isImplicitSuperCall = true; final bool isImplicitSuperCall = true;
verifyThatConstructorMatchesCall(constructor, verifyThatConstructorMatchesCall(calledConstructor,
calledConstructor,
CallStructure.NO_ARGS, CallStructure.NO_ARGS,
isImplicitSuperCall, isImplicitSuperCall,
functionNode, functionNode,
@ -160,11 +190,19 @@ class InitializerResolver {
constructorSelector); constructorSelector);
registry.registerImplicitSuperCall(calledConstructor); registry.registerImplicitSuperCall(calledConstructor);
registry.registerStaticUse(calledConstructor); registry.registerStaticUse(calledConstructor);
if (isConst && isValidAsConstant) {
return new ConstructedConstantExpression(
targetType,
calledConstructor,
CallStructure.NO_ARGS,
const <ConstantExpression>[]);
}
} }
return null;
} }
void verifyThatConstructorMatchesCall( void verifyThatConstructorMatchesCall(
FunctionElement caller,
ConstructorElementX lookedupConstructor, ConstructorElementX lookedupConstructor,
CallStructure call, CallStructure call,
bool isImplicitSuperCall, bool isImplicitSuperCall,
@ -181,6 +219,7 @@ class InitializerResolver {
: MessageKind.CANNOT_RESOLVE_CONSTRUCTOR; : MessageKind.CANNOT_RESOLVE_CONSTRUCTOR;
visitor.compiler.reportError( visitor.compiler.reportError(
diagnosticNode, kind, {'constructorName': fullConstructorName}); diagnosticNode, kind, {'constructorName': fullConstructorName});
isValidAsConstant = false;
} else { } else {
lookedupConstructor.computeSignature(visitor.compiler); lookedupConstructor.computeSignature(visitor.compiler);
if (!call.signatureApplies(lookedupConstructor.functionSignature)) { if (!call.signatureApplies(lookedupConstructor.functionSignature)) {
@ -188,12 +227,14 @@ class InitializerResolver {
? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT ? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT
: MessageKind.NO_MATCHING_CONSTRUCTOR; : MessageKind.NO_MATCHING_CONSTRUCTOR;
visitor.compiler.reportError(diagnosticNode, kind); visitor.compiler.reportError(diagnosticNode, kind);
} else if (caller.isConst isValidAsConstant = false;
} else if (constructor.isConst
&& !lookedupConstructor.isConst) { && !lookedupConstructor.isConst) {
MessageKind kind = isImplicitSuperCall MessageKind kind = isImplicitSuperCall
? MessageKind.CONST_CALLS_NON_CONST_FOR_IMPLICIT ? MessageKind.CONST_CALLS_NON_CONST_FOR_IMPLICIT
: MessageKind.CONST_CALLS_NON_CONST; : MessageKind.CONST_CALLS_NON_CONST;
visitor.compiler.reportError(diagnosticNode, kind); visitor.compiler.reportError(diagnosticNode, kind);
isValidAsConstant = false;
} }
} }
} }
@ -202,16 +243,50 @@ class InitializerResolver {
* Resolve all initializers of this constructor. In the case of a redirecting * Resolve all initializers of this constructor. In the case of a redirecting
* constructor, the resolved constructor's function element is returned. * constructor, the resolved constructor's function element is returned.
*/ */
ConstructorElement resolveInitializers(ConstructorElementX constructor, ConstructorElement resolveInitializers() {
FunctionExpression functionNode) { Map<dynamic/*String|int*/, ConstantExpression> defaultValues =
<dynamic/*String|int*/, ConstantExpression>{};
ConstructedConstantExpression constructorInvocation;
// Keep track of all "this.param" parameters specified for constructor so // Keep track of all "this.param" parameters specified for constructor so
// that we can ensure that fields are initialized only once. // that we can ensure that fields are initialized only once.
FunctionSignature functionParameters = constructor.functionSignature; FunctionSignature functionParameters = constructor.functionSignature;
functionParameters.forEachParameter((ParameterElement element) { functionParameters.forEachParameter((ParameterElementX element) {
if (isConst) {
if (element.isOptional) {
if (element.constantCache == null) {
// TODO(johnniwinther): Remove this when all constant expressions
// can be computed during resolution.
isValidAsConstant = false;
} else {
ConstantExpression defaultValue = element.constant;
if (defaultValue != null) {
if (element.isNamed) {
defaultValues[element.name] = defaultValue;
} else {
int index =
element.functionDeclaration.parameters.indexOf(element);
defaultValues[index] = defaultValue;
}
} else {
isValidAsConstant = false;
}
}
}
}
if (element.isInitializingFormal) { if (element.isInitializingFormal) {
InitializingFormalElement initializingFormal = element; InitializingFormalElementX initializingFormal = element;
checkForDuplicateInitializers(initializingFormal.fieldElement, FieldElement field = initializingFormal.fieldElement;
element.initializer); checkForDuplicateInitializers(field, element.initializer);
if (isConst) {
if (element.isNamed) {
fieldInitializers[field] = new NamedArgumentReference(element.name);
} else {
int index = element.functionDeclaration.parameters.indexOf(element);
fieldInitializers[field] = new PositionalArgumentReference(index);
}
} else {
isValidAsConstant = false;
}
} }
}); });
@ -224,7 +299,7 @@ class InitializerResolver {
for (Link<Node> link = initializers; !link.isEmpty; link = link.tail) { for (Link<Node> link = initializers; !link.isEmpty; link = link.tail) {
if (link.head.asSendSet() != null) { if (link.head.asSendSet() != null) {
final SendSet init = link.head.asSendSet(); final SendSet init = link.head.asSendSet();
resolveFieldInitializer(constructor, init); resolveFieldInitializer(init);
} else if (link.head.asSend() != null) { } else if (link.head.asSend() != null) {
final Send call = link.head.asSend(); final Send call = link.head.asSend();
if (call.argumentsNode == null) { if (call.argumentsNode == null) {
@ -235,7 +310,14 @@ class InitializerResolver {
if (resolvedSuper) { if (resolvedSuper) {
error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER); error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
} }
resolveSuperOrThisForSend(constructor, functionNode, call); ResolutionResult result = resolveSuperOrThisForSend(call);
if (isConst) {
if (result.isConstant) {
constructorInvocation = result.constant;
} else {
isValidAsConstant = false;
}
}
resolvedSuper = true; resolvedSuper = true;
} else if (Initializers.isConstructorRedirect(call)) { } else if (Initializers.isConstructorRedirect(call)) {
// Check that there is no body (Language specification 7.5.1). If the // Check that there is no body (Language specification 7.5.1). If the
@ -256,9 +338,24 @@ class InitializerResolver {
if (parameter.isInitializingFormal) { if (parameter.isInitializingFormal) {
Node node = parameter.node; Node node = parameter.node;
error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED); error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED);
isValidAsConstant = false;
} }
}); });
return resolveSuperOrThisForSend(constructor, functionNode, call); ResolutionResult result = resolveSuperOrThisForSend(call);
if (isConst) {
if (result.isConstant) {
constructorInvocation = result.constant;
} else {
isValidAsConstant = false;
}
if (isConst && isValidAsConstant) {
constructor.constantConstructor =
new RedirectingGenerativeConstantConstructor(
defaultValues,
constructorInvocation);
}
}
return result.element;
} else { } else {
visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED); visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
return null; return null;
@ -268,7 +365,14 @@ class InitializerResolver {
} }
} }
if (!resolvedSuper) { if (!resolvedSuper) {
resolveImplicitSuperConstructorSend(constructor, functionNode); constructorInvocation = resolveImplicitSuperConstructorSend();
}
if (isConst && isValidAsConstant) {
constructor.constantConstructor = new GenerativeConstantConstructor(
constructor.enclosingClass.thisType,
defaultValues,
fieldInitializers,
constructorInvocation);
} }
return null; // If there was no redirection always return null. return null; // If there was no redirection always return null.
} }

View file

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

View file

@ -160,9 +160,9 @@ class ResolverTask extends CompilerTask {
if (element.isGenerativeConstructor) { if (element.isGenerativeConstructor) {
// Even if there is no initializer list we still have to do the // Even if there is no initializer list we still have to do the
// resolution in case there is an implicit super constructor call. // resolution in case there is an implicit super constructor call.
InitializerResolver resolver = new InitializerResolver(visitor); InitializerResolver resolver =
FunctionElement redirection = new InitializerResolver(visitor, element, tree);
resolver.resolveInitializers(element, tree); FunctionElement redirection = resolver.resolveInitializers();
if (redirection != null) { if (redirection != null) {
resolveRedirectingConstructor(resolver, tree, element, redirection); resolveRedirectingConstructor(resolver, tree, element, redirection);
} }

View file

@ -23,6 +23,7 @@ abstract class ResolutionResult {
} }
ResultKind get kind; ResultKind get kind;
Node get node => null;
Element get element => null; Element get element => null;
DartType get type => null; DartType get type => null;
ConstantExpression get constant => null; ConstantExpression get constant => null;
@ -64,11 +65,16 @@ class AssertResult extends ResolutionResult {
String toString() => 'AssertResult()'; String toString() => 'AssertResult()';
} }
/// The result for resolving a constant expression.
class ConstantResult extends ResolutionResult { class ConstantResult extends ResolutionResult {
final Node node; final Node node;
final ConstantExpression constant; final ConstantExpression constant;
final Element element;
ConstantResult(this.node, this.constant); /// Creates a result for the [constant] expression. [node] is provided for
/// error reporting on the constant and [element] is provided if the
/// expression additionally serves an [Element] like [ElementResult].
ConstantResult(this.node, this.constant, {this.element});
bool get isConstant => true; bool get isConstant => true;
@ -83,4 +89,31 @@ class NoneResult extends ResolutionResult {
ResultKind get kind => ResultKind.NONE; ResultKind get kind => ResultKind.NONE;
String toString() => 'NoneResult()'; String toString() => 'NoneResult()';
}
/// The result of resolving a list of arguments.
class ArgumentsResult {
/// The call structure of the arguments.
final CallStructure callStructure;
/// The resolutions results for each argument.
final List<ResolutionResult> argumentResults;
/// `true` if the arguments are valid as arguments to a constructed constant
/// expression.
final bool isValidAsConstant;
ArgumentsResult(
this.callStructure,
this.argumentResults,
{this.isValidAsConstant});
/// Returns the list of [ConstantExpression]s for each of the arguments. If
/// [isValidAsConstant] is `false`, `null` is returned.
List<ConstantExpression> get constantArguments {
if (!isValidAsConstant) return null;
return argumentResults.map((ResolutionResult result) {
return result.constant;
}).toList();
}
} }

View file

@ -686,7 +686,7 @@ Future resolveConstructor(
new ResolverVisitor(compiler, element, new ResolverVisitor(compiler, element,
new ResolutionRegistry.internal(compiler, new ResolutionRegistry.internal(compiler,
new CollectingTreeElements(element))); new CollectingTreeElements(element)));
new InitializerResolver(visitor).resolveInitializers(element, tree); new InitializerResolver(visitor, element, tree).resolveInitializers();
visitor.visit(tree.body); visitor.visit(tree.body);
Expect.equals(expectedElementCount, map(visitor).length, Expect.equals(expectedElementCount, map(visitor).length,
"${map(visitor).values} for '$statement' in context of `$script`"); "${map(visitor).values} for '$statement' in context of `$script`");