From 0738ed1049599db55b44dac64b4135f592262ca6 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Mon, 17 Jul 2023 08:07:36 -0500 Subject: [PATCH] GROOVY-2433, GROOVY-3073, GROOVY-9987: `this.privateMethod()` in closure --- .../groovy/vmplugin/v8/IndyInterface.java | 20 +++--- src/test/groovy/ClosureTest.groovy | 70 +++++++++++++++++++ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/IndyInterface.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/IndyInterface.java index 5f738118df5..04a2d2d94ae 100644 --- a/src/main/java/org/codehaus/groovy/vmplugin/v8/IndyInterface.java +++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/IndyInterface.java @@ -22,13 +22,13 @@ import org.apache.groovy.util.SystemUtil; import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.reflection.ClassInfo; +import org.codehaus.groovy.runtime.GeneratedClosure; import org.codehaus.groovy.runtime.NullObject; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.invoke.MutableCallSite; import java.lang.invoke.SwitchPoint; @@ -204,7 +204,7 @@ protected static void invalidateSwitchPoints() { * @return the produced CallSite * @since Groovy 2.1.0 */ - public static CallSite bootstrap(Lookup caller, String callType, MethodType type, String name, int flags) { + public static CallSite bootstrap(MethodHandles.Lookup caller, String callType, MethodType type, String name, int flags) { CallType ct = CallType.fromCallSiteName(callType); if (null == ct) throw new GroovyBugError("Unknown call type: " + callType); @@ -219,13 +219,17 @@ public static CallSite bootstrap(Lookup caller, String callType, MethodType type /** * backing bootstrap method with all parameters */ - private static CallSite realBootstrap(Lookup caller, String name, int callID, MethodType type, boolean safe, boolean thisCall, boolean spreadCall) { - // since indy does not give us the runtime types - // we produce first a dummy call site, which then changes the target to one when INDY_OPTIMIZE_THRESHOLD is reached, - // that does the method selection including the direct call to the - // real method. + private static CallSite realBootstrap(MethodHandles.Lookup caller, String name, int callID, MethodType type, boolean safe, boolean thisCall, boolean spreadCall) { + // first produce a dummy call site, since indy doesn't give the runtime types; + // the site then changes to the target when INDY_OPTIMIZE_THRESHOLD is reached + // that does the method selection including the direct call to the real method CacheableCallSite mc = new CacheableCallSite(type); - final Class sender = caller.lookupClass(); + Class sender = caller.lookupClass(); + if (thisCall) { + while (GeneratedClosure.class.isAssignableFrom(sender)) { + sender = sender.getEnclosingClass(); // GROOVY-2433 + } + } MethodHandle mh = makeAdapter(mc, sender, name, callID, type, safe, thisCall, spreadCall); mc.setTarget(mh); mc.setDefaultTarget(mh); diff --git a/src/test/groovy/ClosureTest.groovy b/src/test/groovy/ClosureTest.groovy index 35b67b72bd3..0fb52ee58ff 100644 --- a/src/test/groovy/ClosureTest.groovy +++ b/src/test/groovy/ClosureTest.groovy @@ -538,6 +538,76 @@ final class ClosureTest { assert err.message.contains('"methodMissing" implementations are not supported on static inner classes as a synthetic version of "methodMissing" is added during compilation for the purpose of outer class delegation.') } + // GROOVY-2433, GROOVY-3073, GROOVY-9987 + @Test + void testClosureAccessToEnclosingClassPrivateMethod() { + assertScript ''' + class C { + def getIds() { + populateIds() + } + def populateIds = { -> + this.sort([ 1, 5, 3, 4, 2 ]) + } + private sort(list) { + list.sort{ one, two -> one <=> two } + } + } + + class D extends C { + void test() { + assert ids == [1,2,3,4,5] + } + } + + new D().test() + ''' + + assertScript ''' + class C { + protected String protectedMethod() { + def closure = { -> + this.privateMethod() + } + closure() + } + private String privateMethod() { + 'hello world' + } + } + + class D extends C { + void test() { + def result = protectedMethod() + assert result == 'hello world' + } + } + + new D().test() + ''' + + assertScript ''' + class C { + def publicMethod() { + [1].each { + this.privateStaticMethod() + } + } + private static privateStaticMethod() { + 'hello world' + } + } + + class D extends C { + void test() { + publicMethod() + } + } + + new D().test() + ''' + } + // GROOVY-3142 @Test void testClosureAccessToEnclosingClassPrivateField() {