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 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.ExpressionTree; 32 import com.sun.source.tree.MethodTree; 33 import com.sun.source.tree.ReturnTree; 34 import com.sun.source.tree.StatementTree; 35 import com.sun.source.tree.Tree; 36 import com.sun.source.tree.VariableTree; 37 import com.sun.source.util.SourcePositions; 38 import com.sun.source.util.TreePath; 39 import com.sun.source.util.Trees; 40 import com.sun.tools.javac.code.Type; 41 import com.sun.tools.javac.code.Type.MethodType; 42 import com.sun.tools.javac.code.Types; 43 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 44 import com.sun.tools.javac.util.JavacMessages; 45 import com.sun.tools.javac.util.Name; 46 import static jdk.jshell.Util.isDoIt; 47 import jdk.jshell.Wrap.Range; 48 import java.util.List; 49 import java.util.Locale; 50 import java.util.function.BinaryOperator; 51 import java.util.function.Predicate; 52 import java.util.stream.Stream; 53 import javax.lang.model.type.TypeMirror; 54 import jdk.jshell.Util.Pair; 55 56 /** 57 * Utilities for analyzing compiler API parse trees. 58 * @author Robert Field 59 */ 60 61 class 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 }