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