/*
 * 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.
/** * 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
Params:
  • code – the expression as a string
  • state – a JShell instance
Returns:type information
/** * 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; } } }