1 /*
2  * Copyright (c) 2014, 2018, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.jshell;
27 
28 
29 import com.sun.source.tree.ClassTree;
30 import com.sun.source.tree.CompilationUnitTree;
31 import com.sun.source.tree.MethodTree;
32 import com.sun.source.tree.StatementTree;
33 import com.sun.source.tree.Tree;
34 import com.sun.source.tree.VariableTree;
35 import com.sun.source.util.SourcePositions;
36 import com.sun.source.util.Trees;
37 import com.sun.tools.javac.code.Type;
38 import com.sun.tools.javac.code.Type.MethodType;
39 import com.sun.tools.javac.code.Types;
40 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
41 import com.sun.tools.javac.util.Name;
42 import static jdk.jshell.Util.isDoIt;
43 import jdk.jshell.TaskFactory.AnalyzeTask;
44 import jdk.jshell.Wrap.Range;
45 
46 import java.util.List;
47 
48 import java.util.function.Predicate;
49 import java.util.stream.Stream;
50 import javax.lang.model.type.TypeMirror;
51 import jdk.jshell.TypePrinter.AnonymousTypeKind;
52 import jdk.jshell.Util.Pair;
53 
54 /**
55  * Utilities for analyzing compiler API parse trees.
56  * @author Robert Field
57  */
58 
59 class TreeDissector {
60 
61     private final TaskFactory.BaseTask bt;
62     private final ClassTree targetClass;
63     private final CompilationUnitTree targetCompilationUnit;
64     private SourcePositions theSourcePositions = null;
65 
TreeDissector(TaskFactory.BaseTask bt, CompilationUnitTree targetCompilationUnit, ClassTree targetClass)66     private TreeDissector(TaskFactory.BaseTask bt, CompilationUnitTree targetCompilationUnit, ClassTree targetClass) {
67         this.bt = bt;
68         this.targetCompilationUnit = targetCompilationUnit;
69         this.targetClass = targetClass;
70     }
71 
createByFirstClass(TaskFactory.BaseTask bt)72     static TreeDissector createByFirstClass(TaskFactory.BaseTask bt) {
73         Pair<CompilationUnitTree, ClassTree> pair = classes(bt.firstCuTree())
74                 .findFirst().orElseGet(() -> new Pair<>(bt.firstCuTree(), null));
75 
76         return new TreeDissector(bt, pair.first, pair.second);
77     }
78 
79     private static final Predicate<? super Tree> isClassOrInterface =
80             t -> t.getKind() == Tree.Kind.CLASS || t.getKind() == Tree.Kind.INTERFACE;
81 
classes(CompilationUnitTree cut)82     private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(CompilationUnitTree cut) {
83         return cut == null
84                 ? Stream.empty()
85                 : cut.getTypeDecls().stream()
86                         .filter(isClassOrInterface)
87                         .map(decl -> new Pair<>(cut, (ClassTree)decl));
88     }
89 
classes(Iterable<? extends CompilationUnitTree> cuts)90     private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(Iterable<? extends CompilationUnitTree> cuts) {
91         return Util.stream(cuts)
92                 .flatMap(TreeDissector::classes);
93     }
94 
createBySnippet(TaskFactory.BaseTask bt, Snippet si)95     static TreeDissector createBySnippet(TaskFactory.BaseTask bt, Snippet si) {
96         String name = si.className();
97 
98         Pair<CompilationUnitTree, ClassTree> pair = classes(bt.cuTrees())
99                 .filter(p -> p.second.getSimpleName().contentEquals(name))
100                 .findFirst().orElseThrow(() ->
101                         new IllegalArgumentException("Class " + name + " is not found."));
102 
103         return new TreeDissector(bt, pair.first, pair.second);
104     }
105 
types()106     Types types() {
107         return bt.types();
108     }
109 
trees()110     Trees trees() {
111         return bt.trees();
112     }
113 
getSourcePositions()114     SourcePositions getSourcePositions() {
115         if (theSourcePositions == null) {
116             theSourcePositions = trees().getSourcePositions();
117         }
118         return theSourcePositions;
119     }
120 
getStartPosition(Tree tree)121     int getStartPosition(Tree tree) {
122         return (int) getSourcePositions().getStartPosition(targetCompilationUnit, tree);
123     }
124 
getEndPosition(Tree tree)125     int getEndPosition(Tree tree) {
126         return (int) getSourcePositions().getEndPosition(targetCompilationUnit, tree);
127     }
128 
treeToRange(Tree tree)129     Range treeToRange(Tree tree) {
130         return new Range(getStartPosition(tree), getEndPosition(tree));
131     }
132 
treeListToRange(List<? extends Tree> treeList)133     Range treeListToRange(List<? extends Tree> treeList) {
134         int start = Integer.MAX_VALUE;
135         int end = -1;
136         for (Tree t : treeList) {
137             int tstart = getStartPosition(t);
138             int tend = getEndPosition(t);
139             if (tstart < start) {
140                 start = tstart;
141             }
142             if (tend > end) {
143                 end = tend;
144             }
145         }
146         if (start == Integer.MAX_VALUE) {
147             return null;
148         }
149         return new Range(start, end);
150     }
151 
method(MethodSnippet msn)152     MethodTree method(MethodSnippet msn) {
153         if (targetClass == null) {
154             return null;
155         }
156         OuterWrap ow = msn.outerWrap();
157         if (!(ow instanceof OuterSnippetsClassWrap)) {
158             return null;
159         }
160         int ordinal = ((OuterSnippetsClassWrap) ow).ordinal(msn);
161         if (ordinal < 0) {
162             return null;
163         }
164         int count = 0;
165         String name = msn.name();
166         for (Tree mem : targetClass.getMembers()) {
167             if (mem.getKind() == Tree.Kind.METHOD) {
168                 MethodTree mt = (MethodTree) mem;
169                 if (mt.getName().toString().equals(name)) {
170                     if (count == ordinal) {
171                         return mt;
172                     }
173                     ++count;
174                 }
175             }
176         }
177         return null;
178     }
179 
firstStatement()180     StatementTree firstStatement() {
181         if (targetClass != null) {
182             for (Tree mem : targetClass.getMembers()) {
183                 if (mem.getKind() == Tree.Kind.METHOD) {
184                     MethodTree mt = (MethodTree) mem;
185                     if (isDoIt(mt.getName())) {
186                         List<? extends StatementTree> stmts = mt.getBody().getStatements();
187                         if (!stmts.isEmpty()) {
188                             return stmts.get(0);
189                         }
190                     }
191                 }
192             }
193         }
194         return null;
195     }
196 
firstVariable()197     VariableTree firstVariable() {
198         if (targetClass != null) {
199             for (Tree mem : targetClass.getMembers()) {
200                 if (mem.getKind() == Tree.Kind.VARIABLE) {
201                     VariableTree vt = (VariableTree) mem;
202                     return vt;
203                 }
204             }
205         }
206         return null;
207     }
208 
typeOfMethod(MethodSnippet msn)209     String typeOfMethod(MethodSnippet msn) {
210         Tree unitTree = method(msn);
211         if (unitTree instanceof JCMethodDecl) {
212             JCMethodDecl mtree = (JCMethodDecl) unitTree;
213             Type mt = types().erasure(mtree.type);
214             if (mt instanceof MethodType) {
215                 return signature(types(), (MethodType) mt);
216             }
217         }
218         return null;
219     }
220 
signature(Types types, MethodType mt)221     static String signature(Types types, MethodType mt) {
222         TDSignatureGenerator sg = new TDSignatureGenerator(types);
223         sg.assembleSig(mt);
224         return sg.toString();
225     }
226 
printType(AnalyzeTask at, JShell state, TypeMirror type)227     public static String printType(AnalyzeTask at, JShell state, TypeMirror type) {
228         Type typeImpl = (Type) type;
229         try {
230             TypePrinter tp = new TypePrinter(at.messages(),
231                     state.maps::fullClassNameAndPackageToClass, true, AnonymousTypeKind.DISPLAY);
232             return tp.toString(typeImpl);
233         } catch (Exception ex) {
234             return null;
235         }
236     }
237 
238     /**
239      * Signature Generation
240      */
241     private static class TDSignatureGenerator extends Types.SignatureGenerator {
242 
243         /**
244          * An output buffer for type signatures.
245          */
246         StringBuilder sb = new StringBuilder();
247 
TDSignatureGenerator(Types types)248         TDSignatureGenerator(Types types) {
249             super(types);
250         }
251 
252         @Override
append(char ch)253         protected void append(char ch) {
254             sb.append(ch);
255         }
256 
257         @Override
append(byte[] ba)258         protected void append(byte[] ba) {
259             sb.append(new String(ba));
260         }
261 
262         @Override
append(Name name)263         protected void append(Name name) {
264             sb.append(name);
265         }
266 
267         @Override
toString()268         public String toString() {
269             return sb.toString();
270         }
271     }
272 }
273