Chromium Code Reviews| Index: lib/compiler/implementation/resolver.dart |
| diff --git a/lib/compiler/implementation/resolver.dart b/lib/compiler/implementation/resolver.dart |
| index 3de67b305322f456a1d5ef3c13fef8c238b0f5ca..ee2716a23a982d4b8ab9100c8f83eb2e0e275856 100644 |
| --- a/lib/compiler/implementation/resolver.dart |
| +++ b/lib/compiler/implementation/resolver.dart |
| @@ -29,6 +29,8 @@ class TreeElementMapping implements TreeElements { |
| } |
| return true; |
| })); |
| + // TODO(johnniwinther): Simplify this invariant to use only declarations in |
| + // [TreeElements]. |
| assert(invariant(node, () { |
| if (!element.isErroneous() && currentElement != null && element.isPatch) { |
| return currentElement.getImplementationLibrary().isPatch; |
| @@ -94,7 +96,12 @@ class ResolverTask extends CompilerTask { |
| } |
| FunctionElement resolveConstructorRedirection(FunctionElement constructor) { |
| + if (constructor.isPatched){ |
| + checkMatchingSignatures(constructor, constructor.patch); |
| + constructor = constructor.patch; |
| + } |
| FunctionExpression node = constructor.parseNode(compiler); |
| + |
| // A synthetic constructor does not have a node. |
| if (node === null) return null; |
| if (node.initializers === null) return null; |
| @@ -126,6 +133,72 @@ class ResolverTask extends CompilerTask { |
| } |
| } |
| + void checkMatchingParameters(FunctionElement origin, |
|
ahe
2012/09/24 12:30:18
Use the word "patch" in method name.
Johnni Winther
2012/09/25 09:01:07
Done.
|
| + Link<Element> originParameters, |
| + Link<Element> patchParameters) { |
| + while (!originParameters.isEmpty()) { |
| + Element originParameter = originParameters.head; |
| + Element patchParameter = patchParameters.head; |
| + String originParameterText = |
| + originParameter.parseNode(compiler).toString(); |
| + String patchParameterText = |
| + patchParameter.parseNode(compiler).toString(); |
| + if (originParameterText != patchParameterText) { |
| + error(originParameter.parseNode(compiler), |
| + MessageKind.PATCH_PARAMETER_MISMATCH, |
| + [origin.name, originParameterText, patchParameterText]); |
| + } |
| + |
| + originParameters = originParameters.tail; |
| + patchParameters = patchParameters.tail; |
| + } |
| + } |
| + |
| + void checkMatchingSignatures(FunctionElement origin, FunctionElement patch) { |
|
ahe
2012/09/24 12:30:18
Use the word "patch" in method name.
Johnni Winther
2012/09/25 09:01:07
Done.
|
| + // TODO(johnniwinther): Show both origin and patch locations on errors. |
| + FunctionExpression originTree = compiler.withCurrentElement(origin, () { |
| + return origin.parseNode(compiler); |
| + }); |
| + FunctionSignature originSignature = compiler.withCurrentElement(origin, () { |
| + return origin.computeSignature(compiler); |
| + }); |
| + FunctionExpression patchTree = compiler.withCurrentElement(patch, () { |
| + return patch.parseNode(compiler); |
| + }); |
| + FunctionSignature patchSignature = compiler.withCurrentElement(patch, () { |
| + return patch.computeSignature(compiler); |
| + }); |
| + |
| + if (originSignature.returnType != patchSignature.returnType) { |
| + Node errorNode = |
| + originTree.returnType !== null ? originTree.returnType : originTree; |
| + error(errorNode, MessageKind.PATCH_RETURN_TYPE_MISMATCH, |
| + [origin.name, originSignature.returnType, patchSignature.returnType]); |
| + } |
| + if (originSignature.requiredParameterCount != |
| + patchSignature.requiredParameterCount) { |
| + error(originTree, |
| + MessageKind.PATCH_REQUIRED_PARAMETER_COUNT_MISMATCH, |
| + [origin.name, originSignature.requiredParameterCount, |
| + patchSignature.requiredParameterCount]); |
| + } else { |
| + checkMatchingParameters(origin, |
| + originSignature.requiredParameters, |
| + patchSignature.requiredParameters); |
| + } |
| + if (originSignature.optionalParameterCount != |
| + patchSignature.optionalParameterCount) { |
| + error(originTree, |
| + MessageKind.PATCH_OPTIONAL_PARAMETER_COUNT_MISMATCH, |
| + [origin.name, originSignature.optionalParameterCount, |
| + patchSignature.optionalParameterCount]); |
| + } else { |
| + checkMatchingParameters(origin, |
| + originSignature.optionalParameters, |
| + patchSignature.optionalParameters); |
| + } |
| + } |
| + |
| TreeElements resolveMethodElement(FunctionElement element) { |
| assert(invariant(element, element.isDeclaration)); |
| return compiler.withCurrentElement(element, () { |
| @@ -136,32 +209,40 @@ class ResolverTask extends CompilerTask { |
| assert(isConstructor); |
| return elements; |
| } |
| - FunctionExpression tree = element.parseNode(compiler); |
| - if (isConstructor) { |
| - if (tree.returnType !== null) { |
| - error(tree, MessageKind.CONSTRUCTOR_WITH_RETURN_TYPE); |
| + if (element.isPatched) { |
| + checkMatchingSignatures(element, element.patch); |
| + element = element.patch; |
| + } |
| + return compiler.withCurrentElement(element, () { |
| + FunctionExpression tree = element.parseNode(compiler); |
| + |
| + // TODO(johnniwinther): Check signature match. |
|
ahe
2012/09/24 12:30:18
Stale TODO?
Johnni Winther
2012/09/25 09:01:07
Done.
|
| + if (isConstructor) { |
| + if (tree.returnType !== null) { |
| + error(tree, MessageKind.CONSTRUCTOR_WITH_RETURN_TYPE); |
| + } |
| + resolveConstructorImplementation(element, tree); |
| } |
| - 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); |
| + 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); |
| } |
| - } else if (tree.initializers != null) { |
| - error(tree, MessageKind.FUNCTION_WITH_INITIALIZER); |
| - } |
| - visitBody(visitor, tree.body); |
| + visitBody(visitor, tree.body); |
| - return visitor.mapping; |
| + return visitor.mapping; |
| + }); |
| }); |
| } |
| @@ -307,7 +388,8 @@ class ResolverTask extends CompilerTask { |
| void checkMembers(ClassElement cls) { |
| if (cls === compiler.objectClass) return; |
| - cls.forEachMember((holder, member) { |
| + cls.forEachMember(includeInjectedMembers: true, |
| + f: (holder, member) { |
| // Perform various checks as side effect of "computing" the type. |
| member.computeType(compiler); |
| @@ -534,8 +616,8 @@ class InitializerResolver { |
| // Lookup target field. |
| Element target; |
| if (isFieldInitializer(init)) { |
| - final ClassElement classElement = constructor.getEnclosingClass(); |
| - target = classElement.lookupLocalMember(name); |
| + Scope localScope = constructor.getEnclosingClass().buildLocalScope(); |
| + target = localScope.lookup(name); |
| if (target === null) { |
| error(selector, MessageKind.CANNOT_RESOLVE, [name]); |
| } else if (target.kind != ElementKind.FIELD) { |
| @@ -1326,6 +1408,10 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| // for a real error message. |
| error(node.receiver, MessageKind.GENERIC, ["Object has no superclass"]); |
| } |
| + // TODO(johnniwinther): Is this the right semantics? Should [: super. :] |
| + // not access inherited methods on super? |
| + // TODO(johnniwinther): Ensure correct behavior if currentClass is a |
| + // patch. |
| target = currentClass.lookupSuperMember(name); |
| // [target] may be null which means invoking noSuchMethod on |
| // super. |
| @@ -1333,8 +1419,13 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| return null; |
| } else if (resolvedReceiver.kind === ElementKind.CLASS) { |
| ClassElement receiverClass = resolvedReceiver; |
| - target = receiverClass.ensureResolved(compiler).lookupLocalMember(name); |
| + receiverClass.ensureResolved(compiler); |
| + target = receiverClass.buildLocalScope().lookup(name); |
| if (target === null) { |
| + // TODO(johnniwinther): With the simplified [TreeElements] invariant, |
| + // try to resolve ghost elements if [currentClass] is in the patch |
| + // library of [receiverClass]. |
| + |
| // TODO(karlklose): this should be reported by the caller of |
| // [resolveSend] to select better warning messages for getters and |
| // setters. |
| @@ -1672,6 +1763,7 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| cls.forEachInstanceField( |
| includeBackendMembers: false, |
| includeSuperMembers: true, |
| + includeInjectedMembers: true, |
| f: (ClassElement enclosingClass, Element member) { |
| world.addToWorkList(member); |
| }); |
| @@ -2321,7 +2413,7 @@ class ClassSupertypeResolver extends CommonResolverVisitor { |
| ClassElement classElement; |
| ClassSupertypeResolver(Compiler compiler, ClassElement cls) |
| - : context = new TopScope(cls.getLibrary()), |
| + : context = cls.buildEnclosingScope(), |
| this.classElement = cls, |
| super(compiler); |
| @@ -2795,7 +2887,7 @@ class TypeDeclarationScope extends Scope { |
| } |
| String toString() => |
| - '$element${element.typeVariables} > $parent'; |
| + 'TypeDeclarationScope($element)'; |
| } |
| class MethodScope extends Scope { |
| @@ -2817,13 +2909,13 @@ class MethodScope extends Scope { |
| return newElement; |
| } |
| - String toString() => '$element${elements.getKeys()} > $parent'; |
| + String toString() => '$element${elements.getKeys()}'; |
| } |
| class BlockScope extends MethodScope { |
| BlockScope(Scope parent) : super(parent, parent.element); |
| - String toString() => 'block${elements.getKeys()} > $parent'; |
| + String toString() => 'block${elements.getKeys()}'; |
| } |
| /** |
| @@ -2835,7 +2927,9 @@ class ClassScope extends TypeDeclarationScope { |
| bool inStaticContext = false; |
| ClassScope(Scope parentScope, ClassElement element) |
| - : super(parentScope, element); |
| + : super(parentScope, element) { |
| + assert(parent !== null); |
| + } |
| Element localLookup(SourceString name) { |
| ClassElement cls = element; |
| @@ -2845,13 +2939,15 @@ class ClassScope extends TypeDeclarationScope { |
| // If not in a static context, we can lookup in the |
| // TypeDeclaration scope, which contains the type variables of |
| // the class. |
| - return super.localLookup(name); |
| + result = super.localLookup(name); |
| } |
| - return null; |
| + return result; |
| } |
| Element lookup(SourceString name) { |
| - Element result = super.lookup(name); |
| + Element result = localLookup(name); |
| + if (result !== null) return result; |
| + result = parent.lookup(name); |
| if (result !== null) return result; |
| ClassElement cls = element; |
| return cls.lookupSuperMember(name); |
| @@ -2861,7 +2957,90 @@ class ClassScope extends TypeDeclarationScope { |
| throw "Cannot add an element in a class scope"; |
| } |
| - String toString() => '$element > $parent'; |
| + String toString() => 'ClassScope($element)'; |
| +} |
| + |
| +class PatchClassScope extends TypeDeclarationScope { |
| + bool inStaticContext = false; |
| + ClassElement get origin => element; |
| + final ClassElement patch; |
| + |
| + PatchClassScope(Scope parentScope, |
| + ClassElement origin, ClassElement this.patch) |
| + : super(parentScope, origin) { |
| + assert(parent !== null); |
| + } |
| + |
| + Element localLookup(SourceString name) { |
| + Element result = patch.lookupLocalMember(name); |
| + if (result !== null) return result; |
| + result = origin.lookupLocalMember(name); |
| + if (result !== null) return result; |
| + if (!inStaticContext) { |
| + // If not in a static context, we can lookup in the |
| + // TypeDeclaration scope, which contains the type variables of |
| + // the class. |
| + result = super.localLookup(name); |
| + if (result !== null) return result; |
| + } |
| + result = parent.lookup(name); |
| + if (result !== null) return result; |
| + return result; |
| + } |
| + |
| + Element lookup(SourceString name) { |
| + Element result = localLookup(name); |
| + if (result !== null) return result; |
| + // TODO(johnniwinther): Should we support patch lookup on supertypes? |
| + return origin.lookupSuperMember(name); |
| + } |
| + |
| + Element add(Element newElement) { |
| + throw "Cannot add an element in a class scope"; |
| + } |
| + |
| + String toString() => 'PatchClassScope($origin,$patch)'; |
| +} |
| + |
| +class LocalClassScope extends Scope { |
| + LocalClassScope(ClassElement element) |
| + : super(null, element); |
| + |
| + Element lookup(SourceString name) => localLookup(name); |
| + |
| + Element localLookup(SourceString name) { |
| + ClassElement cls = element; |
| + return cls.lookupLocalMember(name); |
| + } |
| + |
| + Element add(Element newElement) { |
| + throw "Cannot add an element in a class scope"; |
| + } |
| + |
| + String toString() => 'LocalClassScope($element)'; |
| +} |
| + |
| +class LocalPatchClassScope extends Scope { |
| + ClassElement get origin => element; |
| + final ClassElement patch; |
| + |
| + LocalPatchClassScope(ClassElement origin, ClassElement this.patch) |
| + : super(null, origin); |
| + |
| + Element lookup(SourceString name) => localLookup(name); |
| + |
| + Element localLookup(SourceString name) { |
| + Element result = patch.lookupLocalMember(name); |
| + if (result !== null) return result; |
| + return origin.lookupLocalMember(name); |
| + } |
| + |
| + |
| + Element add(Element newElement) { |
| + throw "Cannot add an element in a class scope"; |
| + } |
| + |
| + String toString() => 'LocalPatchClassScope($origin,$patch)'; |
| } |
| class TopScope extends Scope { |
| @@ -2876,5 +3055,32 @@ class TopScope extends Scope { |
| Element add(Element newElement) { |
| throw "Cannot add an element in the top scope"; |
| } |
| - String toString() => '$element'; |
| + String toString() => 'LibraryScope($element)'; |
| +} |
| + |
| +class PatchLibraryScope extends Scope { |
| + LibraryElement get origin => element; |
| + final LibraryElement patch; |
| + |
| + PatchLibraryScope(LibraryElement origin, LibraryElement this.patch) |
| + : super(null, origin); |
| + |
| + Element localLookup(SourceString name) { |
| + Element result = patch.find(name); |
| + if (result !== null) { |
| + return result; |
| + } |
| + result = origin.find(name); |
| + if (result !== null) { |
| + return result; |
| + } |
| + return result; |
| + } |
| + Element lookup(SourceString name) => localLookup(name); |
| + Element lexicalLookup(SourceString name) => localLookup(name); |
| + |
| + Element add(Element newElement) { |
| + throw "Cannot add an element in a patch library scope"; |
| + } |
| + String toString() => 'PatchLibraryScope($origin,$patch)'; |
| } |