TreeDissector.java revision 3185:51136404ee5e
1/*
2 * Copyright (c) 2014, 2015, 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
26package jdk.jshell;
27
28
29import com.sun.source.tree.ClassTree;
30import com.sun.source.tree.CompilationUnitTree;
31import com.sun.source.tree.ExpressionTree;
32import com.sun.source.tree.MethodTree;
33import com.sun.source.tree.ReturnTree;
34import com.sun.source.tree.StatementTree;
35import com.sun.source.tree.Tree;
36import com.sun.source.tree.VariableTree;
37import com.sun.source.util.SourcePositions;
38import com.sun.source.util.TreePath;
39import com.sun.source.util.Trees;
40import com.sun.tools.javac.code.Type;
41import com.sun.tools.javac.code.Type.MethodType;
42import com.sun.tools.javac.code.Types;
43import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
44import com.sun.tools.javac.util.JavacMessages;
45import com.sun.tools.javac.util.Name;
46import static jdk.jshell.Util.isDoIt;
47import jdk.jshell.Wrap.Range;
48import java.util.List;
49import java.util.Locale;
50import java.util.function.BinaryOperator;
51import java.util.function.Predicate;
52import java.util.stream.Stream;
53import javax.lang.model.type.TypeMirror;
54import jdk.jshell.Util.Pair;
55
56/**
57 * Utilities for analyzing compiler API parse trees.
58 * @author Robert Field
59 */
60
61class TreeDissector {
62
63    private static final String OBJECT_TYPE = "Object";
64
65    static class ExpressionInfo {
66
67        boolean isNonVoid;
68        String typeName;
69        ExpressionTree tree;
70        String signature;
71    }
72
73    private final TaskFactory.BaseTask bt;
74    private final ClassTree targetClass;
75    private final CompilationUnitTree targetCompilationUnit;
76    private SourcePositions theSourcePositions = null;
77
78    private TreeDissector(TaskFactory.BaseTask bt, CompilationUnitTree targetCompilationUnit, ClassTree targetClass) {
79        this.bt = bt;
80        this.targetCompilationUnit = targetCompilationUnit;
81        this.targetClass = targetClass;
82    }
83
84    static TreeDissector createByFirstClass(TaskFactory.BaseTask bt) {
85        Pair<CompilationUnitTree, ClassTree> pair = classes(bt.firstCuTree())
86                .findFirst().orElseGet(() -> new Pair<>(bt.firstCuTree(), null));
87
88        return new TreeDissector(bt, pair.first, pair.second);
89    }
90
91    private static final Predicate<? super Tree> isClassOrInterface =
92            t -> t.getKind() == Tree.Kind.CLASS || t.getKind() == Tree.Kind.INTERFACE;
93
94    private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(CompilationUnitTree cut) {
95        return cut == null
96                ? Stream.empty()
97                : cut.getTypeDecls().stream()
98                        .filter(isClassOrInterface)
99                        .map(decl -> new Pair<>(cut, (ClassTree)decl));
100    }
101
102    private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(Iterable<? extends CompilationUnitTree> cuts) {
103        return Util.stream(cuts)
104                .flatMap(TreeDissector::classes);
105    }
106
107    static TreeDissector createBySnippet(TaskFactory.BaseTask bt, Snippet si) {
108        String name = si.className();
109
110        Pair<CompilationUnitTree, ClassTree> pair = classes(bt.cuTrees())
111                .filter(p -> p.second.getSimpleName().contentEquals(name))
112                .findFirst().orElseThrow(() ->
113                        new IllegalArgumentException("Class " + name + " is not found."));
114
115        return new TreeDissector(bt, pair.first, pair.second);
116    }
117
118    Types types() {
119        return bt.types();
120    }
121
122    Trees trees() {
123        return bt.trees();
124    }
125
126    SourcePositions getSourcePositions() {
127        if (theSourcePositions == null) {
128            theSourcePositions = trees().getSourcePositions();
129        }
130        return theSourcePositions;
131    }
132
133    int getStartPosition(Tree tree) {
134        return (int) getSourcePositions().getStartPosition(targetCompilationUnit, tree);
135    }
136
137    int getEndPosition(Tree tree) {
138        return (int) getSourcePositions().getEndPosition(targetCompilationUnit, tree);
139    }
140
141    Range treeToRange(Tree tree) {
142        return new Range(getStartPosition(tree), getEndPosition(tree));
143    }
144
145    Range treeListToRange(List<? extends Tree> treeList) {
146        int start = Integer.MAX_VALUE;
147        int end = -1;
148        for (Tree t : treeList) {
149            int tstart = getStartPosition(t);
150            int tend = getEndPosition(t);
151            if (tstart < start) {
152                start = tstart;
153            }
154            if (tend > end) {
155                end = tend;
156            }
157        }
158        if (start == Integer.MAX_VALUE) {
159            return null;
160        }
161        return new Range(start, end);
162    }
163
164    Tree firstClassMember() {
165        if (targetClass != null) {
166            //TODO: missing classes
167            for (Tree mem : targetClass.getMembers()) {
168                if (mem.getKind() == Tree.Kind.VARIABLE) {
169                    return mem;
170                }
171                if (mem.getKind() == Tree.Kind.METHOD) {
172                    MethodTree mt = (MethodTree) mem;
173                    if (!isDoIt(mt.getName()) && !mt.getName().toString().equals("<init>")) {
174                        return mt;
175                    }
176                }
177            }
178        }
179        return null;
180    }
181
182    StatementTree firstStatement() {
183        if (targetClass != null) {
184            for (Tree mem : targetClass.getMembers()) {
185                if (mem.getKind() == Tree.Kind.METHOD) {
186                    MethodTree mt = (MethodTree) mem;
187                    if (isDoIt(mt.getName())) {
188                        List<? extends StatementTree> stmts = mt.getBody().getStatements();
189                        if (!stmts.isEmpty()) {
190                            return stmts.get(0);
191                        }
192                    }
193                }
194            }
195        }
196        return null;
197    }
198
199    VariableTree firstVariable() {
200        if (targetClass != null) {
201            for (Tree mem : targetClass.getMembers()) {
202                if (mem.getKind() == Tree.Kind.VARIABLE) {
203                    VariableTree vt = (VariableTree) mem;
204                    return vt;
205                }
206            }
207        }
208        return null;
209    }
210
211
212    ExpressionInfo typeOfReturnStatement(JavacMessages messages, BinaryOperator<String> fullClassNameAndPackageToClass) {
213        ExpressionInfo ei = new ExpressionInfo();
214        Tree unitTree = firstStatement();
215        if (unitTree instanceof ReturnTree) {
216            ei.tree = ((ReturnTree) unitTree).getExpression();
217            if (ei.tree != null) {
218                TreePath viPath = trees().getPath(targetCompilationUnit, ei.tree);
219                if (viPath != null) {
220                    TypeMirror tm = trees().getTypeMirror(viPath);
221                    if (tm != null) {
222                        Type type = (Type)tm;
223                        TypePrinter tp = new TypePrinter(messages, fullClassNameAndPackageToClass, type);
224                        ei.typeName = tp.visit(type, Locale.getDefault());
225                        switch (tm.getKind()) {
226                            case VOID:
227                            case NONE:
228                            case ERROR:
229                            case OTHER:
230                                break;
231                            case NULL:
232                                ei.isNonVoid = true;
233                                ei.typeName = OBJECT_TYPE;
234                                break;
235                            default: {
236                                ei.isNonVoid = true;
237                                break;
238
239                            }
240                        }
241                    }
242                }
243            }
244        }
245        return ei;
246    }
247
248    String typeOfMethod() {
249        Tree unitTree = firstClassMember();
250        if (unitTree instanceof JCMethodDecl) {
251            JCMethodDecl mtree = (JCMethodDecl) unitTree;
252            Type mt = types().erasure(mtree.type);
253            if (mt instanceof MethodType) {
254                return signature(types(), (MethodType) mt);
255            }
256        }
257        return null;
258    }
259
260    static String signature(Types types, MethodType mt) {
261        TDSignatureGenerator sg = new TDSignatureGenerator(types);
262        sg.assembleSig(mt);
263        return sg.toString();
264    }
265
266    /**
267     * Signature Generation
268     */
269    private static class TDSignatureGenerator extends Types.SignatureGenerator {
270
271        /**
272         * An output buffer for type signatures.
273         */
274        StringBuilder sb = new StringBuilder();
275
276        TDSignatureGenerator(Types types) {
277            super(types);
278        }
279
280        @Override
281        protected void append(char ch) {
282            sb.append(ch);
283        }
284
285        @Override
286        protected void append(byte[] ba) {
287            sb.append(new String(ba));
288        }
289
290        @Override
291        protected void append(Name name) {
292            sb.append(name);
293        }
294
295        @Override
296        public String toString() {
297            return sb.toString();
298        }
299    }
300}
301