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