Files
sdk/lib/compiler/implementation/resolver.dart
T
2012-07-12 08:52:19 +00:00

2215 lines
76 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);
Type getType(TypeAnnotation annotation);
}
class TreeElementMapping implements TreeElements {
Map<Node, Element> map;
Map<Send, Selector> selectors;
Map<TypeAnnotation, Type> types;
TreeElementMapping()
: map = new LinkedHashMap<Node, Element>(),
selectors = new LinkedHashMap<Send, Selector>(),
types = new LinkedHashMap<TypeAnnotation, Type>();
operator []=(Node node, Element element) => map[node] = element;
operator [](Node node) => map[node];
void remove(Node node) { map.remove(node); }
void setType(TypeAnnotation annotation, Type type) {
types[annotation] = type;
}
Type getType(TypeAnnotation annotation) => types[annotation];
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(() {
ElementKind kind = element.kind;
if (kind === ElementKind.GENERATIVE_CONSTRUCTOR ||
kind === ElementKind.FUNCTION ||
kind === ElementKind.GETTER ||
kind === ElementKind.SETTER) {
return resolveMethodElement(element);
}
if (kind === ElementKind.FIELD) return resolveField(element);
if (kind === ElementKind.PARAMETER ||
kind === ElementKind.FIELD_PARAMETER) {
return resolveParameter(element);
}
compiler.unimplemented("resolve($element)",
node: element.parseNode(compiler));
});
}
SourceString getConstructorName(Send node) {
if (node.receiver !== null) {
return node.selector.asIdentifier().source;
} else {
return const SourceString('');
}
}
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)) {
final ClassElement classElement = constructor.enclosingElement;
final SourceString constructorName =
getConstructorName(initializers.head);
final SourceString className = classElement.name;
return classElement.lookupConstructor(className, constructorName);
}
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 (isConstructor) {
// 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);
if (redirection !== null) {
resolveRedirectingConstructor(resolver, tree, element, redirection);
}
} else if (tree.initializers != null) {
error(tree, MessageKind.FUNCTION_WITH_INITIALIZER);
}
visitBody(visitor, 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 visitBody(ResolverVisitor visitor, Statement body) {
visitor.visit(body);
}
void resolveConstructorImplementation(FunctionElement constructor,
FunctionExpression node) {
if (constructor.defaultImplementation !== constructor) return;
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".
// If the default class implements the interface then we must use the
// default class' name. Otherwise we look for a factory with the name
// of the interface.
SourceString name;
if (defaultClass.implementsInterface(intrface)) {
// TODO(ahe): Don't use string replacement here.
name = new SourceString(constructor.name.slowToString().replaceFirst(
intrface.name.slowToString(),
defaultClass.name.slowToString()));
} else {
name = constructor.name;
}
constructor.defaultImplementation = defaultClass.lookupConstructor(name);
if (constructor.defaultImplementation === null) {
// We failed to find a constructor named either
// "MyInterface.name" or "MyClass.name".
error(node,
MessageKind.CANNOT_FIND_CONSTRUCTOR2,
[name, defaultClass.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 resolveTypeAnnotation(Element element, TypeAnnotation annotation) {
if (annotation === null) return compiler.types.dynamicType;
ResolverVisitor visitor = new ResolverVisitor(compiler, element);
Type result = visitor.resolveTypeAnnotation(annotation);
if (result === null) {
// TODO(karklose): warning.
return compiler.types.dynamicType;
}
return result;
}
void resolveClass(ClassElement element) {
if (element.isResolved || element.isBeingResolved) return;
element.isBeingResolved = true;
measure(() {
ClassNode tree = element.parseNode(compiler);
ClassResolverVisitor visitor =
new ClassResolverVisitor(compiler, element.getLibrary(), element);
visitor.visit(tree);
element.isBeingResolved = false;
element.isResolved = true;
while (!toResolve.isEmpty()) {
ClassElement classElement = toResolve.removeFirst();
classElement.ensureResolved(compiler);
}
checkMembers(element);
});
}
checkMembers(ClassElement cls) {
if (cls === compiler.objectClass) return;
cls.forEachMember((holder, member) {
checkAbstractField(member);
checkValidOverride(member, cls.lookupSuperMember(member.name));
});
}
void checkAbstractField(Element member) {
if (member is !AbstractFieldElement) return;
if (member.getter === null) return;
if (member.setter === null) return;
int getterFlags = member.getter.modifiers.flags | Modifiers.FLAG_ABSTRACT;
int setterFlags = member.setter.modifiers.flags | Modifiers.FLAG_ABSTRACT;
if (getterFlags !== setterFlags) {
final mismatchedFlags =
new Modifiers.withFlags(null, getterFlags ^ setterFlags);
compiler.reportMessage(
compiler.spanFromElement(member.getter),
MessageKind.GETTER_MISMATCH.error([mismatchedFlags]),
api.Diagnostic.ERROR);
compiler.reportMessage(
compiler.spanFromElement(member.setter),
MessageKind.SETTER_MISMATCH.error([mismatchedFlags]),
api.Diagnostic.ERROR);
}
}
reportErrorWithContext(Element errorneousElement,
MessageKind errorMessage,
Element contextElement,
MessageKind contextMessage) {
compiler.reportMessage(
compiler.spanFromElement(errorneousElement),
errorMessage.error([contextElement.name,
contextElement.getEnclosingClass().name]),
api.Diagnostic.ERROR);
compiler.reportMessage(
compiler.spanFromElement(contextElement),
contextMessage.error(),
api.Diagnostic.INFO);
}
void checkValidOverride(Element member, Element superMember) {
if (superMember === null) return;
if (member.modifiers.isStatic()) {
reportErrorWithContext(
member, MessageKind.NO_STATIC_OVERRIDE,
superMember, MessageKind.NO_STATIC_OVERRIDE_CONT);
} else {
FunctionElement superFunction = superMember.asFunctionElement();
FunctionElement function = member.asFunctionElement();
if (superFunction === null || superFunction.isAccessor()) {
// Field or accessor in super.
if (function !== null && !function.isAccessor()) {
// But a plain method in this class.
reportErrorWithContext(
member, MessageKind.CANNOT_OVERRIDE_FIELD_WITH_METHOD,
superMember, MessageKind.CANNOT_OVERRIDE_FIELD_WITH_METHOD_CONT);
}
} else {
// Instance method in super.
if (function === null || function.isAccessor()) {
// But a field (or accessor) in this class.
reportErrorWithContext(
member, MessageKind.CANNOT_OVERRIDE_METHOD_WITH_FIELD,
superMember, MessageKind.CANNOT_OVERRIDE_METHOD_WITH_FIELD_CONT);
} else {
// Both are plain instance methods.
if (superFunction.requiredParameterCount(compiler) !=
function.requiredParameterCount(compiler)) {
reportErrorWithContext(
member,
MessageKind.BAD_ARITY_OVERRIDE,
superMember,
MessageKind.BAD_ARITY_OVERRIDE_CONT);
}
// TODO(ahe): Check optional parameters.
}
}
}
}
FunctionSignature resolveSignature(FunctionElement element) {
return compiler.withCurrentElement(element, () {
FunctionExpression node =
compiler.parser.measure(() => element.parseNode(compiler));
return measure(() => SignatureResolver.analyze(
compiler, node.parameters, node.returnType, element));
});
}
FunctionSignature resolveTypedef(TypedefElement element) {
return compiler.withCurrentElement(element, () {
Typedef node =
compiler.parser.measure(() => element.parseNode(compiler));
return measure(() => SignatureResolver.analyze(
compiler, node.formals, node.returnType, element));
});
}
FunctionType computeFunctionType(Element element,
FunctionSignature signature) {
LinkBuilder<Type> parameterTypes = new LinkBuilder<Type>();
for (Link<Element> link = signature.requiredParameters;
!link.isEmpty();
link = link.tail) {
parameterTypes.addLast(link.head.computeType(compiler));
// TODO(karlklose): optional parameters.
}
return new FunctionType(signature.returnType,
parameterTypes.toLink(),
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 resolveSuperOrThisForSend(FunctionElement constructor,
FunctionExpression functionNode,
Send call) {
// Resolve the arguments, and make sure the call gets a selector
// by calling handleArguments.
ResolverTask resolver = visitor.compiler.resolver;
visitor.inStaticContext( () => visitor.handleArguments(call) );
Selector selector = visitor.mapping.getSelector(call);
bool isSuperCall = Initializers.isSuperConstructorCall(call);
SourceString constructorName = resolver.getConstructorName(call);
Element result = resolveSuperOrThis(
constructor, isSuperCall, false, constructorName, selector, call);
visitor.useElement(call, result);
return result;
}
void resolveImplicitSuperConstructorSend(FunctionElement constructor,
FunctionExpression functionNode) {
// If the class has a super resolve the implicit super call.
ClassElement classElement = constructor.enclosingElement;
ClassElement superClass = classElement.superclass;
if (classElement != visitor.compiler.objectClass) {
assert(superClass !== null);
assert(superClass.isResolved);
var element = resolveSuperOrThis(constructor, true, true,
const SourceString(''),
Selector.INVOCATION_0, functionNode);
visitor.world.registerStaticUse(element);
}
}
Element resolveSuperOrThis(FunctionElement constructor,
bool isSuperCall,
bool isImplicitSuperCall,
SourceString constructorName,
Selector selector,
Node diagnosticNode) {
ClassElement lookupTarget = constructor.enclosingElement;
bool validTarget = true;
FunctionElement result;
if (isSuperCall) {
// Calculate correct lookup target and constructor name.
if (lookupTarget === visitor.compiler.objectClass) {
error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
} else {
lookupTarget = lookupTarget.supertype.element;
}
}
// Lookup constructor and try to match it to the selector.
ResolverTask resolver = visitor.compiler.resolver;
final SourceString className = lookupTarget.name;
result = lookupTarget.lookupConstructor(className, constructorName);
if (result === null) {
String classNameString = className.slowToString();
String constructorNameString = constructorName.slowToString();
String name = (constructorName === const SourceString(''))
? classNameString
: "$classNameString.$constructorNameString";
MessageKind kind = isImplicitSuperCall
? MessageKind.CANNOT_RESOLVE_CONSTRUCTOR_FOR_IMPLICIT
: MessageKind.CANNOT_RESOLVE_CONSTRUCTOR;
error(diagnosticNode, kind, [name]);
} else {
if (!selector.applies(result, visitor.compiler)) {
MessageKind kind = isImplicitSuperCall
? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT
: MessageKind.NO_MATCHING_CONSTRUCTOR;
error(diagnosticNode, kind);
}
}
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 resolveSuperOrThisForSend(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) {
initializers = const EmptyLink<Node>();
} else {
initializers = functionNode.initializers.nodes;
}
FunctionElement result;
bool resolvedSuper = false;
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();
if (Initializers.isSuperConstructorCall(call)) {
if (resolvedSuper) {
error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
}
resolveSuperOrThisForSend(constructor, functionNode, call);
resolvedSuper = true;
} 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);
}
return resolveSuperOrThisForSend(constructor, functionNode, call);
} else {
visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
return null;
}
} else {
error(link.head, MessageKind.INVALID_INITIALIZER);
}
}
if (!resolvedSuper) {
resolveImplicitSuperConstructorSend(constructor, functionNode);
}
return null; // If there was no redirection always return null.
}
}
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 Map<String, LabelElement> labels;
LabeledStatementLabelScope(this.outer, this.labels);
LabelElement lookup(String labelName) {
LabelElement label = labels[labelName];
if (label !== null) 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(Map<String, LabelElement> elements) {
labels = new LabeledStatementLabelScope(labels, elements);
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 TypeResolver {
final Compiler compiler;
TypeResolver(this.compiler);
Element resolveTypeName(Scope context, TypeAnnotation node) {
Identifier typeName = node.typeName.asIdentifier();
Send send = node.typeName.asSend();
if (send !== null) {
typeName = send.selector;
}
if (typeName.source.stringValue === 'void') {
return compiler.types.voidType.element;
} else if (send !== null) {
Element e = context.lookup(send.receiver.asIdentifier().source);
if (e !== null && e.kind === ElementKind.PREFIX) {
// The receiver is a prefix. Lookup in the imported members.
PrefixElement prefix = e;
return prefix.lookupLocalMember(typeName.source);
} else if (e !== null && e.kind === ElementKind.CLASS) {
// The receiver is the class part of a named constructor.
return e;
} else {
return null;
}
} else {
return context.lookup(typeName.source);
}
}
Type resolveTypeAnnotation(TypeAnnotation node,
[Scope inContext, ClassElement inClass,
onFailure(Node, MessageKind, [List arguments]),
whenResolved(Node, Type)]) {
if (onFailure === null) {
onFailure = (n, k, [arguments]) {};
}
if (whenResolved === null) {
whenResolved = (n, t) {};
}
if (inClass !== null) {
inContext = new ClassScope(inClass, inClass.getLibrary());
}
if (inContext === null) {
compiler.internalError('resolveTypeAnnotation: no scope specified');
}
return resolveTypeAnnotationInContext(inContext, node, onFailure,
whenResolved);
}
Type resolveTypeAnnotationInContext(Scope context, TypeAnnotation node,
onFailure, whenResolved) {
Element element = resolveTypeName(context, node);
Type type;
if (element === null) {
onFailure(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]);
} else if (!element.impliesType()) {
onFailure(node, MessageKind.NOT_A_TYPE, [node.typeName]);
} else {
if (element === compiler.types.voidType.element ||
element === compiler.types.dynamicType.element) {
type = element.computeType(compiler);
} else if (element.isClass()) {
ClassElement cls = element;
if (!cls.isResolved) compiler.resolveClass(cls);
LinkBuilder<Type> arguments = new LinkBuilder<Type>();
if (node.typeArguments !== null) {
int index = 0;
for (Link<Node> typeArguments = node.typeArguments.nodes;
!typeArguments.isEmpty();
typeArguments = typeArguments.tail) {
if (++index > cls.typeParameters.length) {
onFailure(typeArguments.head,
MessageKind.ADDITIONAL_TYPE_ARGUMENT);
}
Type argType = resolveTypeAnnotationInContext(context,
typeArguments.head,
onFailure,
whenResolved);
arguments.addLast(argType);
}
if (index < cls.typeParameters.length) {
onFailure(node.typeArguments, MessageKind.MISSING_TYPE_ARGUMENT);
}
}
if (cls.typeParameters.length == 0) {
// Return the canonical type if it has no type parameters.
type = cls.computeType(compiler);
} else {
type = new InterfaceType(cls, arguments.toLink());
}
} else if (element.isTypedef()) {
type = element.computeType(compiler);
} else if (element.isTypeVariable()) {
type = element.computeType(compiler);
} else {
compiler.cancel("unexpected element kind ${element.kind}",
node: node);
}
}
whenResolved(node, type);
return type;
}
}
class ResolverVisitor extends CommonResolverVisitor<Element> {
final TreeElementMapping mapping;
final Element enclosingElement;
final TypeResolver typeResolver;
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.currentClass = element.isMember() ? element.enclosingElement : null,
this.statementScope = new StatementScope(),
typeResolver = new TypeResolver(compiler),
super(compiler) {
LibraryElement library = element.getLibrary();
element = element.getEnclosingMember();
if (element !== null) {
context = new ClassScope(element.enclosingElement, library);
} else {
this.context = new TopScope(library);
}
}
Enqueuer get world() => compiler.enqueuer.resolution;
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);
}
}
Element visitTypeAnnotation(TypeAnnotation node) {
Type type = resolveTypeAnnotation(node);
if (type !== null) return type.element;
return null;
}
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;
mapping[node] = element;
if (element.isTopLevel() || element.isMember()) {
if (element.isInstanceMember()) {
var send = node.asSend();
if (send !== null) {
Selector selector = mapping.getSelector(send);
if (selector !== null) {
world.registerDynamicInvocation(element.name, selector);
}
}
} else {
if (!element.isPrefix() &&
!element.isClass() &&
!element.isTypedef() &&
!element.isTypeVariable()) {
world.registerStaticUse(element);
}
}
}
return element;
}
Type useType(TypeAnnotation annotation, Type type) {
if (type !== null) {
mapping.setType(annotation, type);
useElement(annotation, type.element);
}
return type;
}
void setupFunction(FunctionExpression node, FunctionElement function) {
context = new MethodScope(context, function);
// Put the parameters in scope.
FunctionSignature functionParameters =
function.computeSignature(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;
});
}
visitCascade(Cascade node) {
visit(node.expression);
}
visitCascadeReceiver(CascadeReceiver node) {
visit(node.expression);
}
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;
}
Type resolveTypeTest(Node argument) {
TypeAnnotation node = argument.asTypeAnnotation();
if (node === null) {
// node is of the form !Type.
node = argument.asSend().receiver.asTypeAnnotation();
if (node === null) compiler.cancel("malformed send");
}
return 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 Selector.invocation(count, namedArguments));
}
visitSend(Send node) {
Element target = resolveSend(node);
if (node.isOperator) {
Operator op = node.selector.asOperator();
if (op.source.stringValue === 'is' || op.source.stringValue === 'as') {
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): We should do the check in
// visitExpressionStatement instead.
if (target === compiler.assertMethod && !node.isCall) {
// We can only use assert by calling it.
if (!inInstanceContext) {
error(node, MessageKind.MISSING_ARGUMENTS_TO_ASSERT, [node]);
}
target = null;
}
// TODO(ngeoffray): Warn if target is null and the send is
// unqualified.
useElement(node, target);
if (target === null) registerDynamicSend(node);
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.
registerDynamicSend(node);
return useElement(node, setter);
}
registerDynamicSend(Send node) {
Identifier id = node.selector.asIdentifier();
if (id === null) return;
SourceString name = node.selector.asIdentifier().source;
if (node.isIndex && !node.arguments.tail.isEmpty()) {
name = Elements.constructOperatorName(
const SourceString('operator'),
const SourceString('[]='));
} else if (node.selector.asOperator() != null) {
switch (name.stringValue) {
case '===':
case '!==':
case '!':
case '&&':
case '||':
case 'is':
case 'as':
case '>>>':
return null;
}
name = Elements.constructOperatorName(
const SourceString('operator'),
name,
node.argumentsNode is Prefix);
}
Selector selector = mapping.getSelector(node);
if (Selector.GETTER === selector) {
world.registerDynamicGetter(name, selector);
} else if (Selector.SETTER === selector) {
world.registerDynamicSetter(name, selector);
// Also register the getter for compound assignments.
world.registerDynamicGetter(name, Selector.GETTER);
} else {
world.registerDynamicInvocation(name, selector);
}
var interceptor = new Interceptors(compiler).getStaticInterceptor(
name,
node.argumentCount());
if (interceptor !== null) {
world.registerStaticUse(interceptor);
}
}
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);
compiler.withCurrentElement(constructor, () {
FunctionExpression tree = constructor.parseNode(compiler);
compiler.resolver.resolveConstructorImplementation(constructor, tree);
});
world.registerStaticUse(constructor.defaultImplementation);
ClassElement cls = constructor.defaultImplementation.enclosingElement;
world.registerInstantiatedClass(cls);
cls.forEachInstanceField(
includeBackendMembers: false,
includeSuperMembers: true,
f: (ClassElement enclosingClass, Element member) {
world.addToWorkList(member);
});
return null;
}
TypeAnnotation getTypeAnnotationFromSend(Send send) {
if (send.selector.asTypeAnnotation() !== null) {
return send.selector;
} else if (send.selector.asSend() !== null) {
Send selector = send.selector;
if (selector.receiver.asTypeAnnotation() !== null) {
return selector.receiver;
}
} else {
compiler.internalError("malformed send in new expression");
}
}
FunctionElement resolveConstructor(NewExpression node) {
FunctionElement constructor =
node.accept(new ConstructorResolver(compiler, this));
TypeAnnotation annotation = getTypeAnnotationFromSend(node.send);
Type type = resolveTypeRequired(annotation);
if (constructor === null) {
Element resolved = (type != null) ? type.element : null;
if (resolved !== null && resolved.kind === ElementKind.TYPE_VARIABLE) {
error(node, MessageKind.TYPE_VARIABLE_AS_CONSTRUCTOR);
return null;
} else {
error(node.send, MessageKind.CANNOT_FIND_CONSTRUCTOR, [node.send]);
return null;
}
}
return constructor;
}
Type resolveTypeRequired(TypeAnnotation node) {
bool old = typeRequired;
typeRequired = true;
Type result = resolveTypeAnnotation(node);
typeRequired = old;
return result;
}
Type resolveTypeAnnotation(TypeAnnotation node) {
Function report = typeRequired ? error : warning;
return typeResolver.resolveTypeAnnotation(node, inContext: context,
onFailure: report,
whenResolved: useType);
}
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]);
}
// TODO(lrn): Handle continues to switch cases.
if (target.statement is SwitchCase) {
unimplemented(node, "continue to switch case");
}
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, []);
}
}
visitLabel(Label node) {
// Labels are handled by their containing statements/cases.
}
visitLabeledStatement(LabeledStatement node) {
Statement body = node.statement;
TargetElement targetElement = getOrCreateTargetElement(body);
Map<String, LabelElement> labelElements = <LabelElement>{};
for (Label label in node.labels) {
String labelName = label.slowToString();
if (labelElements.containsKey(labelName)) continue;
LabelElement element = targetElement.addLabel(label, labelName);
labelElements[labelName] = element;
}
statementScope.enterLabelScope(labelElements);
visit(node.statement);
statementScope.exitLabelScope();
labelElements.forEach((String labelName, LabelElement element) {
if (element.isTarget) {
mapping[element.label] = element;
} else {
warning(element.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;
for (Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is! Label) continue;
Label label = labelOrCase;
String labelName = label.slowToString();
LabelElement existingElement = continueLabels[labelName];
if (existingElement !== null) {
// It's an error if the same label occurs twice in the same switch.
warning(label, 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(label, MessageKind.DUPLICATE_LABEL, [labelName]);
warning(existingElement.label,
MessageKind.EXISTING_LABEL, [labelName]);
}
}
TargetElement targetElement =
new TargetElement(switchCase,
statementScope.nestingLevel,
enclosingElement);
mapping[switchCase] = targetElement;
LabelElement labelElement =
new LabelElement(label, labelName,
targetElement, enclosingElement);
mapping[label] = labelElement;
continueLabels[labelName] = labelElement;
}
cases = cases.tail;
// Test that only the last case, if any, is a default case.
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) {
if (!label.isContinueTarget) {
TargetElement targetElement = label.target;
SwitchCase switchCase = targetElement.statement;
mapping.remove(switchCase);
mapping.remove(label.label);
}
});
}
visitSwitchCase(SwitchCase node) {
node.labelsAndCases.accept(this);
visitIn(node.statements, new BlockScope(context));
}
visitCaseMatch(CaseMatch node) {
visit(node.expression);
}
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);
}
final objectElement = compiler.objectClass;
if (classElement !== objectElement && classElement.supertype === null) {
if (objectElement === null) {
compiler.internalError("Internal error: cannot resolve Object",
node: node);
} else if (!objectElement.isResolved) {
compiler.resolver.toResolve.add(objectElement);
}
classElement.supertype = new InterfaceType(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', node: node);
} 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 cls,
[Set<ClassElement> seen]) {
Link<Type> allSupertypes = cls.allSupertypes;
if (allSupertypes !== null) return allSupertypes;
if (seen === null) {
seen = new Set<ClassElement>();
}
if (seen.contains(cls)) {
error(cls.parseNode(compiler),
MessageKind.CYCLIC_CLASS_HIERARCHY,
[cls.name]);
cls.allSupertypes = const EmptyLink<Type>();
} else {
cls.ensureResolved(compiler);
calculateAllSupertypes(cls, seen);
}
return cls.allSupertypes;
}
void calculateAllSupertypes(ClassElement cls, 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 (cls.allSupertypes !== null) return;
final Type supertype = cls.supertype;
if (seen.contains(cls)) {
error(cls.parseNode(compiler),
MessageKind.CYCLIC_CLASS_HIERARCHY,
[cls.name]);
cls.allSupertypes = const EmptyLink<Type>();
} else if (supertype != null) {
seen.add(cls);
Link<Type> superSupertypes =
getOrCalculateAllSupertypes(supertype.element, seen);
Link<Type> supertypes = new Link<Type>(supertype, superSupertypes);
for (Link<Type> interfaces = cls.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(cls);
cls.allSupertypes = supertypes;
} else {
cls.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) {
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);
}
SourceString getParameterName(Send node) {
var identifier = node.selector.asIdentifier();
if (identifier !== null) {
// Normal parameter: [:Type name:].
return identifier.source;
} else {
// Function type parameter: [:void name(Type arg):].
var functionExpression = node.selector.asFunctionExpression();
if (functionExpression !== null &&
functionExpression.name.asIdentifier() !== null) {
return functionExpression.name.asIdentifier().source;
} else {
cancel(node,
'internal error: unimplemented receiver on parameter send');
}
}
}
// 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 {
SourceString name = getParameterName(node);
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(name,
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);
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 FunctionSignature analyze(Compiler compiler,
NodeList formalParameters,
Node returnNode,
Element element) {
SignatureResolver visitor = new SignatureResolver(compiler, element);
LinkBuilder<Element> parametersBuilder =
visitor.analyzeNodes(formalParameters.nodes);
Link<Element> parameters = parametersBuilder.toLink();
Type returnType =
compiler.resolveTypeAnnotation(element, returnNode);
return new FunctionSignature(parameters,
visitor.optionalParameters,
parametersBuilder.length,
visitor.optionalParameterCount,
returnType);
}
// TODO(ahe): This is temporary.
void resolveExpression(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 newElement) {
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 found = elements[name];
if (found !== null) return found;
return parent.lookup(name);
}
Element add(Element newElement) {
if (elements.containsKey(newElement.name)) {
return elements[newElement.name];
}
elements[newElement.name] = newElement;
return newElement;
}
}
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 newElement) {
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 newElement) {
throw "Cannot add an element in the top scope";
}
}