Index: src/main/org/codehaus/groovy/ast/ClassNode.java =================================================================== --- src/main/org/codehaus/groovy/ast/ClassNode.java (revision 18047) +++ src/main/org/codehaus/groovy/ast/ClassNode.java (working copy) @@ -173,6 +173,8 @@ // if set to true the name getGenericsTypes consists // of 1 element describing the name of the placeholder private boolean placeholder; + + private List callSites; /** * Returns the ClassNode this ClassNode is redirecting to. @@ -1380,4 +1382,13 @@ public boolean isEnum() { return (getModifiers()&Opcodes.ACC_ENUM) != 0; } + + public void setCallSites(List cs) { + this.callSites = new ArrayList(); + this.callSites.addAll(cs); + } + + public List getCallSites() { + return this.callSites; + } } Index: src/main/org/codehaus/groovy/ast/InnerClassNode.java =================================================================== --- src/main/org/codehaus/groovy/ast/InnerClassNode.java (revision 18047) +++ src/main/org/codehaus/groovy/ast/InnerClassNode.java (working copy) @@ -15,6 +15,9 @@ */ package org.codehaus.groovy.ast; +import java.util.ArrayList; +import java.util.List; + /** * Represents an inner class declaration * @@ -24,6 +27,8 @@ public class InnerClassNode extends ClassNode { private ClassNode outerClass; + private List callSites; + private boolean helperForInterface = false; /** * @param name is the full name of the class @@ -54,4 +59,12 @@ public FieldNode getOuterField(String name) { return outerClass.getDeclaredField(name); } + + public void setHelperForInterface () { + helperForInterface = true; + } + + public boolean isHelperForInterface () { + return helperForInterface; + } } Index: src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java =================================================================== --- src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java (revision 18047) +++ src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java (working copy) @@ -208,7 +208,15 @@ public void visitClass(ClassNode classNode) { try { callSites.clear(); - + if(classNode instanceof InnerClassNode) { + InnerClassNode icn = (InnerClassNode) classNode; + if(icn.isHelperForInterface()) { + List cs = icn.getOuterClass().getCallSites(); + if(cs != null) { + callSites.addAll(cs); + } + } + } referencedClasses.clear(); this.classNode = classNode; this.outermostClass = null; @@ -238,6 +246,9 @@ super.visitClass(classNode); createInterfaceSyntheticStaticFields(); + ((InnerClassNode)interfaceClassLoadingClass).setHelperForInterface(); + // store the callsites on the ClassNode so that the helper inner class can pick them up later + classNode.setCallSites(callSites); } else { super.visitClass(classNode); if (!classNode.declaresInterface(ClassHelper.GENERATED_CLOSURE_Type)) { @@ -300,7 +311,13 @@ private void generateGetCallSiteArray() { - MethodVisitor mv = cv.visitMethod(ACC_PRIVATE+ACC_SYNTHETIC+ACC_STATIC,"$getCallSiteArray", "()[Lorg/codehaus/groovy/runtime/callsite/CallSite;", null, null); + int visibility = ACC_PRIVATE; + if(classNode instanceof InnerClassNode) { + if(((InnerClassNode) classNode).isHelperForInterface()) { + visibility = ACC_PUBLIC; + } + } + MethodVisitor mv = cv.visitMethod(visibility+ACC_SYNTHETIC+ACC_STATIC,"$getCallSiteArray", "()[Lorg/codehaus/groovy/runtime/callsite/CallSite;", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, internalClassName, "$callSiteArray", "Ljava/lang/ref/SoftReference;"); Label l0 = new Label(); @@ -1853,7 +1870,7 @@ mv.visitVarInsn(ALOAD, callSiteArrayVarIndex); } else { - mv.visitMethodInsn(INVOKESTATIC,internalClassName,"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); + mv.visitMethodInsn(INVOKESTATIC,getClassName(),"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); } final int index = allocateIndex(methodName); mv.visitLdcInsn(index); @@ -1879,7 +1896,7 @@ mv.visitVarInsn(ALOAD, callSiteArrayVarIndex); } else { - mv.visitMethodInsn(INVOKESTATIC,internalClassName,"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); + mv.visitMethodInsn(INVOKESTATIC,getClassName(),"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); } final int index = allocateIndex(methodName); mv.visitLdcInsn(index); @@ -1923,7 +1940,7 @@ mv.visitVarInsn(ALOAD, callSiteArrayVarIndex); } else { - mv.visitMethodInsn(INVOKESTATIC,internalClassName,"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); + mv.visitMethodInsn(INVOKESTATIC,getClassName(),"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); } final int index = allocateIndex(message); mv.visitLdcInsn(index); @@ -2226,13 +2243,22 @@ mv.visitVarInsn(ALOAD, callSiteArrayVarIndex); } else { - mv.visitMethodInsn(INVOKESTATIC,internalClassName,"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); + mv.visitMethodInsn(INVOKESTATIC,getClassName(),"$getCallSiteArray","()[Lorg/codehaus/groovy/runtime/callsite/CallSite;"); } final int index = allocateIndex(message); mv.visitLdcInsn(index); mv.visitInsn(AALOAD); } + private String getClassName() { + String className; + if (!classNode.isInterface() || interfaceClassLoadingClass == null) { + className = internalClassName; + } else { + className = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass); + } + return className; + } private int allocateIndex(String name) { callSites.add(name); return callSites.size()-1; Index: src/test/groovy/bugs/Groovy3830Bug.groovy =================================================================== --- src/test/groovy/bugs/Groovy3830Bug.groovy (revision 0) +++ src/test/groovy/bugs/Groovy3830Bug.groovy (revision 0) @@ -0,0 +1,15 @@ +package groovy.bugs + +class Groovy3830Bug extends GroovyTestCase { + void testCallSitesUsageInAnInterface() { + assert I3830.i == 2 + assert I3830.i2 == 5 + assert I3830.i3 == 6 + } +} + +interface I3830 { + Integer i = 2 + Integer i2 = i + 3 + Integer i3 = i * 3 +}