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