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