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 }