Index: src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java =================================================================== --- src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java (revision 20261) +++ src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java (revision ) @@ -1,5 +1,5 @@ /* - * Copyright 2008-2009 the original author or authors. + * Copyright 2008-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package org.codehaus.groovy.transform; +import groovy.lang.GroovyObject; +import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.ExpressionStatement; @@ -22,13 +24,16 @@ import org.codehaus.groovy.classgen.Verifier; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.SyntaxException; import org.codehaus.groovy.syntax.Token; import org.codehaus.groovy.syntax.Types; import org.objectweb.asm.Opcodes; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Map; +import java.util.List; import java.util.Set; /** @@ -41,10 +46,11 @@ @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) public class DelegateASTTransformation implements ASTTransformation, Opcodes { private static final ClassNode DEPRECATED_TYPE = new ClassNode(Deprecated.class); + private static final ClassNode GROOVYOBJECT_TYPE = new ClassNode(GroovyObject.class); public void visit(ASTNode[] nodes, SourceUnit source) { if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) { - throw new RuntimeException("Internal error: expecting [AnnotationNode, AnnotatedNode] but got: " + Arrays.asList(nodes)); + throw new GroovyBugError("Internal error: expecting [AnnotationNode, AnnotatedNode] but got: " + Arrays.asList(nodes)); } AnnotatedNode parent = (AnnotatedNode) nodes[1]; @@ -53,14 +59,17 @@ if (parent instanceof FieldNode) { FieldNode fieldNode = (FieldNode) parent; final ClassNode type = fieldNode.getType(); - final Map fieldMethods = type.getDeclaredMethodsMap(); + if (type.equals(ClassHelper.OBJECT_TYPE) || type.equals(GROOVYOBJECT_TYPE)) { + addError("@Delegate should not be used with type " + type.getName(), parent, source); + return; + } + final List fieldMethods = getAllMethods(type); final ClassNode owner = fieldNode.getOwner(); final Expression deprecatedElement = node.getMember("deprecated"); - final boolean deprecated = (deprecatedElement instanceof ConstantExpression && ((ConstantExpression) deprecatedElement).getValue().equals(true)); + final boolean deprecated = hasBooleanValue(deprecatedElement, true); - final Map ownMethods = owner.getDeclaredMethodsMap(); - for (Map.Entry e : fieldMethods.entrySet()) { - addDelegateMethod(fieldNode, owner, ownMethods, e, deprecated); + for (MethodNode mn : fieldMethods) { + addDelegateMethod(fieldNode, owner, getAllMethods(owner), mn, deprecated); } for (PropertyNode prop : type.getProperties()) { @@ -72,8 +81,7 @@ } final Expression interfacesElement = node.getMember("interfaces"); - if (interfacesElement instanceof ConstantExpression && ((ConstantExpression) interfacesElement).getValue().equals(false)) - return; + if (hasBooleanValue(interfacesElement, false)) return; final Set allInterfaces = type.getAllInterfaces(); final Set ownerIfaces = owner.getAllInterfaces(); @@ -89,6 +97,20 @@ } } + private List getAllMethods(ClassNode type) { + ClassNode node = type; + List result = new ArrayList(); + while (node != null) { + result.addAll(node.getMethods()); + node = node.getSuperClass(); + } + return result; + } + + private boolean hasBooleanValue(Expression expression, boolean bool) { + return expression instanceof ConstantExpression && ((ConstantExpression) expression).getValue().equals(bool); + } + private void addSetterIfNeeded(FieldNode fieldNode, ClassNode owner, PropertyNode prop, String name) { String setterName = "set" + Verifier.capitalize(name); if ((prop.getModifiers() & ACC_FINAL) == 0 && owner.getSetterMethod(setterName) == null) { @@ -122,16 +144,27 @@ } } - private void addDelegateMethod(FieldNode fieldNode, ClassNode owner, Map ownMethods, Map.Entry e, boolean deprecated) { - MethodNode method = e.getValue(); - + private void addDelegateMethod(FieldNode fieldNode, ClassNode owner, List ownMethods, MethodNode method, boolean deprecated) { if (!method.isPublic() || method.isStatic() || 0 != (method.getModifiers () & Opcodes.ACC_SYNTHETIC)) return; if (!method.getAnnotations(DEPRECATED_TYPE).isEmpty() && !deprecated) return; - MethodNode existingNode = ownMethods.get(e.getKey()); + // ignore methods from GroovyObject + for (MethodNode mn : GROOVYOBJECT_TYPE.getMethods()) { + if (mn.getTypeDescriptor().equals(method.getTypeDescriptor())) { + return; + } + } + + MethodNode existingNode = null; + for (MethodNode mn : ownMethods) { + if (mn.getTypeDescriptor().equals(method.getTypeDescriptor()) && !mn.isAbstract()) { + existingNode = mn; + break; + } + } // TODO work out why the code was null for super interfaces if (existingNode == null || existingNode.getCode() == null) { final ArgumentListExpression args = new ArgumentListExpression(); @@ -166,4 +199,12 @@ return type; } } + + public void addError(String msg, ASTNode expr, SourceUnit source) { + int line = expr.getLineNumber(); + int col = expr.getColumnNumber(); + source.getErrorCollector().addErrorAndContinue( + new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source) + ); -} \ No newline at end of file + } +}