groovy-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Eric Milles (JIRA)" <j...@apache.org>
Subject [jira] [Comment Edited] (GROOVY-9086) @CompileStatic of closure calls with OWNER_FIRST fail at runtime with ClassCastException
Date Tue, 16 Apr 2019 22:59:00 GMT

    [ 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 10:58 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);
                    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}


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}

> @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)

Mime
View raw message