| /* |
| * 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.TaskFactory.AnalyzeTask; |
| import jdk.jshell.Wrap.Range; |
| |
| import java.util.List; |
| import java.util.Locale; |
| |
| import java.util.function.Predicate; |
| import java.util.stream.Stream; |
| import javax.lang.model.type.TypeMirror; |
| import jdk.jshell.Util.Pair; |
| |
| /** |
| * 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<CompilationUnitTree, ClassTree> pair = classes(bt.firstCuTree()) |
| .findFirst().orElseGet(() -> new Pair<>(bt.firstCuTree(), null)); |
| |
| return new TreeDissector(bt, pair.first, pair.second); |
| } |
| |
| private static final Predicate<? super Tree> isClassOrInterface = |
| t -> t.getKind() == Tree.Kind.CLASS || t.getKind() == Tree.Kind.INTERFACE; |
| |
| private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(CompilationUnitTree cut) { |
| return cut == null |
| ? Stream.empty() |
| : cut.getTypeDecls().stream() |
| .filter(isClassOrInterface) |
| .map(decl -> new Pair<>(cut, (ClassTree)decl)); |
| } |
| |
| private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(Iterable<? extends CompilationUnitTree> cuts) { |
| return Util.stream(cuts) |
| .flatMap(TreeDissector::classes); |
| } |
| |
| static TreeDissector createBySnippet(TaskFactory.BaseTask bt, Snippet si) { |
| String name = si.className(); |
| |
| Pair<CompilationUnitTree, ClassTree> 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<? extends Tree> 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); |
| } |
| |
| MethodTree method(MethodSnippet msn) { |
| if (targetClass == null) { |
| return null; |
| } |
| OuterWrap ow = msn.outerWrap(); |
| if (!(ow instanceof OuterSnippetsClassWrap)) { |
| return null; |
| } |
| int ordinal = ((OuterSnippetsClassWrap) ow).ordinal(msn); |
| if (ordinal < 0) { |
| return null; |
| } |
| int count = 0; |
| String name = msn.name(); |
| for (Tree mem : targetClass.getMembers()) { |
| if (mem.getKind() == Tree.Kind.METHOD) { |
| MethodTree mt = (MethodTree) mem; |
| if (mt.getName().toString().equals(name)) { |
| if (count == ordinal) { |
| return mt; |
| } |
| ++count; |
| } |
| } |
| } |
| 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<? extends StatementTree> 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 = 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(MethodSnippet msn) { |
| Tree unitTree = method(msn); |
| 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(); |
| } |
| |
| public static String printType(AnalyzeTask at, JShell state, TypeMirror type) { |
| Type typeImpl = (Type) type; |
| TypePrinter tp = new TypePrinter(at.messages(), state.maps::fullClassNameAndPackageToClass, typeImpl); |
| return tp.visit(typeImpl, Locale.getDefault()); |
| } |
| |
| /** |
| * 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(); |
| } |
| } |
| } |