1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 20:16:39 +00:00

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 '../tree/tree.dart' show DartString;
import '../universe/universe.dart' show CallStructure;
import '../util/util.dart';
import 'values.dart';
enum ConstantExpressionKind {
@ -141,6 +142,23 @@ class GenerativeConstantConstructor implements ConstantConstructor{
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() {
StringBuffer sb = new StringBuffer();
sb.write("{'type': $type");
@ -157,6 +175,16 @@ class GenerativeConstantConstructor implements ConstantConstructor{
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
/// [constructorInvocation]. If [constructorInvocation] is `null`, an empty
/// map is created.
@ -205,6 +233,20 @@ class RedirectingGenerativeConstantConstructor implements ConstantConstructor {
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() {
StringBuffer sb = new StringBuffer();
sb.write("{'type': ${thisConstructorInvocation.type}");
@ -240,6 +282,16 @@ class RedirectingFactoryConstantConstructor implements ConstantConstructor {
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() {
StringBuffer sb = new StringBuffer();
sb.write("{");

View File

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

View File

@ -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);
}
ClassElement getSuperOrThisLookupTarget(FunctionElement constructor,
bool isSuperCall,
Node diagnosticNode) {
ClassElement lookupTarget = constructor.enclosingClass;
if (isSuperCall) {
// Calculate correct lookup target and constructor name.
if (identical(lookupTarget, visitor.compiler.objectClass)) {
error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
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 {
return lookupTarget.supertype.element;
isValidAsConstant = false;
}
}
return lookupTarget;
}
Element resolveSuperOrThisForSend(FunctionElement constructor,
FunctionExpression functionNode,
Send call) {
// Resolve the selector and the arguments.
visitor.inStaticContext(() {
visitor.resolveSelector(call, null);
visitor.resolveArguments(call.argumentsNode);
});
Selector selector = registry.getSelector(call);
bool isSuperCall = Initializers.isSuperConstructorCall(call);
InterfaceType getSuperOrThisLookupTarget(Node diagnosticNode,
{bool isSuperCall}) {
if (isSuperCall) {
// Calculate correct lookup target and constructor name.
if (identical(constructor.enclosingClass, visitor.compiler.objectClass)) {
error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
isValidAsConstant = false;
} else {
return constructor.enclosingClass.supertype;
}
}
return constructor.enclosingClass.thisType;
}
ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor,
isSuperCall,
call);
ResolutionResult resolveSuperOrThisForSend(Send call) {
// Resolve the selector and the arguments.
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 =
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.
}

View File

@ -4,6 +4,24 @@
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.
*
@ -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,14 +174,6 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
return result;
}
inStaticContext(action()) {
bool wasInstanceContext = inInstanceContext;
inInstanceContext = false;
var result = action();
inInstanceContext = wasInstanceContext;
return result;
}
doInPromotionScope(Node node, action()) {
promotionScope = promotionScope.prepend(node);
var result = action();
@ -168,8 +181,47 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
return result;
}
ResolutionResult visitInStaticContext(Node node) {
return inStaticContext(() => visit(node));
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;
}
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(
@ -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) {

View File

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

View File

@ -23,6 +23,7 @@ abstract class ResolutionResult {
}
ResultKind get kind;
Node get node => null;
Element get element => null;
DartType get type => null;
ConstantExpression get constant => null;
@ -64,11 +65,16 @@ class AssertResult extends ResolutionResult {
String toString() => 'AssertResult()';
}
/// The result for resolving a constant expression.
class ConstantResult extends ResolutionResult {
final Node node;
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;
@ -83,4 +89,31 @@ class NoneResult extends ResolutionResult {
ResultKind get kind => ResultKind.NONE;
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 ResolutionRegistry.internal(compiler,
new CollectingTreeElements(element)));
new InitializerResolver(visitor).resolveInitializers(element, tree);
new InitializerResolver(visitor, element, tree).resolveInitializers();
visitor.visit(tree.body);
Expect.equals(expectedElementCount, map(visitor).length,
"${map(visitor).values} for '$statement' in context of `$script`");