[ 
https://issues.apache.org/jira/browse/GROOVY-9086?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16820378#comment-16820378
 ] 

Eric Milles edited comment on GROOVY-9086 at 4/17/19 6:48 PM:
--------------------------------------------------------------

This version of {{addReceivers}} should properly add all owner receivers before 
the delegate in the case of {{OWNER_FIRST}}.  And {{OWNER_ONLY}} gets receivers 
from outer scopes in case of nesting.
{code:java}
    protected void addReceivers(List<Receiver<String>> receivers, 
Collection<Receiver<String>> owners, boolean implicitThis) {
        if (!implicitThis || typeCheckingContext.delegationMetadata == null) {
            receivers.addAll(owners);
        } else {
            addReceivers(receivers, owners, "", 
typeCheckingContext.delegationMetadata);
        }
    }

    private static void addReceivers(List<Receiver<String>> receivers, 
Collection<Receiver<String>> owners, String path, DelegationMetadata dmd) {
        int strategy = dmd.getStrategy();
        switch (strategy) {
            case Closure.DELEGATE_ONLY:
            case Closure.DELEGATE_FIRST:
                addDelegateReceiver(receivers, dmd.getType(), path + 
"delegate");
                if (strategy == Closure.DELEGATE_FIRST) {
                    if (dmd.getParent() == null) {
                        receivers.addAll(owners);
                    } else {
                        receivers.add(new Receiver<String>(CLOSURE_TYPE, path + 
"owner"));
                        addReceivers(receivers, owners, path + "owner.", 
dmd.getParent());
                    }
                }
                break;
            case Closure.OWNER_ONLY:
            case Closure.OWNER_FIRST:
                if (dmd.getParent() == null) {
                    receivers.addAll(owners);
                } else {
                    receivers.add(new Receiver<String>(CLOSURE_TYPE, path + 
"owner"));
                    addReceivers(receivers, owners, path + "owner.", 
dmd.getParent());
                }
                if (strategy == Closure.OWNER_FIRST) {
                    addDelegateReceiver(receivers, dmd.getType(), path + 
"delegate");
                }
                break;
        }
    }

    private static void addDelegateReceiver(List<Receiver<String>> receivers, 
ClassNode delegate, String path) {
        receivers.add(new Receiver<String>(delegate, path));
        if (isTraitHelper(delegate)) {
            receivers.add(new Receiver<String>(delegate.getOuterClass(), path));
        }
    }
{code}

I'm not sure if the addition of the {{CLOSURE_TYPE}} receivers are necessary.


was (Author: emilles):
This version of {{addReceivers}} should properly add all owner receivers before 
the delegate in the case of {{OWNER_FIRST}}.  And {{OWNER_ONLY}} gets receivers 
from outer scopes in case of nesting.
{code:java}
    protected void addReceivers(List<Receiver<String>> receivers, 
Collection<Receiver<String>> owners, boolean implicitThis) {
        if (!implicitThis || typeCheckingContext.delegationMetadata == null) {
            receivers.addAll(owners);
        } else {
            addReceivers(receivers, owners, "", 
typeCheckingContext.delegationMetadata);
        }
    }

    private static void addReceivers(List<Receiver<String>> receivers, 
Collection<Receiver<String>> owners, String path, DelegationMetadata dmd) {
        int strategy = dmd.getStrategy();
        switch (strategy) {
            case Closure.DELEGATE_ONLY:
            case Closure.DELEGATE_FIRST:
                addDelegateReceiver(receivers, dmd.getType(), path + 
"delegate");
                if (strategy == Closure.DELEGATE_FIRST) {
                    if (dmd.getParent() == null) {
                        receivers.addAll(owners);
                    } else {
                        receivers.add(new Receiver<String>(CLOSURE_TYPE, path + 
"owner"));
                        addReceivers(receivers, owners, path + "owner.", 
dmd.getParent());
                    }
                }
                break;
            case Closure.OWNER_ONLY:
            case Closure.OWNER_FIRST:
                if (dmd.getParent() == null) {
                    receivers.addAll(owners);
                } else {
                    receivers.add(new Receiver<String>(CLOSURE_TYPE, path + 
"owner"));
                    addReceivers(receivers, owners, path + "owner.", 
dmd.getParent());
                }
                if (strategy == Closure.OWNER_FIRST) {
                    addDelegateReceiver(receivers, dmd.getType(), path + 
"delegate");
                }
                break;
        }
    }

    private static void addDelegateReceiver(List<Receiver<String>> receivers, 
ClassNode delegate, String path) {
        receivers.add(new Receiver<String>(delegate, path));
        if (isTraitHelper(delegate)) {
            receivers.add(new Receiver<String>(delegate.getOuterClass(), path));
        }
    }
{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)

Reply via email to