Repository navigation
GROOVY-12581: SC: make dynamic calls on a closure delegate as normal method calls - #3107
codeconsole wants to merge 1 commit into
Conversation
…od calls
The type checker records when an implicit-this call in a closure goes to
the delegate, and the static invocation writer then replaces the
receiver with this.delegate. It kept the call adapter that was chosen
for a call on this, though. When the call has no method target and is
made dynamically, that adapter is ScriptBytecodeAdapter's
invokeMethodOnCurrent, which takes a GroovyObject, so a delegate of a
Java class failed at run time:
@CompileStatic
class C {
List<String> log = [];
{ log << new StringBuilder('a').with { append('b'); toString() } }
}
new C() failed with "IncompatibleClassChangeError: Class
java.lang.StringBuilder does not implement the requested interface
groovy.lang.GroovyObject". The instance initializer blocks of a
@CompileStatic class are type checked but their calls get no method
targets, so with, tap and other delegating closures in them hit this
with any delegate that is not a Groovy object.
Once the receiver has been replaced, the writer now makes such a call
with the invokeMethod adapter, as for any other receiver.
🚨 All tests passed but jobs failed 🚨Failed Jobs without Test Failures❌ Build and test / lts (17, windows-latest, 1) 🏷️ Commit: ac5dcd7 Rerun ControlsClick the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app/docs. |
daniellansun
left a comment
There was a problem hiding this comment.
GROOVY-12581: when SC rewrites an implicit-this call onto the closure delegate (tryImplicitReceiver), the call must not keep invokeMethodOnCurrent — that assumes a GroovyObject receiver and blows up with IncompatibleClassChangeError on StringBuilder / ArrayList. Remapping to invokeMethod restores Groovy 4 behavior.
testClosureDelegateInInstanceInitializer covers with/tap on Java types inside an instance initializer. Small, right layer.
CI: lts (17, windows-latest, 1) 60m timeout — re-run then APPROVE. Same StaticInvocationWriter conflict surface as #3102/#3103/#3106.
{ log << new StringBuilder('a').with { append('b') }.toString() }in a@CompileStaticclass now works, as it did in 4.0. Before, it failed withIncompatibleClassChangeError: Class java.lang.StringBuilder does not implement the requested interface groovy.lang.GroovyObjectfor any delegate that is not a Groovy object.The type checker records when an implicit-this call in a closure goes to the delegate, and the static invocation writer then replaces the receiver with
this.delegate, but kept the call adapter chosen for a call onthis. The instance initializer blocks of a@CompileStaticclass are type checked, but their calls get no method targets, so the call was made dynamically withScriptBytecodeAdapter.invokeMethodOnCurrent, which takes aGroovyObject. Once the receiver has been replaced, the writer now makes such a call with theinvokeMethodadapter, as for any other receiver. AtoString()call inside such a closure goes to the closure itself under static compilation until GROOVY-12576 (#3102) is merged.Tests:
ClosuresSTCTest.testClosureDelegateInInstanceInitializer(also run statically compiled byClosuresStaticCompileTest), which fails without the change. Thegroovy,gls,bugsandorg.codehaus.groovypackages pass (15,512 tests), apart from three@Grabtests (CoercionSTCTest.testCoerceToFunctionalInterface14, also run byCoercionStaticCompileTests, andGenericsSTCTest.testSelfReferentialTypeParameter5) whose download failed while other builds ran; they pass when rerun. The Checkstyle and CodeNarc gates pass.A regression in 5.0.0: 4.0.33 is correct, 5.0.8 and 6.0.0 are not.