/* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8223443 * @summary Verify binary names are not changed and are correct * when using Trees.getScope * @modules jdk.compiler */ import com.sun.source.tree.ClassTree; import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.Element; import javax.lang.model.element.NestingKind; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.tools.JavaCompiler; import javax.tools.SimpleJavaFileObject; import javax.tools.ToolProvider; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.Scope; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.source.util.JavacTask; import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; import static javax.tools.JavaFileObject.Kind.SOURCE; public class TestGetScopeBinaryNames { public static void main(String... args) throws IOException { new TestGetScopeBinaryNames().run(); } public void run() throws IOException { class EnclosingDesc { final String code; final boolean supportsLocal; public EnclosingDesc(String code, boolean supportsLocal) { this.code = code; this.supportsLocal = supportsLocal; } } List enclosingEnvs = List.of( new EnclosingDesc("class Test {" + " void test() {" + " $" + " }" + "}", true), new EnclosingDesc("class Test {" + " {" + " $" + " }" + "}", true), new EnclosingDesc("class Test {" + " static {" + " $" + " }" + "}", true), new EnclosingDesc("class Test {" + " Object I = $" + "}", true) ); class LocalDesc { final String localCode; final boolean isLocalClass; public LocalDesc(String localCode, boolean isLocalClass) { this.localCode = localCode; this.isLocalClass = isLocalClass; } } List locals = List.of( new LocalDesc("new A() {" + " class AI extends B {" + " class AII extends C {" + " private void t() {" + " new D() { class DI extends E {} };" + " }" + " }" + " private void t() { new F() {}; }" + " }" + " private void t() { new G() {}; }" + "};", false), new LocalDesc("class AA extends A {" + " class AI extends B {" + " class AII extends C {" + " private void t() {" + " new D() { class DI extends E {} };" + " }" + " }" + " private void t() { new F() {}; }" + " }" + " private void t() { new G() {}; }" + "}", false) ); String markerClasses = "class A {} class B {} class C {}" + "class D {} class E {} class F {}" + "class G {}"; for (EnclosingDesc enclosing : enclosingEnvs) { for (LocalDesc local : locals) { if (!local.isLocalClass || enclosing.supportsLocal) { doTest(enclosing.code.replace("$", local.localCode) + markerClasses); } } } } void doTest(String code, String... expected) throws IOException { Map name2BinaryName = new HashMap<>(); Map name2QualifiedName = new HashMap<>(); computeNames(code, name2BinaryName, name2QualifiedName); JavaCompiler c = ToolProvider.getSystemJavaCompiler(); JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, List.of(new MyFileObject(code))); CompilationUnitTree cut = t.parse().iterator().next(); Trees trees = Trees.instance(t); t.addTaskListener(new TaskListener() { @Override public void finished(TaskEvent e) { if (e.getKind() == TaskEvent.Kind.ENTER) { new TreePathScanner() { @Override public Void scan(Tree tree, Void p) { if (tree != null && !isInExtendsClause(getCurrentPath(), tree)) { TreePath path = new TreePath(getCurrentPath(), tree); Scope scope = trees.getScope(path); checkScope(t.getElements(), scope, name2BinaryName, name2QualifiedName); } return super.scan(tree, p); } }.scan(cut, null); } } }); t.analyze(); new TreePathScanner() { @Override public Void visitClass(ClassTree node, Void p) { TypeElement type = (TypeElement) trees.getElement(getCurrentPath()); checkClass(t.getElements(), type, name2BinaryName, name2QualifiedName); return super.visitClass(node, p); } }.scan(cut, null); new TreePathScanner() { @Override public Void scan(Tree tree, Void p) { if (tree != null && !isInExtendsClause(getCurrentPath(), tree)) { TreePath path = new TreePath(getCurrentPath(), tree); Scope scope = trees.getScope(path); checkScope(t.getElements(), scope, name2BinaryName, name2QualifiedName); } return super.scan(tree, p); } }.scan(cut, null); } void computeNames(String code, Map name2BinaryName, Map name2QualifiedName) throws IOException { JavaCompiler c = ToolProvider.getSystemJavaCompiler(); JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, List.of(new MyFileObject(code))); CompilationUnitTree cut = t.parse().iterator().next(); t.analyze(); new TreePathScanner() { Trees trees = Trees.instance(t); Elements els = t.getElements(); @Override public Void visitClass(ClassTree node, Void p) { TypeElement type = (TypeElement) trees.getElement(getCurrentPath()); String key = type.getSuperclass().toString(); name2BinaryName.put(key, els.getBinaryName(type).toString()); name2QualifiedName.put(key, type.getQualifiedName().toString()); return super.visitClass(node, p); } }.scan(cut, null); } boolean isInExtendsClause(TreePath clazz, Tree toCheck) { return clazz != null && clazz.getLeaf().getKind() == Kind.CLASS && ((ClassTree) clazz.getLeaf()).getExtendsClause() == toCheck; } void checkClass(Elements els, TypeElement type, Map name2BinaryName, Map name2QualifiedName) { if (type.getNestingKind() == NestingKind.TOP_LEVEL || type.getNestingKind() == NestingKind.MEMBER) { return ; } String binaryName = name2BinaryName.get(type.getSuperclass().toString()); if (!els.getBinaryName(type).contentEquals(binaryName)) { throw new AssertionError("Unexpected: " + els.getBinaryName(type)); } String qualifiedName = name2QualifiedName.get(type.getSuperclass().toString()); if (qualifiedName != null) { if (!type.getQualifiedName().contentEquals(qualifiedName)) { throw new AssertionError("Unexpected: " + type.getQualifiedName() + ", expected: " + qualifiedName); } } } void checkScope(Elements els, Scope scope, Map name2BinaryName, Map name2QualifiedName) { while (scope != null) { for (Element el : scope.getLocalElements()) { if (el.getKind().isClass()) { checkClass(els, (TypeElement) el, name2BinaryName, name2QualifiedName); } } scope = scope.getEnclosingScope(); } } class MyFileObject extends SimpleJavaFileObject { private final String code; MyFileObject(String code) { super(URI.create("myfo:///Test.java"), SOURCE); this.code = code; } @Override public String getCharContent(boolean ignoreEncodingErrors) { return code; } } }