Details
Description
This code will hang groovyc 1.7beta2:
class XXX extends XXX {
}
With stack:
at org.codehaus.groovy.ast.ClassNode.getUnresolvedSuperClass(ClassNode.java:919) at org.codehaus.groovy.ast.ClassNode.getSuperClass(ClassNode.java:913) at org.codehaus.groovy.classgen.InnerClassVisitor.getObjectDistance(InnerClassVisitor.java:517) at org.codehaus.groovy.classgen.InnerClassVisitor.addDispatcherMethods(InnerClassVisitor.java:378) at org.codehaus.groovy.classgen.InnerClassVisitor.visitClass(InnerClassVisitor.java:67) at org.codehaus.groovy.control.CompilationUnit$3.call(CompilationUnit.java:173) at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:936)
basically this method loops indefinetly InnerClassVisitor.getObjectDistance():
private int getObjectDistance(ClassNode node) { int count = 1; while (node!=null && node!=ClassHelper.OBJECT_TYPE) { count++; node = node.getSuperClass(); } return count; }
whereas 1.6.5 prints:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, cyclic inheritance involving XXX in class XXX
This variant will hang 1.6.5 and 1.7b2:
class XXX extends XXX { public static void main(String []argv) { print 'hey' } }
it instead loops indefinetly 'around' this location:
at java.util.AbstractList.iterator(AbstractList.java:273) at java.util.AbstractCollection.toArray(AbstractCollection.java:120) at java.util.ArrayList.addAll(ArrayList.java:472) at org.codehaus.groovy.ast.ClassNode.getMethods(ClassNode.java:807) at org.codehaus.groovy.ast.ClassNode.hasPossibleStaticMethod(ClassNode.java:1169) at org.codehaus.groovy.control.StaticImportVisitor.transformMethodCallExpression(StaticImportVisitor.java:189) at org.codehaus.groovy.control.StaticImportVisitor.transform(StaticImportVisitor.java:77) at org.codehaus.groovy.ast.ClassCodeExpressionTransformer.visitExpressionStatement(ClassCodeExpressionTransformer.java:139) at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:40) at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:35)
Attaching 2 versions of the fix for review.
Prior to inner class changes, (upto 1.6.6), although it fails with "cyclic inheritence" error, it goes all the way through to the class generation. I think it has enough information to fail as early as ResolveVisitor#visitClass()
to reject the class if both class and superclass are the same ClassNode.
So,
v1 of patch) Simply implements the above and fails in ResolveVisitor#visitClass() when there are cyclic inheritence
v2 of patch) Removes the earlier cyclic check from CompilationUnit that used to happen after classgen phase. After it starts catching the error in "resolve" phase itself, this code doesn't seem needed to me.
I think it's safe to go ahead with v2 but wanted to double check.
All existing tests are, of-course, going through with both the patches.