[ https://issues.apache.org/jira/browse/GROOVY-9086?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16819556#comment-16819556 ]
Eric Milles edited comment on GROOVY-9086 at 4/16/19 11:00 PM: --------------------------------------------------------------- The {{DelegationMetadata}} is deciphered by this method in {{StaticTypeCheckingVisitor}} to produce "Receiver\{type=Test.C1, data=delegate.delegate\}": {code:java} protected void addReceivers(final List<Receiver<String>> receivers, final Collection<Receiver<String>> owners, final boolean implicitThis) { if (typeCheckingContext.delegationMetadata == null || !implicitThis) { receivers.addAll(owners); return; } DelegationMetadata dmd = typeCheckingContext.delegationMetadata; StringBuilder path = new StringBuilder(); while (dmd != null) { int strategy = dmd.getStrategy(); ClassNode delegate = dmd.getType(); dmd = dmd.getParent(); switch (strategy) { case Closure.OWNER_FIRST: receivers.addAll(owners); path.append("delegate"); doAddDelegateReceiver(receivers, path, delegate); break; case Closure.DELEGATE_FIRST: path.append("delegate"); doAddDelegateReceiver(receivers, path, delegate); receivers.addAll(owners); break; case Closure.OWNER_ONLY: receivers.addAll(owners); dmd = null; break; case Closure.DELEGATE_ONLY: path.append("delegate"); doAddDelegateReceiver(receivers, path, delegate); dmd = null; break; } path.append('.'); } } {code} It starts from the innermost scope and moves outwards. It seems that "owner" should be appended for each parent so that the path correctly refers to outer scopes. Here is what I am trying out: {code:java} protected void addReceivers(final List<Receiver<String>> receivers, final Collection<Receiver<String>> owners, final boolean implicitThis) { if (typeCheckingContext.delegationMetadata == null || !implicitThis) { receivers.addAll(owners); return; } DelegationMetadata dmd = typeCheckingContext.delegationMetadata; StringBuilder path = new StringBuilder(); while (dmd != null) { int strategy = dmd.getStrategy(); ClassNode delegate = dmd.getType(); dmd = dmd.getParent(); switch (strategy) { case Closure.OWNER_FIRST: receivers.addAll(owners); addDelegateReceiver(receivers, delegate, path); break; case Closure.DELEGATE_FIRST: addDelegateReceiver(receivers, delegate, path); receivers.addAll(owners); break; case Closure.OWNER_ONLY: receivers.addAll(owners); dmd = null; break; case Closure.DELEGATE_ONLY: addDelegateReceiver(receivers, delegate, path); dmd = null; break; } path.append("owner."); } } private static void addDelegateReceiver(final List<Receiver<String>> receivers, final ClassNode delegate, final CharSequence path) { receivers.add(new Receiver<String>(delegate, path.toString() + "delegate")); if (isTraitHelper(delegate)) { receivers.add(new Receiver<String>(delegate.getOuterClass(), path.toString() + "delegate")); } } {code} was (Author: emilles): The {{DelegationMetadata}} is deciphered by this method in {{StaticTypeCheckingVisitor}} to produce "Receiver\{type=Test.C1, data=delegate.delegate\}": {code:java} protected void addReceivers(final List<Receiver<String>> receivers, final Collection<Receiver<String>> owners, final boolean implicitThis) { if (typeCheckingContext.delegationMetadata == null || !implicitThis) { receivers.addAll(owners); return; } DelegationMetadata dmd = typeCheckingContext.delegationMetadata; StringBuilder path = new StringBuilder(); while (dmd != null) { int strategy = dmd.getStrategy(); ClassNode delegate = dmd.getType(); dmd = dmd.getParent(); switch (strategy) { case Closure.OWNER_FIRST: receivers.addAll(owners); path.append("delegate"); doAddDelegateReceiver(receivers, path, delegate); break; case Closure.DELEGATE_FIRST: path.append("delegate"); doAddDelegateReceiver(receivers, path, delegate); receivers.addAll(owners); break; case Closure.OWNER_ONLY: receivers.addAll(owners); dmd = null; break; case Closure.DELEGATE_ONLY: path.append("delegate"); doAddDelegateReceiver(receivers, path, delegate); dmd = null; break; } path.append('.'); } } {code} It starts from the innermost scope and moves outwards. It seems that "owner" should be appended for each parent so that the path correctly refers to outer scopes. Here is what I am trying out: {code:java} protected void addReceivers(final List<Receiver<String>> receivers, final Collection<Receiver<String>> owners, final boolean implicitThis) { if (typeCheckingContext.delegationMetadata == null || !implicitThis) { receivers.addAll(owners); return; } DelegationMetadata dmd = typeCheckingContext.delegationMetadata; StringBuilder path = new StringBuilder(); while (dmd != null) { int strategy = dmd.getStrategy(); ClassNode delegate = dmd.getType(); dmd = dmd.getParent(); switch (strategy) { case Closure.OWNER_FIRST: receivers.addAll(owners); doAddDelegateReceiver(receivers, path, delegate); break; case Closure.DELEGATE_FIRST: doAddDelegateReceiver(receivers, path, delegate); receivers.addAll(owners); break; case Closure.OWNER_ONLY: receivers.addAll(owners); dmd = null; break; case Closure.DELEGATE_ONLY: doAddDelegateReceiver(receivers, path, delegate); dmd = null; break; } path.append("owner."); } } private static void doAddDelegateReceiver(final List<Receiver<String>> receivers, final CharSequence path, final ClassNode delegate) { receivers.add(new Receiver<String>(delegate, path.toString() + "delegate")); if (isTraitHelper(delegate)) { receivers.add(new Receiver<String>(delegate.getOuterClass(), path.toString() + "delegate")); } } {code} > @CompileStatic of closure calls with OWNER_FIRST fail at runtime with > ClassCastException > ---------------------------------------------------------------------------------------- > > Key: GROOVY-9086 > URL: https://issues.apache.org/jira/browse/GROOVY-9086 > Project: Groovy > Issue Type: Bug > Components: Static compilation > Affects Versions: 2.5.6 > Environment: WIndows7, Groovy 2.5.6, Java 8 + 11 > Reporter: Andreas Turban > Priority: Major > Attachments: TestScript.groovy > > > The attached script fails with a ClassCastException, when method m2 is > annotated with > > {code:java} > strategy = Closure.OWNER_FIRST > {code} > but work with > > {code:java} > strategy = Closure.DELEGATE_FIRST > {code} > The expected system-out should be > > {code:java} > c1Method called{code} > in both cases. The generated bytecode for closure > Test$_test_closure1$_closure2.class contains a call to getDelegate() instead > of getOwner() in case of Closure.OWNER_FIRST. But correctly generates code > getOwner() in case of Closure.DELEGATE_FIRST. > The resulting ClassCastException at runtime is: > {code:java} > java.lang.ClassCastException: Test$C2 cannot be cast to groovy.lang.Closure > at Test$_test_closure1$_closure2.doCall(TestScript.groovy:28) > at Test$_test_closure1$_closure2.doCall(TestScript.groovy) > at Test.m2(TestScript.groovy:20) > at Test$_test_closure1.doCall(TestScript.groovy:26) > at Test$_test_closure1.doCall(TestScript.groovy) > at Test.m1(TestScript.groovy:8) > at Test.test(TestScript.groovy:25) > at Test$test.call(Unknown Source) > at TestScript.run(TestScript.groovy:42) > {code} > The wrong bytecode for case Closure.OWNER_FIRST: > {code:java} > public java.lang.Object doCall(java.lang.Object); > descriptor: (Ljava/lang/Object;)Ljava/lang/Object; > flags: ACC_PUBLIC > Code: > stack=1, locals=2, args_size=2 > 0: aload_0 > 1: checkcast #2 // class Test$_test_closure1$_closure2 > 4: invokevirtual #29 // Method getDelegate:()Ljava/lang/Object; > 7: checkcast #4 // class groovy/lang/Closure > 10: invokevirtual #30 // Method > groovy/lang/Closure.getDelegate:()Ljava/lang/Object; > 13: checkcast #32 // class Test$C1 > 16: invokevirtual #36 // Method Test$C1.c1Method:()V > 19: aconst_null > 20: areturn > {code} > The correct bytecode for case Closure.DELEGATE_FIRST: > {code:java} > public java.lang.Object doCall(java.lang.Object); > descriptor: (Ljava/lang/Object;)Ljava/lang/Object; > flags: ACC_PUBLIC > Code: > stack=1, locals=2, args_size=2 > 0: aload_0 > 1: checkcast #2 // class Test$_test_closure1$_closure2 > 4: invokevirtual #29 // Method getOwner:()Ljava/lang/Object; > 7: checkcast #4 // class groovy/lang/Closure > 10: invokevirtual #32 // Method > groovy/lang/Closure.getDelegate:()Ljava/lang/Object; > 13: checkcast #34 // class Test$C1 > 16: invokevirtual #38 // Method Test$C1.c1Method:()V > 19: aconst_null > 20: areturn > {code} -- This message was sent by Atlassian JIRA (v7.6.3#76005)