diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java index 3f13dde7970e..d32c1d4ca96a 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java @@ -26,6 +26,7 @@ import com.sun.source.tree.ImportTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.NewClassTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; @@ -33,6 +34,7 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; import java.beans.PropertyVetoException; import java.io.File; import java.io.IOException; @@ -1553,6 +1555,63 @@ public void run(WorkingCopy parameter) throws Exception { parameter.rewrite(testTree, nueTestTree); } } + + public void testGenerateMethodInLambda() throws Exception { + performTest("test/Test.java", + """ + package test; + public class Test { + private void test(Runnable r) { + test(() -> { + new Base() {}; + }); + } + } + class Base { + public T1 test(T1 p) {} + } + """, + "17", + new Task() { + @Override + public void run(WorkingCopy copy) throws java.lang.Exception { + copy.toPhase(Phase.RESOLVED); + new TreePathScanner() { + @Override + public Void visitNewClass(NewClassTree node, Void p) { + if (node.getClassBody() != null) { + ClassTree clazz = node.getClassBody(); + TypeElement anon = (TypeElement) copy.getTrees().getElement(new TreePath(getCurrentPath(), clazz)); + TypeElement superClass = (TypeElement) ((DeclaredType) anon.getSuperclass()).asElement(); + ExecutableElement method = (ExecutableElement) superClass.getEnclosedElements().stream().filter(el -> el.getSimpleName().contentEquals("test")).findAny().get(); + copy.rewrite(clazz, copy.getTreeMaker().addClassMember(clazz, GeneratorUtilities.get(copy).createOverridingMethod(anon, method))); + } + return super.visitNewClass(node, p); + } + + }.scan(copy.getCompilationUnit(), null); + } + }, + new ContentValidator(""" + package test; + public class Test { + private void test(Runnable r) { + test(() -> { + new Base() { + @Override + public Void test(Void p) { + return super.test(p); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/OverriddenMethodBody + } + }; + }); + } + } + class Base { + public T1 test(T1 p) {} + } + """), + false); + } private static final class ContentValidator implements Validator { diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java index 14b7657f89ab..97041a85f6fc 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java @@ -49,7 +49,9 @@ import java.util.regex.Pattern; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.source.Comment.Style; @@ -874,4 +876,27 @@ public void testPathForInUnnamedClass() throws Exception { assertEquals("System", it.getName().toString()); } + + public void testAttributeTreeCrashes() throws Exception { + this.sourceLevel = "21"; + + String code = """ + package test; + public class Test { + private void test(Runnable r) { + test(() -> { + | + }); + } + } + """; + + prepareTest("Test", code.replace("|", "")); + + int pos = code.indexOf("|"); + TreePath tp = info.getTreeUtilities().pathFor(pos); + Scope scope = info.getTrees().getScope(tp); + StatementTree tree = info.getTreeUtilities().parseStatement("{ return super.test(p); }", new SourcePositions[1]); + info.getTreeUtilities().attributeTree(tree, scope); + } } diff --git a/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBJavacTrees.java b/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBJavacTrees.java index 980bed37babc..3ef60eddfe75 100644 --- a/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBJavacTrees.java +++ b/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBJavacTrees.java @@ -24,13 +24,21 @@ import com.sun.tools.javac.api.JavacTrees; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Check; +import com.sun.tools.javac.comp.Check.CheckContext; +import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.util.Context; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.lang.model.element.Element; /** @@ -39,7 +47,9 @@ */ public class NBJavacTrees extends JavacTrees { + private static final Logger LOG = Logger.getLogger(NBJavacTrees.class.getName()); private final Map element2paths = new HashMap<>(); + private final CheckContext chkBasicHandler; public static void preRegister(Context context) { context.put(JavacTrees.class, new Context.Factory() { @@ -50,7 +60,21 @@ public JavacTrees make(Context c) { } protected NBJavacTrees(Context context) { super(context); + Check chk = Check.instance(context); + + CheckContext chkBasicHandlerTemp = null; + + try { + if (basicHandlerField != null) { + chkBasicHandlerTemp = (CheckContext) basicHandlerField.get(chk); + } + } catch (ReflectiveOperationException ex) { + LOG.log(Level.FINE, null, ex); + } + + chkBasicHandler = chkBasicHandlerTemp; } + @Override public TreePath getPath(Element e) { TreePath path = super.getPath(e); @@ -81,4 +105,53 @@ public JCTree visitVariable(VariableTree node, JCTree p) { }; } + @Override + public JavacScope getScope(TreePath path) { + JavacScope result = super.getScope(path); + + if (returnResultField != null) { + Env env = result.getEnv(); + + try { + Object returnResult = returnResultField.get(env.info); + if (returnResult != null) { + //ensure the returnResult's checkContext is the Check.basicHandler: + returnResultField.set(env.info, dupMethod.invoke(returnResult, chkBasicHandler)); + } + } catch (ReflectiveOperationException ex) { + LOG.log(Level.FINE, null, ex); + } + } + + return result; + } + + private static final Field basicHandlerField; + private static final Field returnResultField; + private static final Method dupMethod; + + static { + Field basicHandlerFieldTemp; + Field returnResultFieldTemp; + Method dupMethodTemp; + + try { + basicHandlerFieldTemp = Check.class.getDeclaredField("basicHandler"); + basicHandlerFieldTemp.setAccessible(true); + returnResultFieldTemp = AttrContext.class.getDeclaredField("returnResult"); + returnResultFieldTemp.setAccessible(true); + dupMethodTemp = Class.forName("com.sun.tools.javac.comp.Attr$ResultInfo") + .getDeclaredMethod("dup", CheckContext.class); + dupMethodTemp.setAccessible(true); + } catch (ReflectiveOperationException ex) { + LOG.log(Level.FINE, null, ex); + basicHandlerFieldTemp = null; + returnResultFieldTemp = null; + dupMethodTemp = null; + } + + basicHandlerField = basicHandlerFieldTemp; + returnResultField = returnResultFieldTemp; + dupMethod = dupMethodTemp; + } }