Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CRASH] When @Getter(lazy = true) has Lambda in the initialization expression. #2300

Closed
NekoCaffeine opened this issue Nov 30, 2019 · 5 comments · Fixed by #2516
Closed

[CRASH] When @Getter(lazy = true) has Lambda in the initialization expression. #2300

NekoCaffeine opened this issue Nov 30, 2019 · 5 comments · Fixed by #2516

Comments

@NekoCaffeine
Copy link

NekoCaffeine commented Nov 30, 2019

When Javac tried to parse (attr) the JCLambda node in the LazyGetter Method Body generated by @Getter(lazy = true), it crashed because the cache type at that location did not match what was expected.

And in addition to crashing it can cause type inference to fail.

JDK: OpenJDK 13.0.1+9
Lombok: 1.18.11 (2019-09-26 08:56:18 UTC)

Reproduced by the following code:

import lombok.Getter;

public class TestLombokBugLazyGetter {
    
    public static String bar() { return ""; }
    
    @Getter(lazy = true)
    private final Object field = foo(bar(), null, () -> { });
    
    public static Object foo(final Object arg0, final Object arg1, final Runnable arg3) { return null; }
    
}
 An exception has occurred in the compiler (13.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the
 Java compiler in your report. Thank you.
  java.lang.ClassCastException: class com.sun.tools.javac.tree.JCTree$JCLambda cannot be cast to class com.sun.tools.javac.tree.JCTree$JCMethodInvocation (com.sun.tools.javac.tree.JCTree$JCLambda and com.sun.tools.javac.tree.JCTree$JCMethodInvocation are in module jdk.compiler of loader 'app')
   at jdk.compiler/com.sun.tools.javac.comp.ArgumentAttr$ResolvedMethodType.dup(ArgumentAttr.java:673)
   at jdk.compiler/com.sun.tools.javac.comp.ArgumentAttr.processArg(ArgumentAttr.java:242)
   at jdk.compiler/com.sun.tools.javac.comp.ArgumentAttr.visitLambda(ArgumentAttr.java:301)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1914)
   at jdk.compiler/com.sun.tools.javac.comp.ArgumentAttr.attribArg(ArgumentAttr.java:199)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:664)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribArgs(Attr.java:766)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitApply(Attr.java:2141)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1737)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribExpr(Attr.java:710)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitVarDef(Attr.java:1191)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:966)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStats(Attr.java:758)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:1311)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1030)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitIf(Attr.java:1856)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCIf.accept(JCTree.java:1492)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStats(Attr.java:758)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:1311)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1030)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitSynchronized(Attr.java:1575)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCSynchronized.accept(JCTree.java:1348)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStats(Attr.java:758)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:1311)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1030)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitIf(Attr.java:1856)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCIf.accept(JCTree.java:1492)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStats(Attr.java:758)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:1311)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1030)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.visitMethodDef(Attr.java:1115)
   at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:876)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribTree(Attr.java:666)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribStat(Attr.java:739)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:4843)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribClass(Attr.java:4734)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attribClass(Attr.java:4663)
   at jdk.compiler/com.sun.tools.javac.comp.Attr.attrib(Attr.java:4608)
   at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1346)
   at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:972)
   at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:318)
   at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
   at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:57)
   at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:43)

Cause of error:

https://github.com/rzwitserloot/lombok/blob/a81df33c10f69d49425bdd0606052178a172a66f/src/core/lombok/javac/handlers/HandleGetter.java#L254-L255
This should not be done here.

com/sun/tools/javac/comp/ArgumentAttr.java

    /**
     * Process a method argument; this method allows the caller to specify a custom speculative attribution
     * logic (this is used e.g. for lambdas).
     */
    @SuppressWarnings("unchecked")
    <T extends JCExpression, Z extends ArgumentType<T>> void processArg(T that, Supplier<Z> argumentTypeFactory) {
        UniquePos pos = new UniquePos(that);
        // in UniquePos <init>
        // this.pos = that.pos;
        Z cached = (Z)argumentTypeCache.get(pos);
        // got com.sun.tools.javac.comp.ArgumentAttr.ResolvedMethodType extends ResolvedMemberType<JCMethodInvocation>
        // <T extends JCExpression> T => JCMethodInvocation
        // but in arg (T that) : T => JCLambda
        if (cached != null) {
            //dup existing speculative type
            setResult(that, cached.dup(that, env));
            // ArgumentType<JCMethodInvocation> dup(JCMethodInvocation tree, Env<AttrContext> env)
            // (JCMethodInvocation) tree => java.lang.ClassCastException
            // class JCTree$JCLambda cannot be cast to class JCTree$JCMethodInvocation
        } else {
            Z res = argumentTypeFactory.get();
            argumentTypeCache.put(pos, res);
            setResult(that, res);
        }
    }

Maybe we should discuss this issue or I send a PR to fix it.

@rzwitserloot
Copy link
Collaborator

If you have a PR, by all means. Including lambdas in that location is fairly exotic, I don't think this is a high priority fix.

@rspilker
Copy link
Collaborator

Suggested workaround, also works in some other similar scenario's: move the initializer code to a private static method:

import lombok.Getter;

public class TestLombokBugLazyGetter {
    
    public static String bar() { return ""; }
    
    @Getter(lazy = true)
    private final Object field = fooInitializer();
    
    private static Object fooInitializer() {
        return foo(bar(), null, () -> { });
    }
    
    public static Object foo(final Object arg0, final Object arg1, final Runnable arg3) { return null; }

}

@NekoCaffeine
Copy link
Author

This is easy to fix, just let the recursiveSetGeneratedBy method not process the initialization expression.

@NekoCaffeine
Copy link
Author

And only when the initialization expression keeps the original position mark, the relevant behavior of Javac is correct.

@rspilker
Copy link
Collaborator

Oh, good one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants