/* * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.jshell; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.util.Name; import static jdk.jshell.Util.isDoIt; import jdk.jshell.Wrap.Range; import java.util.List; import java.util.function.Predicate; import java.util.stream.Stream; import javax.lang.model.type.TypeMirror; import jdk.jshell.Util.Pair; import jdk.jshell.TaskFactory.AnalyzeTask; /** * Utilities for analyzing compiler API parse trees. * @author Robert Field */ class TreeDissector { private static final String OBJECT_TYPE = "Object"; static class ExpressionInfo { boolean isNonVoid; String typeName; ExpressionTree tree; String signature; } private final TaskFactory.BaseTask bt; private final ClassTree targetClass; private final CompilationUnitTree targetCompilationUnit; private SourcePositions theSourcePositions = null; private TreeDissector(TaskFactory.BaseTask bt, CompilationUnitTree targetCompilationUnit, ClassTree targetClass) { this.bt = bt; this.targetCompilationUnit = targetCompilationUnit; this.targetClass = targetClass; } static TreeDissector createByFirstClass(TaskFactory.BaseTask bt) { Pair pair = classes(bt.firstCuTree()) .findFirst().orElseGet(() -> new Pair<>(bt.firstCuTree(), null)); return new TreeDissector(bt, pair.first, pair.second); } private static final Predicate isClassOrInterface = t -> t.getKind() == Tree.Kind.CLASS || t.getKind() == Tree.Kind.INTERFACE; private static Stream> classes(CompilationUnitTree cut) { return cut == null ? Stream.empty() : cut.getTypeDecls().stream() .filter(isClassOrInterface) .map(decl -> new Pair<>(cut, (ClassTree)decl)); } private static Stream> classes(Iterable cuts) { return Util.stream(cuts) .flatMap(TreeDissector::classes); } static TreeDissector createBySnippet(TaskFactory.BaseTask bt, Snippet si) { String name = si.className(); Pair pair = classes(bt.cuTrees()) .filter(p -> p.second.getSimpleName().contentEquals(name)) .findFirst().orElseThrow(() -> new IllegalArgumentException("Class " + name + " is not found.")); return new TreeDissector(bt, pair.first, pair.second); } Types types() { return bt.types(); } Trees trees() { return bt.trees(); } SourcePositions getSourcePositions() { if (theSourcePositions == null) { theSourcePositions = trees().getSourcePositions(); } return theSourcePositions; } int getStartPosition(Tree tree) { return (int) getSourcePositions().getStartPosition(targetCompilationUnit, tree); } int getEndPosition(Tree tree) { return (int) getSourcePositions().getEndPosition(targetCompilationUnit, tree); } Range treeToRange(Tree tree) { return new Range(getStartPosition(tree), getEndPosition(tree)); } Range treeListToRange(List treeList) { int start = Integer.MAX_VALUE; int end = -1; for (Tree t : treeList) { int tstart = getStartPosition(t); int tend = getEndPosition(t); if (tstart < start) { start = tstart; } if (tend > end) { end = tend; } } if (start == Integer.MAX_VALUE) { return null; } return new Range(start, end); } Tree firstClassMember() { if (targetClass != null) { //TODO: missing classes for (Tree mem : targetClass.getMembers()) { if (mem.getKind() == Tree.Kind.VARIABLE) { return mem; } if (mem.getKind() == Tree.Kind.METHOD) { MethodTree mt = (MethodTree) mem; if (!isDoIt(mt.getName()) && !mt.getName().toString().equals("")) { return mt; } } } } return null; } StatementTree firstStatement() { if (targetClass != null) { for (Tree mem : targetClass.getMembers()) { if (mem.getKind() == Tree.Kind.METHOD) { MethodTree mt = (MethodTree) mem; if (isDoIt(mt.getName())) { List stmts = mt.getBody().getStatements(); if (!stmts.isEmpty()) { return stmts.get(0); } } } } } return null; } VariableTree firstVariable() { if (targetClass != null) { for (Tree mem : targetClass.getMembers()) { if (mem.getKind() == Tree.Kind.VARIABLE) { VariableTree vt = (VariableTree) mem; return vt; } } } return null; } ExpressionInfo typeOfReturnStatement(AnalyzeTask at, JShell state) { ExpressionInfo ei = new ExpressionInfo(); Tree unitTree = firstStatement(); if (unitTree instanceof ReturnTree) { ei.tree = ((ReturnTree) unitTree).getExpression(); if (ei.tree != null) { TreePath viPath = trees().getPath(targetCompilationUnit, ei.tree); if (viPath != null) { TypeMirror tm = trees().getTypeMirror(viPath); if (tm != null) { ei.typeName = TypePrinter.printType(at, state, tm); switch (tm.getKind()) { case VOID: case NONE: case ERROR: case OTHER: break; case NULL: ei.isNonVoid = true; ei.typeName = OBJECT_TYPE; break; default: { ei.isNonVoid = true; break; } } } } } } return ei; } String typeOfMethod() { Tree unitTree = firstClassMember(); if (unitTree instanceof JCMethodDecl) { JCMethodDecl mtree = (JCMethodDecl) unitTree; Type mt = types().erasure(mtree.type); if (mt instanceof MethodType) { return signature(types(), (MethodType) mt); } } return null; } static String signature(Types types, MethodType mt) { TDSignatureGenerator sg = new TDSignatureGenerator(types); sg.assembleSig(mt); return sg.toString(); } /** * Signature Generation */ private static class TDSignatureGenerator extends Types.SignatureGenerator { /** * An output buffer for type signatures. */ StringBuilder sb = new StringBuilder(); TDSignatureGenerator(Types types) { super(types); } @Override protected void append(char ch) { sb.append(ch); } @Override protected void append(byte[] ba) { sb.append(new String(ba)); } @Override protected void append(Name name) { sb.append(name); } @Override public String toString() { return sb.toString(); } } }