1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8223443 27 * @summary Verify binary names are not changed and are correct 28 * when using Trees.getScope 29 * @modules jdk.compiler 30 */ 31 32 import com.sun.source.tree.ClassTree; 33 import java.io.IOException; 34 import java.net.URI; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 39 import javax.lang.model.element.Element; 40 import javax.lang.model.element.NestingKind; 41 import javax.lang.model.element.TypeElement; 42 import javax.lang.model.util.Elements; 43 import javax.tools.JavaCompiler; 44 import javax.tools.SimpleJavaFileObject; 45 import javax.tools.ToolProvider; 46 47 import com.sun.source.tree.CompilationUnitTree; 48 import com.sun.source.tree.Scope; 49 import com.sun.source.tree.Tree; 50 import com.sun.source.tree.Tree.Kind; 51 import com.sun.source.util.JavacTask; 52 import com.sun.source.util.TaskEvent; 53 import com.sun.source.util.TaskListener; 54 import com.sun.source.util.TreePath; 55 import com.sun.source.util.TreePathScanner; 56 import com.sun.source.util.Trees; 57 58 import static javax.tools.JavaFileObject.Kind.SOURCE; 59 60 public class TestGetScopeBinaryNames { main(String... args)61 public static void main(String... args) throws IOException { 62 new TestGetScopeBinaryNames().run(); 63 } 64 run()65 public void run() throws IOException { 66 class EnclosingDesc { 67 final String code; 68 final boolean supportsLocal; 69 public EnclosingDesc(String code, boolean supportsLocal) { 70 this.code = code; 71 this.supportsLocal = supportsLocal; 72 } 73 } 74 List<EnclosingDesc> enclosingEnvs = List.of( 75 new EnclosingDesc("class Test {" + 76 " void test() {" + 77 " $" + 78 " }" + 79 "}", 80 true), 81 new EnclosingDesc("class Test {" + 82 " {" + 83 " $" + 84 " }" + 85 "}", 86 true), 87 new EnclosingDesc("class Test {" + 88 " static {" + 89 " $" + 90 " }" + 91 "}", 92 true), 93 new EnclosingDesc("class Test {" + 94 " Object I = $" + 95 "}", 96 true) 97 ); 98 class LocalDesc { 99 final String localCode; 100 final boolean isLocalClass; 101 public LocalDesc(String localCode, boolean isLocalClass) { 102 this.localCode = localCode; 103 this.isLocalClass = isLocalClass; 104 } 105 } 106 List<LocalDesc> locals = List.of( 107 new LocalDesc("new A() {" + 108 " class AI extends B {" + 109 " class AII extends C {" + 110 " private void t() {" + 111 " new D() { class DI extends E {} };" + 112 " }" + 113 " }" + 114 " private void t() { new F() {}; }" + 115 " }" + 116 " private void t() { new G() {}; }" + 117 "};", 118 false), 119 new LocalDesc("class AA extends A {" + 120 " class AI extends B {" + 121 " class AII extends C {" + 122 " private void t() {" + 123 " new D() { class DI extends E {} };" + 124 " }" + 125 " }" + 126 " private void t() { new F() {}; }" + 127 " }" + 128 " private void t() { new G() {}; }" + 129 "}", 130 false) 131 ); 132 String markerClasses = "class A {} class B {} class C {}" + 133 "class D {} class E {} class F {}" + 134 "class G {}"; 135 for (EnclosingDesc enclosing : enclosingEnvs) { 136 for (LocalDesc local : locals) { 137 if (!local.isLocalClass || enclosing.supportsLocal) { 138 doTest(enclosing.code.replace("$", local.localCode) + 139 markerClasses); 140 } 141 } 142 } 143 } 144 doTest(String code, String... expected)145 void doTest(String code, String... expected) throws IOException { 146 Map<String, String> name2BinaryName = new HashMap<>(); 147 Map<String, String> name2QualifiedName = new HashMap<>(); 148 149 computeNames(code, name2BinaryName, name2QualifiedName); 150 151 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 152 JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, 153 List.of(new MyFileObject(code))); 154 CompilationUnitTree cut = t.parse().iterator().next(); 155 Trees trees = Trees.instance(t); 156 157 t.addTaskListener(new TaskListener() { 158 @Override 159 public void finished(TaskEvent e) { 160 if (e.getKind() == TaskEvent.Kind.ENTER) { 161 new TreePathScanner<Void, Void>() { 162 @Override 163 public Void scan(Tree tree, Void p) { 164 if (tree != null && 165 !isInExtendsClause(getCurrentPath(), tree)) { 166 TreePath path = 167 new TreePath(getCurrentPath(), tree); 168 Scope scope = trees.getScope(path); 169 checkScope(t.getElements(), scope, 170 name2BinaryName, name2QualifiedName); 171 } 172 return super.scan(tree, p); 173 } 174 }.scan(cut, null); 175 } 176 } 177 }); 178 179 t.analyze(); 180 181 new TreePathScanner<Void, Void>() { 182 @Override 183 public Void visitClass(ClassTree node, Void p) { 184 TypeElement type = 185 (TypeElement) trees.getElement(getCurrentPath()); 186 checkClass(t.getElements(), type, 187 name2BinaryName, name2QualifiedName); 188 return super.visitClass(node, p); 189 } 190 }.scan(cut, null); 191 192 new TreePathScanner<Void, Void>() { 193 @Override 194 public Void scan(Tree tree, Void p) { 195 if (tree != null && 196 !isInExtendsClause(getCurrentPath(), tree)) { 197 TreePath path = 198 new TreePath(getCurrentPath(), tree); 199 Scope scope = trees.getScope(path); 200 checkScope(t.getElements(), scope, 201 name2BinaryName, name2QualifiedName); 202 } 203 return super.scan(tree, p); 204 } 205 }.scan(cut, null); 206 } 207 computeNames(String code, Map<String, String> name2BinaryName, Map<String, String> name2QualifiedName)208 void computeNames(String code, 209 Map<String, String> name2BinaryName, 210 Map<String, String> name2QualifiedName) throws IOException { 211 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 212 JavacTask t = (JavacTask) c.getTask(null, null, null, null, null, 213 List.of(new MyFileObject(code))); 214 CompilationUnitTree cut = t.parse().iterator().next(); 215 216 t.analyze(); 217 218 new TreePathScanner<Void, Void>() { 219 Trees trees = Trees.instance(t); 220 Elements els = t.getElements(); 221 @Override 222 public Void visitClass(ClassTree node, Void p) { 223 TypeElement type = 224 (TypeElement) trees.getElement(getCurrentPath()); 225 String key = type.getSuperclass().toString(); 226 227 name2BinaryName.put(key, els.getBinaryName(type).toString()); 228 name2QualifiedName.put(key, type.getQualifiedName().toString()); 229 return super.visitClass(node, p); 230 } 231 }.scan(cut, null); 232 } 233 isInExtendsClause(TreePath clazz, Tree toCheck)234 boolean isInExtendsClause(TreePath clazz, Tree toCheck) { 235 return clazz != null && 236 clazz.getLeaf().getKind() == Kind.CLASS && 237 ((ClassTree) clazz.getLeaf()).getExtendsClause() == toCheck; 238 } 239 checkClass(Elements els, TypeElement type, Map<String, String> name2BinaryName, Map<String, String> name2QualifiedName)240 void checkClass(Elements els, TypeElement type, 241 Map<String, String> name2BinaryName, 242 Map<String, String> name2QualifiedName) { 243 if (type.getNestingKind() == NestingKind.TOP_LEVEL || 244 type.getNestingKind() == NestingKind.MEMBER) { 245 return ; 246 } 247 248 String binaryName = name2BinaryName.get(type.getSuperclass().toString()); 249 250 if (!els.getBinaryName(type).contentEquals(binaryName)) { 251 throw new AssertionError("Unexpected: " + els.getBinaryName(type)); 252 } 253 254 String qualifiedName = name2QualifiedName.get(type.getSuperclass().toString()); 255 256 if (qualifiedName != null) { 257 if (!type.getQualifiedName().contentEquals(qualifiedName)) { 258 throw new AssertionError("Unexpected: " + type.getQualifiedName() + 259 ", expected: " + qualifiedName); 260 } 261 } 262 } 263 checkScope(Elements els, Scope scope, Map<String, String> name2BinaryName, Map<String, String> name2QualifiedName)264 void checkScope(Elements els, Scope scope, 265 Map<String, String> name2BinaryName, 266 Map<String, String> name2QualifiedName) { 267 while (scope != null) { 268 for (Element el : scope.getLocalElements()) { 269 if (el.getKind().isClass()) { 270 checkClass(els, (TypeElement) el, 271 name2BinaryName, name2QualifiedName); 272 } 273 } 274 scope = scope.getEnclosingScope(); 275 } 276 } 277 278 class MyFileObject extends SimpleJavaFileObject { 279 private final String code; 280 MyFileObject(String code)281 MyFileObject(String code) { 282 super(URI.create("myfo:///Test.java"), SOURCE); 283 this.code = code; 284 } 285 @Override getCharContent(boolean ignoreEncodingErrors)286 public String getCharContent(boolean ignoreEncodingErrors) { 287 return code; 288 } 289 } 290 } 291 292