Files
sdk/lib/compiler/implementation/resolver.dart
T
lrn@google.com 5163f80703 Generate prettier loops.
Add structure information to HLoopInformation, and use it to generate prettier
loops when the condition can be generated as an expression.
If it can't, it still falls back to the while(true){if(!cond) break;} pattern.
Only handle for/while/for-in, not do-while yet.

Review URL: https://chromiumcodereview.appspot.com//9863037

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@6023 260f80e4-7a28-3924-810f-c04153c831b5
2012-03-30 08:59:40 +00:00

1802 lines
61 KiB
Dart

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
interface TreeElements {
Element operator[](Node node);
Selector getSelector(Send send);
}
class TreeElementMapping implements TreeElements {
Map<Node, Element> map;
Map<Send, Selector> selectors;
TreeElementMapping()
: map = new LinkedHashMap<Node, Element>(),
selectors = new LinkedHashMap<Send, Selector>();
operator []=(Node node, Element element) => map[node] = element;
operator [](Node node) => map[node];
void remove(Node node) { map.remove(node); }
void setSelector(Send send, Selector selector) {
selectors[send] = selector;
}
Selector getSelector(Send send) => selectors[send];
}
class ResolverTask extends CompilerTask {
Queue<ClassElement> toResolve;
// Caches the elements of analyzed constructors to make them available
// for inlining in later tasks.
Map<FunctionElement, TreeElements> constructorElements;
ResolverTask(Compiler compiler)
: super(compiler), toResolve = new Queue<ClassElement>(),
constructorElements = new Map<FunctionElement, TreeElements>();
String get name() => 'Resolver';
TreeElements resolve(Element element) {
return measure(() {
switch (element.kind) {
case ElementKind.GENERATIVE_CONSTRUCTOR:
case ElementKind.FUNCTION:
case ElementKind.GETTER:
case ElementKind.SETTER:
return resolveMethodElement(element);
case ElementKind.FIELD:
return resolveField(element);
case ElementKind.PARAMETER:
case ElementKind.FIELD_PARAMETER:
return resolveParameter(element);
default:
compiler.unimplemented(
"resolver", node: element.parseNode(compiler));
}
});
}
SourceString getConstructorName(Send node) {
if (node.receiver !== null) {
return node.selector.asIdentifier().source;
} else {
return const SourceString('');
}
}
FunctionElement lookupConstructor(ClassElement classElement, Send send,
[noConstructor(Element)]) {
final SourceString constructorName = getConstructorName(send);
final SourceString className = classElement.name;
return classElement.lookupConstructor(className,
constructorName,
noConstructor);
}
FunctionElement resolveConstructorRedirection(FunctionElement constructor) {
FunctionExpression node = constructor.parseNode(compiler);
// A synthetic constructor does not have a node.
if (node === null) return null;
if (node.initializers === null) return null;
Link<Node> initializers = node.initializers.nodes;
if (!initializers.isEmpty() &&
Initializers.isConstructorRedirect(initializers.head)) {
return lookupConstructor(constructor.enclosingElement, initializers.head);
}
return null;
}
void resolveRedirectingConstructor(InitializerResolver resolver,
Node node,
FunctionElement constructor,
FunctionElement redirection) {
Set<FunctionElement> seen = new Set<FunctionElement>();
seen.add(constructor);
while (redirection !== null) {
if (seen.contains(redirection)) {
resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
return;
}
seen.add(redirection);
redirection = resolveConstructorRedirection(redirection);
}
}
TreeElements resolveMethodElement(FunctionElement element) {
return compiler.withCurrentElement(element, () {
bool isConstructor = element.kind === ElementKind.GENERATIVE_CONSTRUCTOR;
if (constructorElements.containsKey(element)) {
assert(isConstructor);
TreeElements elements = constructorElements[element];
if (elements !== null) return elements;
}
FunctionExpression tree = element.parseNode(compiler);
if (isConstructor) {
resolveConstructorImplementation(element, tree);
}
ResolverVisitor visitor = new ResolverVisitor(compiler, element);
visitor.useElement(tree, element);
visitor.setupFunction(tree, element);
if (tree.initializers != null) {
if (!isConstructor) {
error(tree, MessageKind.FUNCTION_WITH_INITIALIZER);
}
InitializerResolver resolver = new InitializerResolver(visitor);
FunctionElement redirection =
resolver.resolveInitializers(element, tree);
if (redirection !== null) {
resolveRedirectingConstructor(resolver, tree, element, redirection);
}
}
visitor.visit(tree.body);
// Resolve the type annotations encountered in the method.
while (!toResolve.isEmpty()) {
ClassElement classElement = toResolve.removeFirst();
classElement.ensureResolved(compiler);
}
if (isConstructor) {
constructorElements[element] = visitor.mapping;
}
return visitor.mapping;
});
}
void resolveConstructorImplementation(FunctionElement constructor,
FunctionExpression node) {
assert(constructor.defaultImplementation === constructor);
ClassElement intrface = constructor.enclosingElement;
if (!intrface.isInterface()) return;
Type defaultType = intrface.defaultClass;
if (defaultType === null) {
error(node, MessageKind.NO_DEFAULT_CLASS, [intrface.name]);
}
ClassElement defaultClass = defaultType.element;
defaultClass.ensureResolved(compiler);
if (defaultClass.isInterface()) {
error(node, MessageKind.CANNOT_INSTANTIATE_INTERFACE,
[defaultClass.name]);
}
// We have now established the following:
// [intrface] is an interface, let's say "MyInterface".
// [defaultClass] is a class, let's say "MyClass".
// First look up the constructor named "MyInterface.name".
constructor.defaultImplementation =
defaultClass.lookupConstructor(constructor.name);
// If that fails, try looking up "MyClass.name".
if (constructor.defaultImplementation === null) {
SourceString name =
new SourceString(constructor.name.slowToString().replaceFirst(
intrface.name.slowToString(),
defaultClass.name.slowToString()));
constructor.defaultImplementation = defaultClass.lookupConstructor(name);
if (constructor.defaultImplementation === null) {
// We failed find a constrcutor named either
// "MyInterface.name" or "MyClass.name".
error(node, MessageKind.CANNOT_FIND_CONSTRUCTOR2,
[constructor.name, name]);
}
}
}
TreeElements resolveField(Element element) {
Node tree = element.parseNode(compiler);
ResolverVisitor visitor = new ResolverVisitor(compiler, element);
initializerDo(tree, visitor.visit);
return visitor.mapping;
}
TreeElements resolveParameter(Element element) {
Node tree = element.parseNode(compiler);
ResolverVisitor visitor =
new ResolverVisitor(compiler, element.enclosingElement);
initializerDo(tree, visitor.visit);
return visitor.mapping;
}
Type resolveType(ClassElement element) {
if (element.isResolved) return element.type;
return measure(() {
ClassNode tree = element.parseNode(compiler);
ClassResolverVisitor visitor =
new ClassResolverVisitor(compiler, element.getLibrary(), element);
visitor.visit(tree);
element.isResolved = true;
return element.type;
});
}
FunctionParameters resolveSignature(FunctionElement element) {
return measure(() => SignatureResolver.analyze(compiler, element));
}
error(Node node, MessageKind kind, [arguments = const []]) {
ResolutionError message = new ResolutionError(kind, arguments);
compiler.reportError(node, message);
}
}
class InitializerResolver {
final ResolverVisitor visitor;
final Map<SourceString, Node> initialized;
Link<Node> initializers;
bool hasSuper;
InitializerResolver(this.visitor)
: initialized = new Map<SourceString, Node>(), hasSuper = false;
error(Node node, MessageKind kind, [arguments = const []]) {
visitor.error(node, kind, arguments);
}
warning(Node node, MessageKind kind, [arguments = const []]) {
visitor.warning(node, kind, arguments);
}
bool isFieldInitializer(SendSet node) {
if (node.selector.asIdentifier() == null) return false;
if (node.receiver == null) return true;
if (node.receiver.asIdentifier() == null) return false;
return node.receiver.asIdentifier().isThis();
}
void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
// init is of the form [this.]field = value.
final Node selector = init.selector;
final SourceString name = selector.asIdentifier().source;
// Lookup target field.
Element target;
if (isFieldInitializer(init)) {
final ClassElement classElement = constructor.enclosingElement;
target = classElement.lookupLocalMember(name);
if (target === null) {
error(selector, MessageKind.CANNOT_RESOLVE, [name]);
} else if (target.kind != ElementKind.FIELD) {
error(selector, MessageKind.NOT_A_FIELD, [name]);
} else if (!target.isInstanceMember()) {
error(selector, MessageKind.INIT_STATIC_FIELD, [name]);
}
} else {
error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER);
}
visitor.useElement(init, target);
// Check for duplicate initializers.
if (initialized.containsKey(name)) {
error(init, MessageKind.DUPLICATE_INITIALIZER, [name]);
warning(initialized[name], MessageKind.ALREADY_INITIALIZED, [name]);
}
initialized[name] = init;
// Resolve initializing value.
visitor.visitInStaticContext(init.arguments.head);
}
Element resolveSuperOrThis(FunctionElement constructor,
FunctionExpression functionNode,
Send call) {
noConstructor(e) {
if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]);
}
ClassElement lookupTarget = constructor.enclosingElement;
bool validTarget = true;
FunctionElement result;
if (Initializers.isSuperConstructorCall(call)) {
// Check for invalid initializers.
if (hasSuper) {
error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
}
hasSuper = true;
// Calculate correct lookup target and constructor name.
if (lookupTarget.name == Types.OBJECT) {
error(call, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
} else {
lookupTarget = lookupTarget.supertype.element;
}
} else if (Initializers.isConstructorRedirect(call)) {
// Check that there is no body (Language specification 7.5.1).
if (functionNode.hasBody()) {
error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY);
}
// Check that there are no other initializers.
if (!initializers.tail.isEmpty()) {
error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER);
}
} else {
visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
validTarget = false;
}
if (validTarget) {
// Resolve the arguments, and make sure the call gets a selector
// by calling handleArguments.
visitor.inStaticContext( () => visitor.handleArguments(call) );
// Lookup constructor and try to match it to the selector.
ResolverTask resolver = visitor.compiler.resolver;
result = resolver.lookupConstructor(lookupTarget, call);
if (result === null) {
SourceString constructorName = resolver.getConstructorName(call);
String className = lookupTarget.name.slowToString();
String name = (constructorName === const SourceString(''))
? className
: "$className.${constructorName.slowToString()}";
error(call, MessageKind.CANNOT_RESOLVE_CONSTRUCTOR, [name]);
} else {
final Compiler compiler = visitor.compiler;
Selector selector = visitor.mapping.getSelector(call);
FunctionParameters parameters = result.computeParameters(compiler);
// TODO(karlklose): support optional arguments.
if (!selector.applies(parameters)) {
error(call, MessageKind.NO_MATCHING_CONSTRUCTOR);
}
}
visitor.useElement(call, result);
}
return result;
}
FunctionElement resolveRedirection(FunctionElement constructor,
FunctionExpression functionNode) {
if (functionNode.initializers === null) return null;
Link<Node> link = functionNode.initializers.nodes;
if (!link.isEmpty() && Initializers.isConstructorRedirect(link.head)) {
return resolveSuperOrThis(constructor, functionNode, link.head);
}
return null;
}
/**
* Resolve all initializers of this constructor. In the case of a redirecting
* constructor, the resolved constructor's function element is returned.
*/
FunctionElement resolveInitializers(FunctionElement constructor,
FunctionExpression functionNode) {
if (functionNode.initializers === null) return null;
initializers = functionNode.initializers.nodes;
FunctionElement result;
for (Link<Node> link = initializers;
!link.isEmpty();
link = link.tail) {
if (link.head.asSendSet() != null) {
final SendSet init = link.head.asSendSet();
resolveFieldInitializer(constructor, init);
} else if (link.head.asSend() !== null) {
final Send call = link.head.asSend();
result = resolveSuperOrThis(constructor, functionNode, call);
} else {
error(link.head, MessageKind.INVALID_INITIALIZER);
}
}
return result;
}
}
class CommonResolverVisitor<R> extends AbstractVisitor<R> {
final Compiler compiler;
CommonResolverVisitor(Compiler this.compiler);
R visitNode(Node node) {
cancel(node, 'internal error');
}
R visitEmptyStatement(Node node) => null;
/** Convenience method for visiting nodes that may be null. */
R visit(Node node) => (node == null) ? null : node.accept(this);
void error(Node node, MessageKind kind, [arguments = const []]) {
ResolutionError message = new ResolutionError(kind, arguments);
compiler.reportError(node, message);
}
void warning(Node node, MessageKind kind, [arguments = const []]) {
ResolutionWarning message = new ResolutionWarning(kind, arguments);
compiler.reportWarning(node, message);
}
void cancel(Node node, String message) {
compiler.cancel(message, node: node);
}
void internalError(Node node, String message) {
compiler.internalError(message, node: node);
}
void unimplemented(Node node, String message) {
compiler.unimplemented(message, node: node);
}
}
interface LabelScope {
LabelScope get outer();
LabelElement lookup(String label);
}
class LabeledStatementLabelScope implements LabelScope {
final LabelScope outer;
final LabelElement label;
LabeledStatementLabelScope(this.outer, this.label);
LabelElement lookup(String labelName) {
if (this.label.labelName == labelName) return label;
return outer.lookup(labelName);
}
}
class SwitchLabelScope implements LabelScope {
final LabelScope outer;
final Map<String, LabelElement> caseLabels;
SwitchLabelScope(this.outer, this.caseLabels);
LabelElement lookup(String labelName) {
LabelElement result = caseLabels[labelName];
if (result !== null) return result;
return outer.lookup(labelName);
}
}
class EmptyLabelScope implements LabelScope {
const EmptyLabelScope();
LabelElement lookup(String label) => null;
LabelScope get outer() {
throw 'internal error: empty label scope has no outer';
}
}
class StatementScope {
LabelScope labels;
Link<TargetElement> breakTargetStack;
Link<TargetElement> continueTargetStack;
// Used to provide different numbers to statements if one is inside the other.
// Can be used to make otherwise duplicate labels unique.
int nestingLevel = 0;
StatementScope()
: labels = const EmptyLabelScope(),
breakTargetStack = const EmptyLink<TargetElement>(),
continueTargetStack = const EmptyLink<TargetElement>();
LabelElement lookupLabel(String label) {
return labels.lookup(label);
}
TargetElement currentBreakTarget() =>
breakTargetStack.isEmpty() ? null : breakTargetStack.head;
TargetElement currentContinueTarget() =>
continueTargetStack.isEmpty() ? null : continueTargetStack.head;
void enterLabelScope(LabelElement element) {
labels = new LabeledStatementLabelScope(labels, element);
nestingLevel++;
}
void exitLabelScope() {
nestingLevel--;
labels = labels.outer;
}
void enterLoop(TargetElement element) {
breakTargetStack = breakTargetStack.prepend(element);
continueTargetStack = continueTargetStack.prepend(element);
nestingLevel++;
}
void exitLoop() {
nestingLevel--;
breakTargetStack = breakTargetStack.tail;
continueTargetStack = continueTargetStack.tail;
}
void enterSwitch(TargetElement breakElement,
Map<String, LabelElement> continueElements) {
breakTargetStack = breakTargetStack.prepend(breakElement);
labels = new SwitchLabelScope(labels, continueElements);
nestingLevel++;
}
void exitSwitch() {
nestingLevel--;
breakTargetStack = breakTargetStack.tail;
labels = labels.outer;
}
}
class ResolverVisitor extends CommonResolverVisitor<Element> {
final TreeElementMapping mapping;
final Element enclosingElement;
bool inInstanceContext;
Scope context;
ClassElement currentClass;
bool typeRequired = false;
StatementScope statementScope;
int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION;
ResolverVisitor(Compiler compiler, Element element)
: this.mapping = new TreeElementMapping(),
this.enclosingElement = element,
inInstanceContext = element.isInstanceMember()
|| element.isGenerativeConstructor(),
this.context = element.isMember()
? new ClassScope(element.enclosingElement, element.getLibrary())
: new TopScope(element.getLibrary()),
this.currentClass = element.isMember() ? element.enclosingElement : null,
this.statementScope = new StatementScope(),
super(compiler);
Element lookup(Node node, SourceString name) {
Element result = context.lookup(name);
if (!inInstanceContext && result != null && result.isInstanceMember()) {
error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]);
}
return result;
}
// Create, or reuse an already created, statement element for a statement.
TargetElement getOrCreateTargetElement(Node statement) {
TargetElement element = mapping[statement];
if (element === null) {
element = new TargetElement(statement,
statementScope.nestingLevel,
enclosingElement);
mapping[statement] = element;
}
return element;
}
inStaticContext(action()) {
bool wasInstanceContext = inInstanceContext;
inInstanceContext = false;
var result = action();
inInstanceContext = wasInstanceContext;
return result;
}
visitInStaticContext(Node node) {
inStaticContext(() => visit(node));
}
Element visitIdentifier(Identifier node) {
if (node.isThis()) {
if (!inInstanceContext) {
error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]);
}
return null;
} else if (node.isSuper()) {
if (!inInstanceContext) error(node, MessageKind.NO_SUPER_IN_STATIC);
if ((ElementCategory.SUPER & allowedCategory) == 0) {
error(node, MessageKind.INVALID_USE_OF_SUPER);
}
return null;
} else {
Element element = lookup(node, node.source);
if (element === null) {
if (!inInstanceContext) error(node, MessageKind.CANNOT_RESOLVE, [node]);
} else {
if ((element.kind.category & allowedCategory) == 0) {
// TODO(ahe): Improve error message. Need UX input.
error(node, MessageKind.GENERIC, ["is not an expression $element"]);
}
}
return useElement(node, element);
}
}
visitTypeAnnotation(TypeAnnotation node) {
Send send = node.typeName.asSend();
Element element;
if (send !== null) {
if (typeRequired) {
element = resolveSend(send);
} else {
// Not calling resolveSend as it will emit an error instead of
// a warning if the type is bogus.
// TODO(ahe): Change resolveSend so it can emit a warning when needed.
return null;
}
} else {
element = context.lookup(node.typeName.asIdentifier().source);
}
if (element === null) {
if (typeRequired) {
error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]);
} else {
warning(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]);
}
} else if (!element.impliesType()) {
if (typeRequired) {
error(node, MessageKind.NOT_A_TYPE, [node.typeName]);
} else {
warning(node, MessageKind.NOT_A_TYPE, [node.typeName]);
}
} else {
if (element.isClass()) {
// TODO(ngeoffray): Should we also resolve typedef?
ClassElement cls = element;
compiler.resolver.toResolve.add(element);
}
// TODO(ahe): This should be a Type.
useElement(node, element);
}
return element;
}
Element defineElement(Node node, Element element,
[bool doAddToScope = true]) {
compiler.ensure(element !== null);
mapping[node] = element;
if (doAddToScope) {
Element existing = context.add(element);
if (existing != element) {
error(node, MessageKind.DUPLICATE_DEFINITION, [node]);
}
}
return element;
}
Element useElement(Node node, Element element) {
if (element === null) return null;
return mapping[node] = element;
}
void setupFunction(FunctionExpression node, FunctionElement function) {
context = new MethodScope(context, function);
// Put the parameters in scope.
FunctionParameters functionParameters =
function.computeParameters(compiler);
Link<Node> parameterNodes = node.parameters.nodes;
functionParameters.forEachParameter((Element element) {
if (element == functionParameters.optionalParameters.head) {
NodeList nodes = parameterNodes.head;
parameterNodes = nodes.nodes;
}
VariableDefinitions variableDefinitions = parameterNodes.head;
Node parameterNode = variableDefinitions.definitions.nodes.head;
initializerDo(parameterNode, (n) => n.accept(this));
// Field parameters (this.x) are not visible inside the constructor. The
// fields they reference are visible, but must be resolved independently.
if (element.kind == ElementKind.FIELD_PARAMETER) {
useElement(parameterNode, element);
} else {
defineElement(variableDefinitions.definitions.nodes.head, element);
}
parameterNodes = parameterNodes.tail;
});
}
Element visitClassNode(ClassNode node) {
cancel(node, "shouldn't be called");
}
visitIn(Node node, Scope scope) {
context = scope;
Element element = visit(node);
context = context.parent;
return element;
}
/**
* Introduces new default targets for break and continue
* before visiting the body of the loop
*/
visitLoopBodyIn(Node loop, Node body, Scope scope) {
TargetElement element = getOrCreateTargetElement(loop);
statementScope.enterLoop(element);
visitIn(body, scope);
statementScope.exitLoop();
if (!element.isTarget) {
mapping.remove(loop);
}
}
visitBlock(Block node) {
visitIn(node.statements, new BlockScope(context));
}
visitDoWhile(DoWhile node) {
visitLoopBodyIn(node, node.body, new BlockScope(context));
visit(node.condition);
}
visitEmptyStatement(EmptyStatement node) { }
visitExpressionStatement(ExpressionStatement node) {
visit(node.expression);
}
visitFor(For node) {
Scope scope = new BlockScope(context);
visitIn(node.initializer, scope);
visitIn(node.condition, scope);
visitIn(node.update, scope);
visitLoopBodyIn(node, node.body, scope);
}
visitFunctionDeclaration(FunctionDeclaration node) {
assert(node.function.name !== null);
visit(node.function);
FunctionElement functionElement = mapping[node.function];
// TODO(floitsch): this might lead to two errors complaining about
// shadowing.
defineElement(node, functionElement);
}
visitFunctionExpression(FunctionExpression node) {
visit(node.returnType);
SourceString name;
if (node.name === null) {
name = const SourceString("");
} else {
name = node.name.asIdentifier().source;
}
FunctionElement enclosing = new FunctionElement.node(
name, node, ElementKind.FUNCTION, new Modifiers.empty(),
context.element);
setupFunction(node, enclosing);
defineElement(node, enclosing, doAddToScope: node.name !== null);
// Run the body in a fresh statement scope.
StatementScope oldScope = statementScope;
statementScope = new StatementScope();
visit(node.body);
statementScope = oldScope;
context = context.parent;
}
visitIf(If node) {
visit(node.condition);
visit(node.thenPart);
visit(node.elsePart);
}
static bool isLogicalOperator(Identifier op) {
String str = op.source.stringValue;
return (str === '&&' || str == '||' || str == '!');
}
Element resolveSend(Send node) {
if (node.receiver === null) {
return node.selector.accept(this);
}
var oldCategory = allowedCategory;
allowedCategory |=
ElementCategory.CLASS | ElementCategory.PREFIX | ElementCategory.SUPER;
Element resolvedReceiver = visit(node.receiver);
allowedCategory = oldCategory;
Element target;
SourceString name = node.selector.asIdentifier().source;
if (name.stringValue === 'this') {
error(node.selector, MessageKind.GENERIC, ["expected an identifier"]);
} else if (node.isSuperCall) {
if (node.isOperator) {
if (isUserDefinableOperator(name.stringValue)) {
name = Elements.constructOperatorName(const SourceString('operator'),
name);
} else {
error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, [name]);
}
}
if (!inInstanceContext) {
error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, [name]);
return null;
}
if (currentClass.supertype === null) {
// This is just to guard against internal errors, so no need
// for a real error message.
error(node.receiver, MessageKind.GENERIC, ["Object has no superclass"]);
}
target = currentClass.lookupSuperMember(name);
// [target] may be null which means invoking noSuchMethod on
// super.
} else if (resolvedReceiver === null) {
return null;
} else if (resolvedReceiver.kind === ElementKind.CLASS) {
ClassElement receiverClass = resolvedReceiver;
target = receiverClass.ensureResolved(compiler).lookupLocalMember(name);
if (target === null) {
error(node, MessageKind.METHOD_NOT_FOUND, [receiverClass.name, name]);
} else if (target.isInstanceMember()) {
error(node, MessageKind.MEMBER_NOT_STATIC, [receiverClass.name, name]);
}
} else if (resolvedReceiver.kind === ElementKind.PREFIX) {
PrefixElement prefix = resolvedReceiver;
target = prefix.lookupLocalMember(name);
if (target == null) {
error(node, MessageKind.NO_SUCH_LIBRARY_MEMBER, [prefix.name, name]);
}
}
return target;
}
resolveTypeTest(Node argument) {
TypeAnnotation node = argument.asTypeAnnotation();
if (node == null) {
node = argument.asSend().receiver;
}
resolveTypeRequired(node);
}
void handleArguments(Send node) {
int count = 0;
List<SourceString> namedArguments = <SourceString>[];
bool seenNamedArgument = false;
for (Link<Node> link = node.argumentsNode.nodes;
!link.isEmpty();
link = link.tail) {
count++;
Expression argument = link.head;
visit(argument);
if (argument.asNamedArgument() != null) {
seenNamedArgument = true;
NamedArgument named = argument;
namedArguments.add(named.name.source);
} else if (seenNamedArgument) {
error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED);
}
}
mapping.setSelector(node, new Invocation(count, namedArguments));
}
visitSend(Send node) {
Element target = resolveSend(node);
if (node.isOperator) {
Operator op = node.selector.asOperator();
if (op.source.stringValue === 'is') {
resolveTypeTest(node.arguments.head);
assert(node.arguments.tail.isEmpty());
mapping.setSelector(node, Selector.BINARY_OPERATOR);
} else if (node.arguments.isEmpty()) {
assert(op.token.kind !== PLUS_TOKEN);
mapping.setSelector(node, Selector.UNARY_OPERATOR);
} else {
visit(node.argumentsNode);
mapping.setSelector(node, Selector.BINARY_OPERATOR);
}
} else if (node.isIndex) {
visit(node.argumentsNode);
assert(node.arguments.tail.isEmpty());
mapping.setSelector(node, Selector.INDEX);
} else if (node.isPropertyAccess) {
mapping.setSelector(node, Selector.GETTER);
} else {
handleArguments(node);
}
if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) {
AbstractFieldElement field = target;
target = field.getter;
}
// TODO(ngeoffray): Warn if target is null and the send is
// unqualified.
useElement(node, target);
if (node.isPropertyAccess) return target;
}
visitSendSet(SendSet node) {
Element target = resolveSend(node);
Element setter = null;
Element getter = null;
if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) {
AbstractFieldElement field = target;
setter = field.setter;
getter = field.getter;
} else {
setter = target;
getter = target;
}
// TODO(ngeoffray): Check if the target can be assigned.
Identifier op = node.assignmentOperator;
bool needsGetter = op.source.stringValue !== '=';
Selector selector;
if (needsGetter) {
if (node.isIndex) {
selector = Selector.INDEX_AND_INDEX_SET;
} else {
selector = Selector.GETTER_AND_SETTER;
}
useElement(node.selector, getter);
} else if (node.isIndex) {
selector = Selector.INDEX_SET;
} else {
selector = Selector.SETTER;
}
visit(node.argumentsNode);
mapping.setSelector(node, selector);
// TODO(ngeoffray): Warn if target is null and the send is
// unqualified.
return useElement(node, setter);
}
visitLiteralInt(LiteralInt node) {
}
visitLiteralDouble(LiteralDouble node) {
}
visitLiteralBool(LiteralBool node) {
}
visitLiteralString(LiteralString node) {
}
visitLiteralNull(LiteralNull node) {
}
visitStringJuxtaposition(StringJuxtaposition node) {
node.visitChildren(this);
}
visitNodeList(NodeList node) {
for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
visit(link.head);
}
}
visitOperator(Operator node) {
unimplemented(node, 'operator');
}
visitReturn(Return node) {
visit(node.expression);
}
visitThrow(Throw node) {
visit(node.expression);
}
visitVariableDefinitions(VariableDefinitions node) {
visit(node.type);
VariableDefinitionsVisitor visitor =
new VariableDefinitionsVisitor(compiler, node, this,
ElementKind.VARIABLE);
visitor.visit(node.definitions);
}
visitWhile(While node) {
visit(node.condition);
visitLoopBodyIn(node, node.body, new BlockScope(context));
}
visitParenthesizedExpression(ParenthesizedExpression node) {
visit(node.expression);
}
visitNewExpression(NewExpression node) {
Node selector = node.send.selector;
FunctionElement constructor = resolveConstructor(node);
handleArguments(node.send);
if (constructor === null) return null;
// TODO(karlklose): handle optional arguments.
if (node.send.argumentCount() != constructor.parameterCount(compiler)) {
// TODO(ngeoffray): resolution error with wrong number of
// parameters. We cannot do this rigth now because of the
// List constructor.
}
useElement(node.send, constructor);
return null;
}
FunctionElement resolveConstructor(NewExpression node) {
FunctionElement constructor =
node.accept(new ConstructorResolver(compiler, this));
if (constructor === null) {
Element resolved = resolveTypeRequired(node.send.selector);
if (resolved !== null && resolved.kind === ElementKind.TYPE_VARIABLE) {
error(node, WarningKind.TYPE_VARIABLE_AS_CONSTRUCTOR);
return null;
} else {
error(node.send, MessageKind.CANNOT_FIND_CONSTRUCTOR, [node.send]);
}
}
return constructor;
}
Element resolveTypeRequired(Node node) {
bool old = typeRequired;
typeRequired = true;
Element element = visit(node);
typeRequired = old;
return element;
}
visitModifiers(Modifiers node) {
// TODO(ngeoffray): Implement this.
unimplemented(node, 'modifiers');
}
visitLiteralList(LiteralList node) {
visit(node.elements);
}
visitConditional(Conditional node) {
node.visitChildren(this);
}
visitStringInterpolation(StringInterpolation node) {
node.visitChildren(this);
}
visitStringInterpolationPart(StringInterpolationPart node) {
node.visitChildren(this);
}
visitBreakStatement(BreakStatement node) {
TargetElement target;
if (node.target === null) {
target = statementScope.currentBreakTarget();
if (target === null) {
error(node, MessageKind.NO_BREAK_TARGET);
return;
}
target.isBreakTarget = true;
} else {
String labelName = node.target.source.slowToString();
LabelElement label = statementScope.lookupLabel(labelName);
if (label === null) {
error(node.target, MessageKind.UNBOUND_LABEL, [labelName]);
return;
}
target = label.target;
if (!target.statement.isValidBreakTarget()) {
error(node.target, MessageKind.INVALID_BREAK, [labelName]);
return;
}
label.setBreakTarget();
mapping[node.target] = label;
}
mapping[node] = target;
}
visitContinueStatement(ContinueStatement node) {
TargetElement target;
if (node.target === null) {
target = statementScope.currentContinueTarget();
if (target === null) {
error(node, MessageKind.NO_CONTINUE_TARGET);
return;
}
target.isContinueTarget = true;
} else {
String labelName = node.target.source.slowToString();
LabelElement label = statementScope.lookupLabel(labelName);
if (label === null) {
error(node.target, MessageKind.UNBOUND_LABEL, [labelName]);
return;
}
target = label.target;
if (!target.statement.isValidContinueTarget()) {
error(node.target, MessageKind.INVALID_CONTINUE, [labelName]);
}
label.setContinueTarget();
mapping[node.target] = label;
}
mapping[node] = target;
}
visitForIn(ForIn node) {
visit(node.expression);
Scope scope = new BlockScope(context);
Node declaration = node.declaredIdentifier;
visitIn(declaration, scope);
visitLoopBodyIn(node, node.body, scope);
// TODO(lrn): Also allow a single identifier.
if ((declaration is !Send || declaration.asSend().selector is !Identifier)
&& (declaration is !VariableDefinitions ||
!declaration.asVariableDefinitions().definitions.nodes.tail.isEmpty()))
{
// The variable declaration is either not an identifier, not a
// declaration, or it's declaring more than one variable.
error(node.declaredIdentifier, MessageKind.INVALID_FOR_IN, []);
}
}
visitLabeledStatement(LabeledStatement node) {
String labelName = node.label.source.slowToString();
LabelElement existingElement = statementScope.lookupLabel(labelName);
if (existingElement !== null) {
warning(node.label, MessageKind.DUPLICATE_LABEL, [labelName]);
warning(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]);
}
Node body = node.getBody();
TargetElement targetElement = getOrCreateTargetElement(body);
LabelElement element = targetElement.addLabel(node.label, labelName);
statementScope.enterLabelScope(element);
visit(node.statement);
statementScope.exitLabelScope();
if (element.isTarget) {
mapping[node.label] = element;
} else {
warning(node.label, MessageKind.UNUSED_LABEL, [labelName]);
}
if (!targetElement.isTarget && mapping[body] === targetElement) {
// If the body is itself a break or continue for another target, it
// might have updated its mapping to the target it actually does target.
mapping.remove(body);
}
}
visitLiteralMap(LiteralMap node) {
node.visitChildren(this);
}
visitLiteralMapEntry(LiteralMapEntry node) {
node.visitChildren(this);
}
visitNamedArgument(NamedArgument node) {
visit(node.expression);
}
visitSwitchStatement(SwitchStatement node) {
node.expression.accept(this);
TargetElement breakElement = getOrCreateTargetElement(node);
Map<String, LabelElement> continueLabels = <LabelElement>{};
Link<Node> cases = node.cases.nodes;
while (!cases.isEmpty()) {
SwitchCase switchCase = cases.head;
if (switchCase.label !== null) {
Identifier labelIdentifier = switchCase.label;
String labelName = labelIdentifier.source.slowToString();
LabelElement existingElement = continueLabels[labelName];
if (existingElement !== null) {
// It's an error if the same label occurs twice in the same switch.
warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]);
error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]);
} else {
// It's only a warning if it shadows another label.
existingElement = statementScope.lookupLabel(labelName);
if (existingElement !== null) {
warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]);
warning(existingElement.label,
MessageKind.EXISTING_LABEL, [labelName]);
}
}
TargetElement TargetElement =
new TargetElement(switchCase,
statementScope.nestingLevel,
enclosingElement);
mapping[switchCase] = TargetElement;
LabelElement label =
new LabelElement(labelIdentifier, labelName,
TargetElement, enclosingElement);
mapping[labelIdentifier] = label;
continueLabels[labelName] = label;
}
cases = cases.tail;
if (switchCase.defaultKeyword !== null && !cases.isEmpty()) {
error(switchCase, MessageKind.INVALID_CASE_DEFAULT);
}
}
statementScope.enterSwitch(breakElement, continueLabels);
node.cases.accept(this);
statementScope.exitSwitch();
// Clean-up unused labels
continueLabels.forEach((String key, LabelElement label) {
TargetElement TargetElement = label.target;
SwitchCase switchCase = TargetElement.statement;
if (!label.isContinueTarget) {
mapping.remove(switchCase);
mapping.remove(label.label);
}
});
}
visitSwitchCase(SwitchCase node) {
// The label was handled in [visitSwitchStatement(SwitchStatement)].
node.expressions.accept(this);
visitIn(node.statements, new BlockScope(context));
}
visitTryStatement(TryStatement node) {
visit(node.tryBlock);
if (node.catchBlocks.isEmpty() && node.finallyBlock == null) {
// TODO(ngeoffray): The precise location is
// node.getEndtoken.next. Adjust when issue #1581 is fixed.
error(node, MessageKind.NO_CATCH_NOR_FINALLY);
}
visit(node.catchBlocks);
visit(node.finallyBlock);
}
visitCatchBlock(CatchBlock node) {
Scope scope = new BlockScope(context);
if (node.formals.isEmpty()) {
error(node, MessageKind.EMPTY_CATCH_DECLARATION);
} else if (!node.formals.nodes.tail.isEmpty()
&& !node.formals.nodes.tail.tail.isEmpty()) {
for (Node extra in node.formals.nodes.tail.tail) {
error(extra, MessageKind.EXTRA_CATCH_DECLARATION);
}
}
visitIn(node.formals, scope);
visitIn(node.block, scope);
}
visitTypedef(Typedef node) {
unimplemented(node, 'typedef');
}
}
class ClassResolverVisitor extends CommonResolverVisitor<Type> {
Scope context;
ClassElement classElement;
ClassResolverVisitor(Compiler compiler, LibraryElement library,
ClassElement this.classElement)
: context = new TopScope(library),
super(compiler);
Type visitClassNode(ClassNode node) {
compiler.ensure(classElement !== null);
compiler.ensure(!classElement.isResolved);
final Link<Node> parameters =
node.typeParameters !== null ? node.typeParameters.nodes
: const EmptyLink<TypeVariable>();
// Create types and elements for type variable.
for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) {
TypeVariable typeNode = link.head;
SourceString variableName = typeNode.name.source;
TypeVariableType variableType = new TypeVariableType(variableName);
TypeVariableElement variableElement =
new TypeVariableElement(variableName, classElement, node,
variableType);
variableType.element = variableElement;
classElement.typeParameters[variableName] = variableElement;
context = new TypeVariablesScope(context, classElement);
}
// Resolve the bounds of type variables.
for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) {
TypeVariable typeNode = link.head;
SourceString variableName = typeNode.name.source;
TypeVariableElement variableElement =
classElement.typeParameters[variableName];
if (typeNode.bound !== null) {
Type boundType = visit(typeNode.bound);
if (boundType !== null && boundType.element == variableElement) {
warning(node, MessageKind.CYCLIC_TYPE_VARIABLE,
[variableElement.name]);
} else if (boundType !== null) {
variableElement.bound = boundType;
} else {
variableElement.bound = compiler.objectClass.computeType(compiler);
}
}
}
// Find super type.
Type supertype = visit(node.superclass);
if (supertype !== null && supertype.element.isExtendable()) {
classElement.supertype = supertype;
if (isBlackListed(supertype)) {
error(node.superclass, MessageKind.CANNOT_EXTEND, [supertype]);
}
} else if (supertype !== null) {
error(node.superclass, MessageKind.TYPE_NAME_EXPECTED);
}
if (classElement.name != Types.OBJECT && classElement.supertype === null) {
ClassElement objectElement = context.lookup(Types.OBJECT);
if (objectElement !== null && !objectElement.isResolved) {
compiler.resolver.toResolve.add(objectElement);
} else if (objectElement === null){
error(node, MessageKind.CANNOT_RESOLVE_TYPE, [Types.OBJECT]);
}
classElement.supertype = new SimpleType(Types.OBJECT, objectElement);
}
if (node.defaultClause !== null) {
classElement.defaultClass = visit(node.defaultClause);
}
for (Link<Node> link = node.interfaces.nodes;
!link.isEmpty();
link = link.tail) {
Type interfaceType = visit(link.head);
if (interfaceType !== null && interfaceType.element.isExtendable()) {
classElement.interfaces =
classElement.interfaces.prepend(interfaceType);
if (isBlackListed(interfaceType)) {
error(link.head, MessageKind.CANNOT_IMPLEMENT, [interfaceType]);
}
} else {
error(link.head, MessageKind.TYPE_NAME_EXPECTED);
}
}
calculateAllSupertypes(classElement, new Set<ClassElement>());
addDefaultConstructorIfNeeded(classElement);
return classElement.computeType(compiler);
}
Type visitTypeAnnotation(TypeAnnotation node) {
return visit(node.typeName);
}
Type visitIdentifier(Identifier node) {
Element element = context.lookup(node.source);
if (element === null) {
error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node]);
return null;
} else if (!element.impliesType() && !element.isTypeVariable()) {
error(node, MessageKind.NOT_A_TYPE, [node]);
return null;
} else {
if (element.isClass()) {
compiler.resolver.toResolve.add(element);
}
if (element.isTypeVariable()) {
TypeVariableElement variableElement = element;
return variableElement.type;
} else if (element.isTypedef()) {
compiler.unimplemented('visitIdentifier for typedefs');
} else {
// TODO(ngeoffray): Use type variables.
return element.computeType(compiler);
}
}
return null;
}
Type visitSend(Send node) {
Identifier prefix = node.receiver.asIdentifier();
if (prefix === null) {
error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]);
return null;
}
Element element = context.lookup(prefix.source);
if (element === null || element.kind !== ElementKind.PREFIX) {
error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]);
return null;
}
PrefixElement prefixElement = element;
Identifier selector = node.selector.asIdentifier();
var e = prefixElement.lookupLocalMember(selector.source);
if (e === null || !e.impliesType()) {
error(node.selector, MessageKind.CANNOT_RESOLVE_TYPE, [node.selector]);
return null;
}
return e.computeType(compiler);
}
Link<Type> getOrCalculateAllSupertypes(ClassElement classElement,
[Set<ClassElement> seen]) {
Link<Type> allSupertypes = classElement.allSupertypes;
if (allSupertypes !== null) return allSupertypes;
if (seen === null) {
seen = new Set<ClassElement>();
}
if (seen.contains(classElement)) {
error(classElement.parseNode(compiler),
MessageKind.CYCLIC_CLASS_HIERARCHY,
[classElement.name]);
classElement.allSupertypes = const EmptyLink<Type>();
} else {
classElement.ensureResolved(compiler);
calculateAllSupertypes(classElement, seen);
}
return classElement.allSupertypes;
}
void calculateAllSupertypes(ClassElement classElement,
Set<ClassElement> seen) {
// TODO(karlklose): substitute type variables.
// TODO(karlklose): check if type arguments match, if a classelement occurs
// more than once in the supertypes.
if (classElement.allSupertypes !== null) return;
final Type supertype = classElement.supertype;
if (seen.contains(classElement)) {
error(classElement.parseNode(compiler),
MessageKind.CYCLIC_CLASS_HIERARCHY,
[classElement.name]);
classElement.allSupertypes = const EmptyLink<Type>();
} else if (supertype != null) {
seen.add(classElement);
Link<Type> superSupertypes =
getOrCalculateAllSupertypes(supertype.element, seen);
Link<Type> supertypes = new Link<Type>(supertype, superSupertypes);
for (Link<Type> interfaces = classElement.interfaces;
!interfaces.isEmpty();
interfaces = interfaces.tail) {
Element element = interfaces.head.element;
Link<Type> interfaceSupertypes =
getOrCalculateAllSupertypes(element, seen);
supertypes = supertypes.reversePrependAll(interfaceSupertypes);
supertypes = supertypes.prepend(interfaces.head);
}
seen.remove(classElement);
classElement.allSupertypes = supertypes;
} else {
classElement.allSupertypes = const EmptyLink<Type>();
}
}
/**
* Add a synthetic nullary constructor if there are no other
* constructors.
*/
void addDefaultConstructorIfNeeded(ClassElement element) {
if (element.constructors.length != 0) return;
SynthesizedConstructorElement constructor =
new SynthesizedConstructorElement(element);
element.constructors[element.name] = constructor;
Type returnType = compiler.types.voidType;
constructor.type = new FunctionType(returnType, const EmptyLink<Type>(),
constructor);
constructor.cachedNode =
new FunctionExpression(new Identifier(element.position()),
new NodeList.empty(),
new Block(new NodeList.empty()),
null, null, null, null);
}
isBlackListed(Type type) {
LibraryElement lib = classElement.getLibrary();
return
lib !== compiler.coreLibrary &&
lib !== compiler.coreImplLibrary &&
lib !== compiler.jsHelperLibrary &&
(type.element === compiler.dynamicClass ||
type.element === compiler.boolClass ||
type.element === compiler.numClass ||
type.element === compiler.intClass ||
type.element === compiler.doubleClass ||
type.element === compiler.stringClass ||
type.element === compiler.nullClass ||
type.element === compiler.functionClass);
}
}
class VariableDefinitionsVisitor extends CommonResolverVisitor<SourceString> {
VariableDefinitions definitions;
ResolverVisitor resolver;
ElementKind kind;
VariableListElement variables;
VariableDefinitionsVisitor(Compiler compiler,
this.definitions, this.resolver, this.kind)
: super(compiler)
{
variables = new VariableListElement.node(
definitions, ElementKind.VARIABLE_LIST, resolver.context.element);
}
SourceString visitSendSet(SendSet node) {
assert(node.arguments.tail.isEmpty()); // Sanity check
resolver.visit(node.arguments.head);
return visit(node.selector);
}
SourceString visitIdentifier(Identifier node) => node.source;
visitNodeList(NodeList node) {
for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
SourceString name = visit(link.head);
VariableElement element = new VariableElement(
name, variables, kind, resolver.context.element, node: link.head);
resolver.defineElement(link.head, element);
}
}
}
class SignatureResolver extends CommonResolverVisitor<Element> {
final Element enclosingElement;
Link<Element> optionalParameters = const EmptyLink<Element>();
int optionalParameterCount = 0;
Node currentDefinitions;
SignatureResolver(Compiler compiler, this.enclosingElement) : super(compiler);
Element visitNodeList(NodeList node) {
// This must be a list of optional arguments.
if (node.beginToken.stringValue !== '[') {
internalError(node, "expected optional parameters");
}
LinkBuilder<Element> elements = analyzeNodes(node.nodes);
optionalParameterCount = elements.length;
optionalParameters = elements.toLink();
return null;
}
Element visitVariableDefinitions(VariableDefinitions node) {
resolveType(node.type);
Link<Node> definitions = node.definitions.nodes;
if (definitions.isEmpty()) {
cancel(node, 'internal error: no parameter definition');
return null;
}
if (!definitions.tail.isEmpty()) {
cancel(definitions.tail.head, 'internal error: extra definition');
return null;
}
Node definition = definitions.head;
if (definition is NodeList) {
cancel(node, 'optional parameters are not implemented');
}
if (currentDefinitions != null) {
cancel(node, 'function type parameters not supported');
}
currentDefinitions = node;
Element element = definition.accept(this);
currentDefinitions = null;
return element;
}
Element visitIdentifier(Identifier node) {
Element variables = new VariableListElement.node(currentDefinitions,
ElementKind.VARIABLE_LIST, enclosingElement);
return new VariableElement(node.source, variables,
ElementKind.PARAMETER, enclosingElement, node: node);
}
// The only valid [Send] can be in constructors and must be of the form
// [:this.x:] (where [:x:] represents an instance field).
FieldParameterElement visitSend(Send node) {
FieldParameterElement element;
if (node.receiver.asIdentifier() === null ||
!node.receiver.asIdentifier().isThis()) {
error(node, MessageKind.INVALID_PARAMETER, []);
} else if (enclosingElement.kind !== ElementKind.GENERATIVE_CONSTRUCTOR) {
error(node, MessageKind.FIELD_PARAMETER_NOT_ALLOWED, []);
} else {
if (node.selector.asIdentifier() == null) {
cancel(node,
'internal error: unimplemented receiver on parameter send');
}
SourceString name = node.selector.asIdentifier().source;
Element fieldElement = currentClass.lookupLocalMember(name);
if (fieldElement === null || fieldElement.kind !== ElementKind.FIELD) {
error(node, MessageKind.NOT_A_FIELD, [name]);
} else if (!fieldElement.isInstanceMember()) {
error(node, MessageKind.NOT_INSTANCE_FIELD, [name]);
}
Element variables = new VariableListElement.node(currentDefinitions,
ElementKind.VARIABLE_LIST, enclosingElement);
element = new FieldParameterElement(node.selector.asIdentifier().source,
fieldElement, variables, enclosingElement, node);
}
return element;
}
Element visitSendSet(SendSet node) {
Element element;
if (node.receiver != null) {
element = visitSend(node);
} else if (node.selector.asIdentifier() != null) {
Element variables = new VariableListElement.node(currentDefinitions,
ElementKind.VARIABLE_LIST, enclosingElement);
element = new VariableElement(node.selector.asIdentifier().source,
variables, ElementKind.PARAMETER, enclosingElement, node: node);
}
// Visit the value. The compile time constant handler will
// make sure it's a compile time constant.
resolveExpression(node.arguments.head);
compiler.enqueue(new WorkItem.toCompile(element));
return element;
}
Element visitFunctionExpression(FunctionExpression node) {
// This is a function typed parameter.
// TODO(ahe): Resolve the function type.
return visit(node.name);
}
LinkBuilder<Element> analyzeNodes(Link<Node> link) {
LinkBuilder<Element> elements = new LinkBuilder<Element>();
for (; !link.isEmpty(); link = link.tail) {
Element element = link.head.accept(this);
if (element != null) {
elements.addLast(element);
} else {
// If parameter is null, the current node should be the last,
// and a list of optional named parameters.
if (!link.tail.isEmpty() || (link.head is !NodeList)) {
internalError(link.head, "expected optional parameters");
}
}
}
return elements;
}
static FunctionParameters analyze(Compiler compiler,
FunctionElement element) {
FunctionExpression node = element.parseNode(compiler);
SignatureResolver visitor = new SignatureResolver(compiler, element);
Link<Node> nodes = node.parameters.nodes;
LinkBuilder<Element> parameters = visitor.analyzeNodes(nodes);
return new FunctionParameters(parameters.toLink(),
visitor.optionalParameters,
parameters.length,
visitor.optionalParameterCount);
}
// TODO(ahe): This is temporary.
void resolveExpression(Node node) {
if (node == null) return;
node.accept(new ResolverVisitor(compiler, enclosingElement));
}
// TODO(ahe): This is temporary.
void resolveType(Node node) {
if (node == null) return;
node.accept(new ResolverVisitor(compiler, enclosingElement));
}
// TODO(ahe): This is temporary.
ClassElement get currentClass() {
return enclosingElement.isMember()
? enclosingElement.enclosingElement : null;
}
}
class ConstructorResolver extends CommonResolverVisitor<Element> {
final ResolverVisitor resolver;
ConstructorResolver(Compiler compiler, this.resolver) : super(compiler);
visitNode(Node node) {
throw 'not supported';
}
visitNewExpression(NewExpression node) {
Node selector = node.send.selector;
Element e = visit(selector);
if (e !== null && e.kind === ElementKind.CLASS) {
ClassElement cls = e;
cls.ensureResolved(compiler);
compiler.resolver.toResolve.add(cls);
if (cls.isInterface() && (cls.defaultClass === null)) {
error(selector, MessageKind.CANNOT_INSTANTIATE_INTERFACE, [cls.name]);
}
e = cls.lookupConstructor(cls.name);
}
return e;
}
visitTypeAnnotation(TypeAnnotation node) {
// TODO(ahe): Do not ignore type arguments.
return visit(node.typeName);
}
visitSend(Send node) {
Element e = visit(node.receiver);
if (e === null) return null; // TODO(ahe): Return erroneous element.
Identifier name = node.selector.asIdentifier();
if (name === null) internalError(node.selector, 'unexpected node');
if (e.kind === ElementKind.CLASS) {
ClassElement cls = e;
cls.ensureResolved(compiler);
compiler.resolver.toResolve.add(cls);
if (cls.isInterface() && (cls.defaultClass === null)) {
error(node.receiver, MessageKind.CANNOT_INSTANTIATE_INTERFACE,
[cls.name]);
}
SourceString constructorName =
Elements.constructConstructorName(cls.name, name.source);
FunctionElement constructor = cls.lookupConstructor(constructorName);
if (constructor === null) {
error(name, MessageKind.CANNOT_FIND_CONSTRUCTOR, [name]);
}
e = constructor;
} else if (e.kind === ElementKind.PREFIX) {
PrefixElement prefix = e;
e = prefix.lookupLocalMember(name.source);
if (e === null) {
error(name, MessageKind.CANNOT_RESOLVE, [name]);
// TODO(ahe): Return erroneous element.
} else if (e.kind !== ElementKind.CLASS) {
error(node, MessageKind.NOT_A_TYPE, [name]);
}
} else {
internalError(node.receiver, 'unexpected element $e');
}
return e;
}
Element visitIdentifier(Identifier node) {
SourceString name = node.source;
Element e = resolver.lookup(node, name);
if (e === null) {
error(node, MessageKind.CANNOT_RESOLVE, [name]);
// TODO(ahe): Return erroneous element.
} else if (e.kind === ElementKind.TYPEDEF) {
error(node, MessageKind.CANNOT_INSTANTIATE_TYPEDEF, [name]);
} else if (e.kind !== ElementKind.CLASS && e.kind !== ElementKind.PREFIX) {
error(node, MessageKind.NOT_A_TYPE, [name]);
}
return e;
}
}
class Scope {
final Element element;
final Scope parent;
Scope(this.parent, this.element);
abstract Element add(Element element);
abstract Element lookup(SourceString name);
}
class TypeVariablesScope extends Scope {
TypeVariablesScope(parent, ClassElement element) : super(parent, element);
Element add(Element element) {
throw "Cannot add element to TypeVariableScope";
}
Element lookup(SourceString name) {
ClassElement cls = element;
Element result = cls.lookupTypeParameter(name);
if (result !== null) return result;
if (parent !== null) return parent.lookup(name);
}
}
class MethodScope extends Scope {
final Map<SourceString, Element> elements;
MethodScope(Scope parent, Element element)
: super(parent, element), this.elements = new Map<SourceString, Element>();
Element lookup(SourceString name) {
Element element = elements[name];
if (element !== null) return element;
return parent.lookup(name);
}
Element add(Element element) {
if (elements.containsKey(element.name)) return elements[element.name];
elements[element.name] = element;
return element;
}
}
class BlockScope extends MethodScope {
BlockScope(Scope parent) : super(parent, parent.element);
}
class ClassScope extends Scope {
ClassScope(ClassElement element, LibraryElement library)
: super(new TopScope(library), element);
Element lookup(SourceString name) {
ClassElement cls = element;
Element result = cls.lookupLocalMember(name);
if (result !== null) return result;
result = cls.lookupTypeParameter(name);
if (result !== null) return result;
result = parent.lookup(name);
if (result != null) return result;
return cls.lookupSuperMember(name);
}
Element add(Element element) {
throw "Cannot add an element in a class scope";
}
}
class TopScope extends Scope {
LibraryElement get library() => element;
TopScope(LibraryElement library) : super(null, library);
Element lookup(SourceString name) {
return library.find(name);
}
Element add(Element element) {
throw "Cannot add an element in the top scope";
}
}