| /* |
| * Copyright (c) 2016, 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.ReturnTree; |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.tree.ConditionalExpressionTree; |
| import com.sun.source.tree.ExpressionTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.util.TreePath; |
| import com.sun.source.util.TreePathScanner; |
| import com.sun.tools.javac.code.Symtab; |
| import com.sun.tools.javac.code.Type; |
| import com.sun.tools.javac.code.Types; |
| import jdk.jshell.TaskFactory.AnalyzeTask; |
| |
| /** |
| * Compute information about an expression string, particularly its type name. |
| */ |
| class ExpressionToTypeInfo { |
| |
| private static final String OBJECT_TYPE_NAME = "Object"; |
| |
| final AnalyzeTask at; |
| final CompilationUnitTree cu; |
| final JShell state; |
| final Symtab syms; |
| final Types types; |
| |
| private ExpressionToTypeInfo(AnalyzeTask at, CompilationUnitTree cu, JShell state) { |
| this.at = at; |
| this.cu = cu; |
| this.state = state; |
| this.syms = Symtab.instance(at.context); |
| this.types = Types.instance(at.context); |
| } |
| |
| public static class ExpressionInfo { |
| ExpressionTree tree; |
| String typeName; |
| boolean isNonVoid; |
| } |
| |
| // return mechanism and other general structure from TreePath.getPath() |
| private static class Result extends Error { |
| |
| static final long serialVersionUID = -5942088234594905629L; |
| final TreePath expressionPath; |
| |
| Result(TreePath path) { |
| this.expressionPath = path; |
| } |
| } |
| |
| private static class PathFinder extends TreePathScanner<TreePath, Boolean> { |
| |
| // Optimize out imports etc |
| @Override |
| public TreePath visitCompilationUnit(CompilationUnitTree node, Boolean isTargetContext) { |
| return scan(node.getTypeDecls(), isTargetContext); |
| } |
| |
| // Only care about members |
| @Override |
| public TreePath visitClass(ClassTree node, Boolean isTargetContext) { |
| return scan(node.getMembers(), isTargetContext); |
| } |
| |
| // Only want the doit method where the code is |
| @Override |
| public TreePath visitMethod(MethodTree node, Boolean isTargetContext) { |
| if (Util.isDoIt(node.getName())) { |
| return scan(node.getBody(), true); |
| } else { |
| return null; |
| } |
| } |
| |
| @Override |
| public TreePath visitReturn(ReturnTree node, Boolean isTargetContext) { |
| ExpressionTree tree = node.getExpression(); |
| TreePath tp = new TreePath(getCurrentPath(), tree); |
| if (isTargetContext) { |
| throw new Result(tp); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| private Type pathToType(TreePath tp) { |
| return (Type) at.trees().getTypeMirror(tp); |
| } |
| |
| private Type pathToType(TreePath tp, Tree tree) { |
| if (tree instanceof ConditionalExpressionTree) { |
| // Conditionals always wind up as Object -- this corrects |
| ConditionalExpressionTree cet = (ConditionalExpressionTree) tree; |
| Type tmt = pathToType(new TreePath(tp, cet.getTrueExpression())); |
| Type tmf = pathToType(new TreePath(tp, cet.getFalseExpression())); |
| if (!tmt.isPrimitive() && !tmf.isPrimitive()) { |
| Type lub = types.lub(tmt, tmf); |
| // System.err.printf("cond ? %s : %s -- lub = %s\n", |
| // varTypeName(tmt), varTypeName(tmf), varTypeName(lub)); |
| return lub; |
| } |
| } |
| return pathToType(tp); |
| } |
| |
| /** |
| * Entry method: get expression info |
| * @param code the expression as a string |
| * @param state a JShell instance |
| * @return type information |
| */ |
| public static ExpressionInfo expressionInfo(String code, JShell state) { |
| if (code == null || code.isEmpty()) { |
| return null; |
| } |
| try { |
| OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(code)); |
| AnalyzeTask at = state.taskFactory.new AnalyzeTask(codeWrap); |
| CompilationUnitTree cu = at.firstCuTree(); |
| if (at.hasErrors() || cu == null) { |
| return null; |
| } |
| return new ExpressionToTypeInfo(at, cu, state).typeOfExpression(); |
| } catch (Exception ex) { |
| return null; |
| } |
| } |
| |
| private ExpressionInfo typeOfExpression() { |
| return treeToInfo(findExpressionPath()); |
| } |
| |
| private TreePath findExpressionPath() { |
| try { |
| new PathFinder().scan(new TreePath(cu), false); |
| } catch (Result result) { |
| return result.expressionPath; |
| } |
| return null; |
| } |
| |
| private ExpressionInfo treeToInfo(TreePath tp) { |
| if (tp != null) { |
| Tree tree = tp.getLeaf(); |
| if (tree instanceof ExpressionTree) { |
| ExpressionInfo ei = new ExpressionInfo(); |
| ei.tree = (ExpressionTree) tree; |
| Type type = pathToType(tp, tree); |
| if (type != null) { |
| switch (type.getKind()) { |
| case VOID: |
| case NONE: |
| case ERROR: |
| case OTHER: |
| break; |
| case NULL: |
| ei.isNonVoid = true; |
| ei.typeName = OBJECT_TYPE_NAME; |
| break; |
| default: { |
| ei.isNonVoid = true; |
| ei.typeName = varTypeName(type); |
| if (ei.typeName == null) { |
| ei.typeName = OBJECT_TYPE_NAME; |
| } |
| break; |
| } |
| } |
| } |
| return ei; |
| } |
| } |
| return null; |
| } |
| |
| private String varTypeName(Type type) { |
| try { |
| TypePrinter tp = new VarTypePrinter(at.messages(), |
| state.maps::fullClassNameAndPackageToClass, syms, types); |
| return tp.toString(type); |
| } catch (Exception ex) { |
| return null; |
| } |
| } |
| |
| } |