You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2021/03/03 20:13:03 UTC

[groovy] branch GROOVY_3_0_X updated (df82d8a -> 7980bf3)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from df82d8a  GROOVY-9954: Stop incrementing index after comments to avoid skipping the next character
     new d8942bb  GROOVY-9955: access static fields using object expression type not owner
     new 7980bf3  GROOVY-9955: invoke static method using object expression type not owner

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../groovy/classgen/asm/InvocationWriter.java      |  91 ++++++++++---------
 .../classgen/asm/sc/StaticInvocationWriter.java    |  52 +++++------
 .../classgen/asm/sc/StaticTypesCallSiteWriter.java | 100 +++++++++++----------
 .../stc/FieldsAndPropertiesSTCTest.groovy          |  24 +++++
 .../sc/FieldsAndPropertiesStaticCompileTest.groovy |   2 +-
 5 files changed, 151 insertions(+), 118 deletions(-)


[groovy] 01/02: GROOVY-9955: access static fields using object expression type not owner

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit d8942bb3cf863c138d9fb303d7eeb103f16f5a96
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Feb 26 11:15:07 2021 -0600

    GROOVY-9955: access static fields using object expression type not owner
    
    - restore direct static field write for "Type.@name = ..."
---
 .../classgen/asm/sc/StaticTypesCallSiteWriter.java | 100 +++++++++++----------
 .../stc/FieldsAndPropertiesSTCTest.groovy          |  22 +++++
 .../sc/FieldsAndPropertiesStaticCompileTest.groovy |   2 +-
 3 files changed, 74 insertions(+), 50 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
index 06d5e9d..0789b6a 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -137,42 +137,13 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
             makeDynamicGetProperty(receiver, propertyName, safe);
             return;
         }
-        TypeChooser typeChooser = controller.getTypeChooser();
-        ClassNode classNode = controller.getClassNode();
-        ClassNode receiverType = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
-        if (receiverType == null) {
-            receiverType = typeChooser.resolveType(receiver, classNode);
-        }
-        Object type = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
-        if (type == null && receiver instanceof VariableExpression) {
-            Variable variable = ((VariableExpression) receiver).getAccessedVariable();
-            if (variable instanceof Expression) {
-                type = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
-            }
-        }
-        if (type != null) {
-            // in case a "flow type" is found, it is preferred to use it instead of
-            // the declaration type
-            receiverType = (ClassNode) type;
-        }
-        boolean isClassReceiver = false;
-        if (isClassClassNodeWrappingConcreteType(receiverType)) {
-            isClassReceiver = true;
-            receiverType = receiverType.getGenericsTypes()[0].getType();
-        }
-
-        if (isPrimitiveType(receiverType)) {
-            // GROOVY-6590: wrap primitive types
-            receiverType = getWrapper(receiverType);
-        }
-
-        MethodVisitor mv = controller.getMethodVisitor();
-
+        boolean[] isClassReceiver = new boolean[1];
+        ClassNode receiverType = getPropertyOwnerType(receiver, isClassReceiver);
         if (receiverType.isArray() && "length".equals(propertyName)) {
             receiver.visit(controller.getAcg());
-            ClassNode arrayGetReturnType = typeChooser.resolveType(receiver, classNode);
+            ClassNode arrayGetReturnType = controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
             controller.getOperandStack().doGroovyCast(arrayGetReturnType);
-            mv.visitInsn(ARRAYLENGTH);
+            controller.getMethodVisitor().visitInsn(ARRAYLENGTH);
             controller.getOperandStack().replace(int_TYPE);
             return;
         } else if (isOrImplements(receiverType, COLLECTION_TYPE) && ("size".equals(propertyName) || "length".equals(propertyName))) {
@@ -189,7 +160,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
 
         if (!isStaticProperty && isOrImplements(receiverType, MAP_TYPE)) {
             // for maps, replace map.foo with map.get('foo')
-            writeMapDotProperty(receiver, propertyName, mv, safe);
+            writeMapDotProperty(receiver, propertyName, safe);
             return;
         }
         if (makeGetPropertyWithGetter(receiver, receiverType, propertyName, safe, implicitThis)) return;
@@ -199,7 +170,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
             if (makeGetPropertyWithGetter(receiver, receiver.getType(), propertyName, safe, implicitThis)) return;
             if (makeGetPrivateFieldWithBridgeMethod(receiver, receiver.getType(), propertyName, safe, implicitThis)) return;
         }
-        if (isClassReceiver) {
+        if (isClassReceiver[0]) {
             // we are probably looking for a property of the class
             if (makeGetPropertyWithGetter(receiver, CLASS_Type, propertyName, safe, implicitThis)) return;
             if (makeGetField(receiver, CLASS_Type, propertyName, safe, false)) return;
@@ -253,7 +224,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
         }
 
         if (!isStaticProperty && isOrImplements(receiverType, LIST_TYPE)) {
-            writeListDotProperty(receiver, propertyName, mv, safe);
+            writeListDotProperty(receiver, propertyName, safe);
             return;
         }
 
@@ -275,9 +246,11 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
         call.visit(controller.getAcg());
     }
 
-    private void writeMapDotProperty(final Expression receiver, final String propertyName, final MethodVisitor mv, final boolean safe) {
+    private void writeMapDotProperty(final Expression receiver, final String propertyName, final boolean safe) {
         receiver.visit(controller.getAcg()); // load receiver
 
+        MethodVisitor mv = controller.getMethodVisitor();
+
         Label exit = new Label();
         if (safe) {
             Label doGet = new Label();
@@ -297,7 +270,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
         controller.getOperandStack().replace(OBJECT_TYPE);
     }
 
-    private void writeListDotProperty(final Expression receiver, final String propertyName, final MethodVisitor mv, final boolean safe) {
+    private void writeListDotProperty(final Expression receiver, final String propertyName, final boolean safe) {
         ClassNode componentType = receiver.getNodeMetaData(StaticCompilationMetadataKeys.COMPONENT_TYPE);
         if (componentType == null) {
             componentType = OBJECT_TYPE;
@@ -307,6 +280,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
         // for (e in list) { result.add (e.foo) }
         // result
         CompileStack compileStack = controller.getCompileStack();
+        MethodVisitor mv = controller.getMethodVisitor();
 
         Label exit = new Label();
         if (safe) {
@@ -536,7 +510,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
             ClassNode replacementType = field.getOriginType();
             OperandStack operandStack = controller.getOperandStack();
             if (field.isStatic()) {
-                mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(replacementType));
+                mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(receiverType), fieldName, BytecodeHelper.getTypeDescription(replacementType));
                 operandStack.push(replacementType);
             } else {
                 if (implicitThis) {
@@ -791,11 +765,11 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
     @Override
     public void fallbackAttributeOrPropertySite(final PropertyExpression expression, final Expression objectExpression, final String name, final MethodCallerMultiAdapter adapter) {
         if (name != null && controller.getCompileStack().isLHS()) {
-            ClassNode classNode = controller.getClassNode();
-            ClassNode receiverType = controller.getTypeChooser().resolveType(objectExpression, classNode);
+            ClassNode receiverType = getPropertyOwnerType(objectExpression);
             if (adapter == AsmClassGenerator.setField || adapter == AsmClassGenerator.setGroovyObjectField) {
                 if (setField(expression, objectExpression, receiverType, name)) return;
             } else if (isThisExpression(objectExpression)) {
+                ClassNode classNode = controller.getClassNode();
                 FieldNode fieldNode = receiverType.getField(name);
                 if (fieldNode != null && fieldNode.isPrivate() && !receiverType.equals(classNode)
                         && StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode)) {
@@ -828,29 +802,57 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes
         super.fallbackAttributeOrPropertySite(expression, objectExpression, name, adapter);
     }
 
+    private ClassNode getPropertyOwnerType(final Expression receiver, final boolean... isClassReceiver) {
+        Object inferredType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+        if (inferredType == null && receiver instanceof VariableExpression) {
+            Variable variable = ((VariableExpression) receiver).getAccessedVariable();
+            if (variable instanceof Expression) {
+                inferredType = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+            }
+        }
+        ClassNode receiverType;
+        if (inferredType instanceof ClassNode) {
+            // in case a "flow type" is found, it is preferred to use it instead of the declaration type
+            receiverType = (ClassNode) inferredType;
+        } else {
+            receiverType = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
+            if (receiverType == null) {
+                receiverType = controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
+            }
+        }
+        if (isClassClassNodeWrappingConcreteType(receiverType)) {
+            if (isClassReceiver.length > 0) isClassReceiver[0] = true;
+            receiverType = receiverType.getGenericsTypes()[0].getType();
+        }
+        if (isPrimitiveType(receiverType)) {
+            // GROOVY-6590: wrap primitive types
+            receiverType = getWrapper(receiverType);
+        }
+        return receiverType;
+    }
+
     // this is just a simple set field handling static and non-static, but not Closure and inner classes
-    private boolean setField(final PropertyExpression expression, final Expression objectExpression, final ClassNode rType, final String name) {
+    private boolean setField(final PropertyExpression expression, final Expression objectExpression, final ClassNode receiverType, final String name) {
         if (expression.isSafe()) return false;
-        FieldNode fn = AsmClassGenerator.getDeclaredFieldOfCurrentClassOrAccessibleFieldOfSuper(controller.getClassNode(), rType, name, false);
+        FieldNode fn = AsmClassGenerator.getDeclaredFieldOfCurrentClassOrAccessibleFieldOfSuper(controller.getClassNode(), receiverType, name, false);
         if (fn == null) return false;
         OperandStack stack = controller.getOperandStack();
         stack.doGroovyCast(fn.getType());
 
         MethodVisitor mv = controller.getMethodVisitor();
-        String ownerName = BytecodeHelper.getClassInternalName(fn.getOwner());
         if (!fn.isStatic()) {
             controller.getCompileStack().pushLHS(false);
             objectExpression.visit(controller.getAcg());
             controller.getCompileStack().popLHS();
-            if (!rType.equals(stack.getTopOperand())) {
-                BytecodeHelper.doCast(mv, rType);
-                stack.replace(rType);
+            if (!receiverType.equals(stack.getTopOperand())) {
+                BytecodeHelper.doCast(mv, receiverType);
+                stack.replace(receiverType);
             }
             stack.swap();
-            mv.visitFieldInsn(PUTFIELD, ownerName, name, BytecodeHelper.getTypeDescription(fn.getType()));
+            mv.visitFieldInsn(PUTFIELD, BytecodeHelper.getClassInternalName(fn.getOwner()), name, BytecodeHelper.getTypeDescription(fn.getType()));
             stack.remove(1);
         } else {
-            mv.visitFieldInsn(PUTSTATIC, ownerName, name, BytecodeHelper.getTypeDescription(fn.getType()));
+            mv.visitFieldInsn(PUTSTATIC, BytecodeHelper.getClassInternalName(receiverType), name, BytecodeHelper.getTypeDescription(fn.getType()));
         }
 
         return true;
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index eb748ec..485c48d 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -19,6 +19,7 @@
 package groovy.transform.stc
 
 import groovy.test.NotYetImplemented
+import groovy.transform.PackageScope
 
 /**
  * Unit tests for static type checking : fields and properties.
@@ -235,6 +236,19 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-9955
+    void testStaticPropertyWithInheritanceFromAnotherSourceUnit() {
+        assertScript '''
+            import groovy.transform.stc.FieldsAndPropertiesSTCTest.Public
+            assert Public.CONST == 'XX'
+            assert Public.VALUE == null
+            Public.VALUE = 'YY'
+            assert Public.VALUE == 'YY'
+            Public.@VALUE = 'ZZ'
+            assert Public.@VALUE == 'ZZ'
+        '''
+    }
+
     void testMethodUsageForProperty() {
         assertScript '''
             class Foo {
@@ -1085,4 +1099,12 @@ import org.codehaus.groovy.ast.stmt.AssertStatement
 
     static class BaseClass2 extends BaseClass {
     }
+
+    @PackageScope static class PackagePrivate {
+        public static final String CONST = 'XX'
+        public static String VALUE
+    }
+
+    static class Public extends PackagePrivate {
+    }
 }
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
index 2fffda8..af1dc40 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
@@ -302,7 +302,7 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT
             assert B.m() == 0
         '''
         def b = astTrees['B'][1]
-        assert  b.contains('GETSTATIC p/A.x')
+        assert  b.contains('GETSTATIC B.x')
         assert !b.contains('INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.getGroovyObjectProperty')
     }
 


[groovy] 02/02: GROOVY-9955: invoke static method using object expression type not owner

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 7980bf3a16d3287559bef15c5e8c8467f3ec3b23
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Feb 26 13:25:27 2021 -0600

    GROOVY-9955: invoke static method using object expression type not owner
---
 .../groovy/classgen/asm/InvocationWriter.java      | 91 ++++++++++++----------
 .../classgen/asm/sc/StaticInvocationWriter.java    | 52 ++++++-------
 .../stc/FieldsAndPropertiesSTCTest.groovy          |  2 +
 3 files changed, 77 insertions(+), 68 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
index 3402fa2..eaa920c 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
@@ -46,18 +46,22 @@ import org.codehaus.groovy.syntax.SyntaxException;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Objects;
 import java.util.TreeMap;
 
 import static org.apache.groovy.ast.tools.ExpressionUtils.isNullConstant;
 import static org.apache.groovy.ast.tools.ExpressionUtils.isSuperExpression;
 import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression;
+import static org.codehaus.groovy.ast.ClassHelper.isFunctionalInterface;
+import static org.codehaus.groovy.ast.ClassHelper.isGeneratedFunction;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isClassClassNodeWrappingConcreteType;
 import static org.objectweb.asm.Opcodes.AALOAD;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ACONST_NULL;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.ATHROW;
@@ -104,7 +108,7 @@ public class InvocationWriter {
     public void makeCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, boolean safe, final boolean spreadSafe, boolean implicitThis) {
         ClassNode sender = controller.getClassNode();
         if (isSuperExpression(receiver) || (isThisExpression(receiver) && !implicitThis)) {
-            while (ClassHelper.isGeneratedFunction(sender)) {
+            while (isGeneratedFunction(sender)) {
                 sender = sender.getOuterClass();
             }
             if (isSuperExpression(receiver)) {
@@ -120,13 +124,8 @@ public class InvocationWriter {
     protected boolean writeDirectMethodCall(final MethodNode target, final boolean implicitThis, final Expression receiver, final TupleExpression args) {
         if (target == null) return false;
 
-        String methodName = target.getName();
-        CompileStack compileStack = controller.getCompileStack();
-        OperandStack operandStack = controller.getOperandStack();
         ClassNode declaringClass = target.getDeclaringClass();
-        ClassNode classNode = controller.getClassNode();
-
-        MethodVisitor mv = controller.getMethodVisitor();
+        String methodName = target.getName();
         int opcode = INVOKEVIRTUAL;
         if (target.isStatic()) {
             opcode = INVOKESTATIC;
@@ -136,12 +135,17 @@ public class InvocationWriter {
             opcode = INVOKESPECIAL;
         }
 
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+        MethodVisitor mv = controller.getMethodVisitor();
+        ClassNode classNode = controller.getClassNode();
+
         // handle receiver
         int argumentsToRemove = 0;
         if (opcode != INVOKESTATIC) {
             if (receiver != null) {
                 // load receiver if not static invocation
-                // todo: fix inner class case
+                // TODO: fix inner class case
                 if (implicitThis
                         && classNode.getOuterClass() != null
                         && !classNode.isDerivedFrom(declaringClass)
@@ -150,7 +154,7 @@ public class InvocationWriter {
                     compileStack.pushImplicitThis(false);
                     if (controller.isInGeneratedFunction()) {
                         new VariableExpression("thisObject").visit(controller.getAcg());
-                    } else {
+                    } else { // TODO: handle implicitThis && !isThisExpression(receiver)
                         Expression expr = new PropertyExpression(new ClassExpression(declaringClass), "this");
                         expr.visit(controller.getAcg());
                     }
@@ -162,54 +166,57 @@ public class InvocationWriter {
                 compileStack.popImplicitThis();
                 argumentsToRemove += 1;
             } else {
-                mv.visitIntInsn(ALOAD,0);
+                mv.visitIntInsn(ALOAD, 0);
                 operandStack.push(classNode);
                 argumentsToRemove += 1;
             }
         }
 
-        int stackSize = operandStack.getStackLength();
+        ClassNode receiverType;
+        if (receiver == null) {
+            receiverType = declaringClass;
+        } else {
+            receiverType = controller.getTypeChooser().resolveType(receiver, classNode);
+            if (isClassClassNodeWrappingConcreteType(receiverType) && target.isStatic()) {
+                receiverType = receiverType.getGenericsTypes()[0].getType();
+            }
+        }
 
+        int stackLen = operandStack.getStackLength();
         String owner = BytecodeHelper.getClassInternalName(declaringClass);
-        ClassNode receiverType = receiver != null ? controller.getTypeChooser().resolveType(receiver, classNode) : declaringClass;
-        if (opcode == INVOKEVIRTUAL && ClassHelper.OBJECT_TYPE.equals(declaringClass)) {
+        if (opcode == INVOKEVIRTUAL && declaringClass.equals(ClassHelper.OBJECT_TYPE)) {
             // avoid using a narrowed type if the method is defined on object because it can interfere
             // with delegate type inference in static compilation mode and trigger a ClassCastException
             receiverType = declaringClass;
-        }
-        if (opcode == INVOKEVIRTUAL) {
-            if (!receiverType.equals(declaringClass)
-                    && !ClassHelper.OBJECT_TYPE.equals(declaringClass)
-                    && !receiverType.isArray()
-                    && !receiverType.isInterface()
-                    && !ClassHelper.isPrimitiveType(receiverType) // e.g int.getClass()
-                    && receiverType.isDerivedFrom(declaringClass)) {
-
-                owner = BytecodeHelper.getClassInternalName(receiverType);
-                ClassNode top = operandStack.getTopOperand();
-                if (!receiverType.equals(top)) {
-                    mv.visitTypeInsn(CHECKCAST, owner);
-                }
-            } else if (target.isPublic()
-                    && (!receiverType.equals(declaringClass) && !Modifier.isPublic(declaringClass.getModifiers()))
-                    && receiverType.isDerivedFrom(declaringClass) && !Objects.equals(receiverType.getPackageName(), classNode.getPackageName())) {
-                // GROOVY-6962: package private class, public method
-                owner = BytecodeHelper.getClassInternalName(receiverType);
+        } else if (opcode == INVOKEVIRTUAL
+                && !receiverType.isArray()
+                && !receiverType.isInterface()
+                && !isPrimitiveType(receiverType)
+                && !receiverType.equals(declaringClass)
+                && receiverType.isDerivedFrom(declaringClass)) {
+
+            owner = BytecodeHelper.getClassInternalName(receiverType);
+            if (!receiverType.equals(operandStack.getTopOperand())) {
+                mv.visitTypeInsn(CHECKCAST, owner);
             }
+        } else if (opcode != INVOKESPECIAL && (declaringClass.getModifiers() & (ACC_FINAL | ACC_PUBLIC)) == 0 && !receiverType.equals(declaringClass)
+                && (declaringClass.isInterface() ? receiverType.implementsInterface(declaringClass) : receiverType.isDerivedFrom(declaringClass))) {
+            // GROOVY-6962, GROOVY-9955: method declared by inaccessible class
+            owner = BytecodeHelper.getClassInternalName(receiverType);
         }
 
         loadArguments(args.getExpressions(), target.getParameters());
 
-        String desc = BytecodeHelper.getMethodDescriptor(target.getReturnType(), target.getParameters());
-        mv.visitMethodInsn(opcode, owner, methodName, desc, declaringClass.isInterface());
-        ClassNode ret = target.getReturnType().redirect();
-        if (ret == ClassHelper.VOID_TYPE) {
-            ret = ClassHelper.OBJECT_TYPE;
+        String descriptor = BytecodeHelper.getMethodDescriptor(target.getReturnType(), target.getParameters());
+        mv.visitMethodInsn(opcode, owner, methodName, descriptor, declaringClass.isInterface());
+        ClassNode returnType = target.getReturnType().redirect();
+        if (returnType == ClassHelper.VOID_TYPE) {
+            returnType = ClassHelper.OBJECT_TYPE;
             mv.visitInsn(ACONST_NULL);
         }
-        argumentsToRemove += (operandStack.getStackLength()-stackSize);
+        argumentsToRemove += (operandStack.getStackLength() - stackLen);
         controller.getOperandStack().remove(argumentsToRemove);
-        controller.getOperandStack().push(ret);
+        controller.getOperandStack().push(returnType);
         return true;
     }
 
@@ -454,7 +461,7 @@ public class InvocationWriter {
         if ("call".equals(call.getMethodAsString())) {
             Expression objectExpression = call.getObjectExpression();
             if (!isThisExpression(objectExpression)) {
-                return ClassHelper.isFunctionalInterface(objectExpression.getType());
+                return isFunctionalInterface(objectExpression.getType());
             }
         }
         return false;
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index 62e2bce..b9712a7 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -683,11 +683,10 @@ public class StaticInvocationWriter extends InvocationWriter {
         private final Expression receiver;
         private final MethodNode target;
 
-        private ClassNode resolvedType;
-
         public CheckcastReceiverExpression(final Expression receiver, final MethodNode target) {
             this.receiver = receiver;
             this.target = target;
+            setType(null);
         }
 
         @Override
@@ -721,32 +720,33 @@ public class StaticInvocationWriter extends InvocationWriter {
 
         @Override
         public ClassNode getType() {
-            if (resolvedType != null) {
-                return resolvedType;
-            }
-            ClassNode type;
-            if (target instanceof ExtensionMethodNode) {
-                type = ((ExtensionMethodNode) target).getExtensionMethodNode().getDeclaringClass();
-            } else {
-                type = ClassHelper.getWrapper(controller.getTypeChooser().resolveType(receiver, controller.getClassNode()));
-                ClassNode declaringClass = target.getDeclaringClass();
-                if (type.getClass() != ClassNode.class
-                        && type.getClass() != InnerClassNode.class
-                        && type.getClass() != DecompiledClassNode.class) {
-                    type = declaringClass; // ex: LUB type
-                }
-                if (ClassHelper.OBJECT_TYPE.equals(type) && !ClassHelper.OBJECT_TYPE.equals(declaringClass)) {
-                    // can happen for compiler rewritten code, where type information is missing
-                    type = declaringClass;
-                }
-                if (ClassHelper.OBJECT_TYPE.equals(declaringClass)) {
-                    // check cast not necessary because Object never evolves
-                    // and it prevents a potential ClassCastException if the delegate of a closure
-                    // is changed in a statically compiled closure
-                    type = ClassHelper.OBJECT_TYPE;
+            ClassNode type = super.getType();
+            if (type == null) {
+                if (target instanceof ExtensionMethodNode) {
+                    type = ((ExtensionMethodNode) target).getExtensionMethodNode().getDeclaringClass();
+                } else {
+                    type = controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
+                    if (ClassHelper.isPrimitiveType(type)) {
+                        type = ClassHelper.getWrapper(type);
+                    }
+                    ClassNode declaringClass = target.getDeclaringClass();
+                    if (type.getClass() != ClassNode.class
+                            && type.getClass() != InnerClassNode.class
+                            && type.getClass() != DecompiledClassNode.class) {
+                        type = declaringClass; // ex: LUB type
+                    }
+                    if (ClassHelper.OBJECT_TYPE.equals(declaringClass)) {
+                        // checkcast not necessary because Object never evolves
+                        // and it prevents a potential ClassCastException if the
+                        // delegate of a closure is changed in a SC closure
+                        type = ClassHelper.OBJECT_TYPE;
+                    } else if (ClassHelper.OBJECT_TYPE.equals(type)) {
+                        // can happen for compiler rewritten code, where type information is missing
+                        type = declaringClass;
+                    }
                 }
+                setType(type);
             }
-            resolvedType = type;
             return type;
         }
     }
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index 485c48d..5fca628 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -240,6 +240,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
     void testStaticPropertyWithInheritanceFromAnotherSourceUnit() {
         assertScript '''
             import groovy.transform.stc.FieldsAndPropertiesSTCTest.Public
+            assert Public.answer == 42
             assert Public.CONST == 'XX'
             assert Public.VALUE == null
             Public.VALUE = 'YY'
@@ -1101,6 +1102,7 @@ import org.codehaus.groovy.ast.stmt.AssertStatement
     }
 
     @PackageScope static class PackagePrivate {
+        public static Number getAnswer() { 42 }
         public static final String CONST = 'XX'
         public static String VALUE
     }