duke@1: /* xdono@117: * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.tree; duke@1: duke@1: import com.sun.source.tree.Tree; duke@1: import com.sun.tools.javac.comp.AttrContext; duke@1: import com.sun.tools.javac.comp.Env; duke@1: import java.util.Map; duke@1: import com.sun.tools.javac.util.*; duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: import com.sun.tools.javac.code.*; duke@1: import com.sun.tools.javac.tree.JCTree.*; duke@1: duke@1: import static com.sun.tools.javac.code.Flags.*; duke@1: duke@1: /** Utility class containing inspector methods for trees. duke@1: * duke@1: *

This is NOT part of any API supported by Sun Microsystems. If duke@1: * you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: public class TreeInfo { duke@1: protected static final Context.Key treeInfoKey = duke@1: new Context.Key(); duke@1: duke@1: public static TreeInfo instance(Context context) { duke@1: TreeInfo instance = context.get(treeInfoKey); duke@1: if (instance == null) duke@1: instance = new TreeInfo(context); duke@1: return instance; duke@1: } duke@1: duke@1: /** The names of all operators. duke@1: */ duke@1: private Name[] opname = new Name[JCTree.MOD - JCTree.POS + 1]; duke@1: duke@1: private TreeInfo(Context context) { duke@1: context.put(treeInfoKey, this); duke@1: jjg@113: Names names = Names.instance(context); duke@1: opname[JCTree.POS - JCTree.POS] = names.fromString("+"); duke@1: opname[JCTree.NEG - JCTree.POS] = names.hyphen; duke@1: opname[JCTree.NOT - JCTree.POS] = names.fromString("!"); duke@1: opname[JCTree.COMPL - JCTree.POS] = names.fromString("~"); duke@1: opname[JCTree.PREINC - JCTree.POS] = names.fromString("++"); duke@1: opname[JCTree.PREDEC - JCTree.POS] = names.fromString("--"); duke@1: opname[JCTree.POSTINC - JCTree.POS] = names.fromString("++"); duke@1: opname[JCTree.POSTDEC - JCTree.POS] = names.fromString("--"); duke@1: opname[JCTree.NULLCHK - JCTree.POS] = names.fromString("<*nullchk*>"); duke@1: opname[JCTree.OR - JCTree.POS] = names.fromString("||"); duke@1: opname[JCTree.AND - JCTree.POS] = names.fromString("&&"); duke@1: opname[JCTree.EQ - JCTree.POS] = names.fromString("=="); duke@1: opname[JCTree.NE - JCTree.POS] = names.fromString("!="); duke@1: opname[JCTree.LT - JCTree.POS] = names.fromString("<"); duke@1: opname[JCTree.GT - JCTree.POS] = names.fromString(">"); duke@1: opname[JCTree.LE - JCTree.POS] = names.fromString("<="); duke@1: opname[JCTree.GE - JCTree.POS] = names.fromString(">="); duke@1: opname[JCTree.BITOR - JCTree.POS] = names.fromString("|"); duke@1: opname[JCTree.BITXOR - JCTree.POS] = names.fromString("^"); duke@1: opname[JCTree.BITAND - JCTree.POS] = names.fromString("&"); duke@1: opname[JCTree.SL - JCTree.POS] = names.fromString("<<"); duke@1: opname[JCTree.SR - JCTree.POS] = names.fromString(">>"); duke@1: opname[JCTree.USR - JCTree.POS] = names.fromString(">>>"); duke@1: opname[JCTree.PLUS - JCTree.POS] = names.fromString("+"); duke@1: opname[JCTree.MINUS - JCTree.POS] = names.hyphen; duke@1: opname[JCTree.MUL - JCTree.POS] = names.asterisk; duke@1: opname[JCTree.DIV - JCTree.POS] = names.slash; duke@1: opname[JCTree.MOD - JCTree.POS] = names.fromString("%"); duke@1: } duke@1: duke@1: duke@1: /** Return name of operator with given tree tag. duke@1: */ duke@1: public Name operatorName(int tag) { duke@1: return opname[tag - JCTree.POS]; duke@1: } duke@1: duke@1: /** Is tree a constructor declaration? duke@1: */ duke@1: public static boolean isConstructor(JCTree tree) { duke@1: if (tree.getTag() == JCTree.METHODDEF) { duke@1: Name name = ((JCMethodDecl) tree).name; jjg@113: return name == name.table.names.init; duke@1: } else { duke@1: return false; duke@1: } duke@1: } duke@1: duke@1: /** Is there a constructor declaration in the given list of trees? duke@1: */ duke@1: public static boolean hasConstructors(List trees) { duke@1: for (List l = trees; l.nonEmpty(); l = l.tail) duke@1: if (isConstructor(l.head)) return true; duke@1: return false; duke@1: } duke@1: duke@1: /** Is statement an initializer for a synthetic field? duke@1: */ duke@1: public static boolean isSyntheticInit(JCTree stat) { duke@1: if (stat.getTag() == JCTree.EXEC) { duke@1: JCExpressionStatement exec = (JCExpressionStatement)stat; duke@1: if (exec.expr.getTag() == JCTree.ASSIGN) { duke@1: JCAssign assign = (JCAssign)exec.expr; duke@1: if (assign.lhs.getTag() == JCTree.SELECT) { duke@1: JCFieldAccess select = (JCFieldAccess)assign.lhs; duke@1: if (select.sym != null && duke@1: (select.sym.flags() & SYNTHETIC) != 0) { duke@1: Name selected = name(select.selected); jjg@113: if (selected != null && selected == selected.table.names._this) duke@1: return true; duke@1: } duke@1: } duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** If the expression is a method call, return the method name, null duke@1: * otherwise. */ duke@1: public static Name calledMethodName(JCTree tree) { duke@1: if (tree.getTag() == JCTree.EXEC) { duke@1: JCExpressionStatement exec = (JCExpressionStatement)tree; duke@1: if (exec.expr.getTag() == JCTree.APPLY) { duke@1: Name mname = TreeInfo.name(((JCMethodInvocation) exec.expr).meth); duke@1: return mname; duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: /** Is this a call to this or super? duke@1: */ duke@1: public static boolean isSelfCall(JCTree tree) { duke@1: Name name = calledMethodName(tree); duke@1: if (name != null) { jjg@113: Names names = name.table.names; duke@1: return name==names._this || name==names._super; duke@1: } else { duke@1: return false; duke@1: } duke@1: } duke@1: duke@1: /** Is this a call to super? duke@1: */ duke@1: public static boolean isSuperCall(JCTree tree) { duke@1: Name name = calledMethodName(tree); duke@1: if (name != null) { jjg@113: Names names = name.table.names; duke@1: return name==names._super; duke@1: } else { duke@1: return false; duke@1: } duke@1: } duke@1: duke@1: /** Is this a constructor whose first (non-synthetic) statement is not duke@1: * of the form this(...)? duke@1: */ duke@1: public static boolean isInitialConstructor(JCTree tree) { duke@1: JCMethodInvocation app = firstConstructorCall(tree); duke@1: if (app == null) return false; duke@1: Name meth = name(app.meth); jjg@113: return meth == null || meth != meth.table.names._this; duke@1: } duke@1: duke@1: /** Return the first call in a constructor definition. */ duke@1: public static JCMethodInvocation firstConstructorCall(JCTree tree) { duke@1: if (tree.getTag() != JCTree.METHODDEF) return null; duke@1: JCMethodDecl md = (JCMethodDecl) tree; jjg@113: Names names = md.name.table.names; duke@1: if (md.name != names.init) return null; duke@1: if (md.body == null) return null; duke@1: List stats = md.body.stats; duke@1: // Synthetic initializations can appear before the super call. duke@1: while (stats.nonEmpty() && isSyntheticInit(stats.head)) duke@1: stats = stats.tail; duke@1: if (stats.isEmpty()) return null; duke@1: if (stats.head.getTag() != JCTree.EXEC) return null; duke@1: JCExpressionStatement exec = (JCExpressionStatement) stats.head; duke@1: if (exec.expr.getTag() != JCTree.APPLY) return null; duke@1: return (JCMethodInvocation)exec.expr; duke@1: } duke@1: mcimadamore@537: /** Return true if a tree represents a diamond new expr. */ mcimadamore@537: public static boolean isDiamond(JCTree tree) { mcimadamore@537: switch(tree.getTag()) { mcimadamore@537: case JCTree.TYPEAPPLY: return ((JCTypeApply)tree).getTypeArguments().isEmpty(); mcimadamore@537: case JCTree.NEWCLASS: return isDiamond(((JCNewClass)tree).clazz); mcimadamore@537: default: return false; mcimadamore@537: } mcimadamore@537: } mcimadamore@537: duke@1: /** Return true if a tree represents the null literal. */ duke@1: public static boolean isNull(JCTree tree) { duke@1: if (tree.getTag() != JCTree.LITERAL) duke@1: return false; duke@1: JCLiteral lit = (JCLiteral) tree; duke@1: return (lit.typetag == TypeTags.BOT); duke@1: } duke@1: duke@1: /** The position of the first statement in a block, or the position of duke@1: * the block itself if it is empty. duke@1: */ duke@1: public static int firstStatPos(JCTree tree) { duke@1: if (tree.getTag() == JCTree.BLOCK && ((JCBlock) tree).stats.nonEmpty()) duke@1: return ((JCBlock) tree).stats.head.pos; duke@1: else duke@1: return tree.pos; duke@1: } duke@1: duke@1: /** The end position of given tree, if it is a block with duke@1: * defined endpos. duke@1: */ duke@1: public static int endPos(JCTree tree) { duke@1: if (tree.getTag() == JCTree.BLOCK && ((JCBlock) tree).endpos != Position.NOPOS) duke@1: return ((JCBlock) tree).endpos; duke@1: else if (tree.getTag() == JCTree.SYNCHRONIZED) duke@1: return endPos(((JCSynchronized) tree).body); duke@1: else if (tree.getTag() == JCTree.TRY) { duke@1: JCTry t = (JCTry) tree; duke@1: return endPos((t.finalizer != null) duke@1: ? t.finalizer duke@1: : t.catchers.last().body); duke@1: } else duke@1: return tree.pos; duke@1: } duke@1: duke@1: duke@1: /** Get the start position for a tree node. The start position is duke@1: * defined to be the position of the first character of the first duke@1: * token of the node's source text. duke@1: * @param tree The tree node duke@1: */ duke@1: public static int getStartPos(JCTree tree) { duke@1: if (tree == null) duke@1: return Position.NOPOS; duke@1: duke@1: switch(tree.getTag()) { duke@1: case(JCTree.APPLY): duke@1: return getStartPos(((JCMethodInvocation) tree).meth); duke@1: case(JCTree.ASSIGN): duke@1: return getStartPos(((JCAssign) tree).lhs); duke@1: case(JCTree.BITOR_ASG): case(JCTree.BITXOR_ASG): case(JCTree.BITAND_ASG): duke@1: case(JCTree.SL_ASG): case(JCTree.SR_ASG): case(JCTree.USR_ASG): duke@1: case(JCTree.PLUS_ASG): case(JCTree.MINUS_ASG): case(JCTree.MUL_ASG): duke@1: case(JCTree.DIV_ASG): case(JCTree.MOD_ASG): duke@1: return getStartPos(((JCAssignOp) tree).lhs); duke@1: case(JCTree.OR): case(JCTree.AND): case(JCTree.BITOR): duke@1: case(JCTree.BITXOR): case(JCTree.BITAND): case(JCTree.EQ): duke@1: case(JCTree.NE): case(JCTree.LT): case(JCTree.GT): duke@1: case(JCTree.LE): case(JCTree.GE): case(JCTree.SL): duke@1: case(JCTree.SR): case(JCTree.USR): case(JCTree.PLUS): duke@1: case(JCTree.MINUS): case(JCTree.MUL): case(JCTree.DIV): duke@1: case(JCTree.MOD): duke@1: return getStartPos(((JCBinary) tree).lhs); duke@1: case(JCTree.CLASSDEF): { duke@1: JCClassDecl node = (JCClassDecl)tree; duke@1: if (node.mods.pos != Position.NOPOS) duke@1: return node.mods.pos; duke@1: break; duke@1: } duke@1: case(JCTree.CONDEXPR): duke@1: return getStartPos(((JCConditional) tree).cond); duke@1: case(JCTree.EXEC): duke@1: return getStartPos(((JCExpressionStatement) tree).expr); duke@1: case(JCTree.INDEXED): duke@1: return getStartPos(((JCArrayAccess) tree).indexed); duke@1: case(JCTree.METHODDEF): { duke@1: JCMethodDecl node = (JCMethodDecl)tree; duke@1: if (node.mods.pos != Position.NOPOS) duke@1: return node.mods.pos; duke@1: if (node.typarams.nonEmpty()) // List.nil() used for no typarams duke@1: return getStartPos(node.typarams.head); duke@1: return node.restype == null ? node.pos : getStartPos(node.restype); duke@1: } duke@1: case(JCTree.SELECT): duke@1: return getStartPos(((JCFieldAccess) tree).selected); duke@1: case(JCTree.TYPEAPPLY): duke@1: return getStartPos(((JCTypeApply) tree).clazz); duke@1: case(JCTree.TYPEARRAY): duke@1: return getStartPos(((JCArrayTypeTree) tree).elemtype); duke@1: case(JCTree.TYPETEST): duke@1: return getStartPos(((JCInstanceOf) tree).expr); duke@1: case(JCTree.POSTINC): duke@1: case(JCTree.POSTDEC): duke@1: return getStartPos(((JCUnary) tree).arg); jjg@482: case(JCTree.ANNOTATED_TYPE): { jjg@482: JCAnnotatedType node = (JCAnnotatedType) tree; jjg@482: if (node.annotations.nonEmpty()) jjg@482: return getStartPos(node.annotations.head); jjg@482: return getStartPos(node.underlyingType); jjg@482: } jjg@482: case(JCTree.NEWCLASS): { jjg@482: JCNewClass node = (JCNewClass)tree; jjg@482: if (node.encl != null) jjg@482: return getStartPos(node.encl); jjg@482: break; jjg@482: } duke@1: case(JCTree.VARDEF): { duke@1: JCVariableDecl node = (JCVariableDecl)tree; duke@1: if (node.mods.pos != Position.NOPOS) { duke@1: return node.mods.pos; duke@1: } else { duke@1: return getStartPos(node.vartype); duke@1: } duke@1: } duke@1: case(JCTree.ERRONEOUS): { duke@1: JCErroneous node = (JCErroneous)tree; duke@1: if (node.errs != null && node.errs.nonEmpty()) duke@1: return getStartPos(node.errs.head); duke@1: } duke@1: } duke@1: return tree.pos; duke@1: } duke@1: duke@1: /** The end position of given tree, given a table of end positions generated by the parser duke@1: */ duke@1: public static int getEndPos(JCTree tree, Map endPositions) { duke@1: if (tree == null) duke@1: return Position.NOPOS; duke@1: duke@1: if (endPositions == null) { duke@1: // fall back on limited info in the tree duke@1: return endPos(tree); duke@1: } duke@1: duke@1: Integer mapPos = endPositions.get(tree); duke@1: if (mapPos != null) duke@1: return mapPos; duke@1: duke@1: switch(tree.getTag()) { duke@1: case(JCTree.BITOR_ASG): case(JCTree.BITXOR_ASG): case(JCTree.BITAND_ASG): duke@1: case(JCTree.SL_ASG): case(JCTree.SR_ASG): case(JCTree.USR_ASG): duke@1: case(JCTree.PLUS_ASG): case(JCTree.MINUS_ASG): case(JCTree.MUL_ASG): duke@1: case(JCTree.DIV_ASG): case(JCTree.MOD_ASG): duke@1: return getEndPos(((JCAssignOp) tree).rhs, endPositions); duke@1: case(JCTree.OR): case(JCTree.AND): case(JCTree.BITOR): duke@1: case(JCTree.BITXOR): case(JCTree.BITAND): case(JCTree.EQ): duke@1: case(JCTree.NE): case(JCTree.LT): case(JCTree.GT): duke@1: case(JCTree.LE): case(JCTree.GE): case(JCTree.SL): duke@1: case(JCTree.SR): case(JCTree.USR): case(JCTree.PLUS): duke@1: case(JCTree.MINUS): case(JCTree.MUL): case(JCTree.DIV): duke@1: case(JCTree.MOD): duke@1: return getEndPos(((JCBinary) tree).rhs, endPositions); duke@1: case(JCTree.CASE): duke@1: return getEndPos(((JCCase) tree).stats.last(), endPositions); duke@1: case(JCTree.CATCH): duke@1: return getEndPos(((JCCatch) tree).body, endPositions); duke@1: case(JCTree.CONDEXPR): duke@1: return getEndPos(((JCConditional) tree).falsepart, endPositions); duke@1: case(JCTree.FORLOOP): duke@1: return getEndPos(((JCForLoop) tree).body, endPositions); duke@1: case(JCTree.FOREACHLOOP): duke@1: return getEndPos(((JCEnhancedForLoop) tree).body, endPositions); duke@1: case(JCTree.IF): { duke@1: JCIf node = (JCIf)tree; duke@1: if (node.elsepart == null) { duke@1: return getEndPos(node.thenpart, endPositions); duke@1: } else { duke@1: return getEndPos(node.elsepart, endPositions); duke@1: } duke@1: } duke@1: case(JCTree.LABELLED): duke@1: return getEndPos(((JCLabeledStatement) tree).body, endPositions); duke@1: case(JCTree.MODIFIERS): duke@1: return getEndPos(((JCModifiers) tree).annotations.last(), endPositions); duke@1: case(JCTree.SYNCHRONIZED): duke@1: return getEndPos(((JCSynchronized) tree).body, endPositions); duke@1: case(JCTree.TOPLEVEL): duke@1: return getEndPos(((JCCompilationUnit) tree).defs.last(), endPositions); duke@1: case(JCTree.TRY): { duke@1: JCTry node = (JCTry)tree; duke@1: if (node.finalizer != null) { duke@1: return getEndPos(node.finalizer, endPositions); duke@1: } else if (!node.catchers.isEmpty()) { duke@1: return getEndPos(node.catchers.last(), endPositions); duke@1: } else { duke@1: return getEndPos(node.body, endPositions); duke@1: } duke@1: } duke@1: case(JCTree.WILDCARD): duke@1: return getEndPos(((JCWildcard) tree).inner, endPositions); duke@1: case(JCTree.TYPECAST): duke@1: return getEndPos(((JCTypeCast) tree).expr, endPositions); duke@1: case(JCTree.TYPETEST): duke@1: return getEndPos(((JCInstanceOf) tree).clazz, endPositions); duke@1: case(JCTree.POS): duke@1: case(JCTree.NEG): duke@1: case(JCTree.NOT): duke@1: case(JCTree.COMPL): duke@1: case(JCTree.PREINC): duke@1: case(JCTree.PREDEC): duke@1: return getEndPos(((JCUnary) tree).arg, endPositions); duke@1: case(JCTree.WHILELOOP): duke@1: return getEndPos(((JCWhileLoop) tree).body, endPositions); jjg@482: case(JCTree.ANNOTATED_TYPE): jjg@482: return getEndPos(((JCAnnotatedType) tree).underlyingType, endPositions); duke@1: case(JCTree.ERRONEOUS): { duke@1: JCErroneous node = (JCErroneous)tree; duke@1: if (node.errs != null && node.errs.nonEmpty()) duke@1: return getEndPos(node.errs.last(), endPositions); duke@1: } duke@1: } duke@1: return Position.NOPOS; duke@1: } duke@1: duke@1: duke@1: /** A DiagnosticPosition with the preferred position set to the duke@1: * end position of given tree, if it is a block with duke@1: * defined endpos. duke@1: */ duke@1: public static DiagnosticPosition diagEndPos(final JCTree tree) { duke@1: final int endPos = TreeInfo.endPos(tree); duke@1: return new DiagnosticPosition() { duke@1: public JCTree getTree() { return tree; } duke@1: public int getStartPosition() { return TreeInfo.getStartPos(tree); } duke@1: public int getPreferredPosition() { return endPos; } duke@1: public int getEndPosition(Map endPosTable) { duke@1: return TreeInfo.getEndPos(tree, endPosTable); duke@1: } duke@1: }; duke@1: } duke@1: duke@1: /** The position of the finalizer of given try/synchronized statement. duke@1: */ duke@1: public static int finalizerPos(JCTree tree) { duke@1: if (tree.getTag() == JCTree.TRY) { duke@1: JCTry t = (JCTry) tree; duke@1: assert t.finalizer != null; duke@1: return firstStatPos(t.finalizer); duke@1: } else if (tree.getTag() == JCTree.SYNCHRONIZED) { duke@1: return endPos(((JCSynchronized) tree).body); duke@1: } else { duke@1: throw new AssertionError(); duke@1: } duke@1: } duke@1: duke@1: /** Find the position for reporting an error about a symbol, where duke@1: * that symbol is defined somewhere in the given tree. */ duke@1: public static int positionFor(final Symbol sym, final JCTree tree) { duke@1: JCTree decl = declarationFor(sym, tree); duke@1: return ((decl != null) ? decl : tree).pos; duke@1: } duke@1: duke@1: /** Find the position for reporting an error about a symbol, where duke@1: * that symbol is defined somewhere in the given tree. */ duke@1: public static DiagnosticPosition diagnosticPositionFor(final Symbol sym, final JCTree tree) { duke@1: JCTree decl = declarationFor(sym, tree); duke@1: return ((decl != null) ? decl : tree).pos(); duke@1: } duke@1: duke@1: /** Find the declaration for a symbol, where duke@1: * that symbol is defined somewhere in the given tree. */ duke@1: public static JCTree declarationFor(final Symbol sym, final JCTree tree) { duke@1: class DeclScanner extends TreeScanner { duke@1: JCTree result = null; duke@1: public void scan(JCTree tree) { duke@1: if (tree!=null && result==null) duke@1: tree.accept(this); duke@1: } duke@1: public void visitTopLevel(JCCompilationUnit that) { duke@1: if (that.packge == sym) result = that; duke@1: else super.visitTopLevel(that); duke@1: } duke@1: public void visitClassDef(JCClassDecl that) { duke@1: if (that.sym == sym) result = that; duke@1: else super.visitClassDef(that); duke@1: } duke@1: public void visitMethodDef(JCMethodDecl that) { duke@1: if (that.sym == sym) result = that; duke@1: else super.visitMethodDef(that); duke@1: } duke@1: public void visitVarDef(JCVariableDecl that) { duke@1: if (that.sym == sym) result = that; duke@1: else super.visitVarDef(that); duke@1: } duke@1: } duke@1: DeclScanner s = new DeclScanner(); duke@1: tree.accept(s); duke@1: return s.result; duke@1: } duke@1: duke@1: public static Env scopeFor(JCTree node, JCCompilationUnit unit) { duke@1: return scopeFor(pathFor(node, unit)); duke@1: } duke@1: duke@1: public static Env scopeFor(List path) { duke@1: // TODO: not implemented yet duke@1: throw new UnsupportedOperationException("not implemented yet"); duke@1: } duke@1: duke@1: public static List pathFor(final JCTree node, final JCCompilationUnit unit) { duke@1: class Result extends Error { duke@1: static final long serialVersionUID = -5942088234594905625L; duke@1: List path; duke@1: Result(List path) { duke@1: this.path = path; duke@1: } duke@1: } duke@1: class PathFinder extends TreeScanner { duke@1: List path = List.nil(); duke@1: public void scan(JCTree tree) { duke@1: if (tree != null) { duke@1: path = path.prepend(tree); duke@1: if (tree == node) duke@1: throw new Result(path); duke@1: super.scan(tree); duke@1: path = path.tail; duke@1: } duke@1: } duke@1: } duke@1: try { duke@1: new PathFinder().scan(unit); duke@1: } catch (Result result) { duke@1: return result.path; duke@1: } duke@1: return List.nil(); duke@1: } duke@1: duke@1: /** Return the statement referenced by a label. duke@1: * If the label refers to a loop or switch, return that switch duke@1: * otherwise return the labelled statement itself duke@1: */ duke@1: public static JCTree referencedStatement(JCLabeledStatement tree) { duke@1: JCTree t = tree; duke@1: do t = ((JCLabeledStatement) t).body; duke@1: while (t.getTag() == JCTree.LABELLED); duke@1: switch (t.getTag()) { duke@1: case JCTree.DOLOOP: case JCTree.WHILELOOP: case JCTree.FORLOOP: case JCTree.FOREACHLOOP: case JCTree.SWITCH: duke@1: return t; duke@1: default: duke@1: return tree; duke@1: } duke@1: } duke@1: duke@1: /** Skip parens and return the enclosed expression duke@1: */ duke@1: public static JCExpression skipParens(JCExpression tree) { duke@1: while (tree.getTag() == JCTree.PARENS) { duke@1: tree = ((JCParens) tree).expr; duke@1: } duke@1: return tree; duke@1: } duke@1: duke@1: /** Skip parens and return the enclosed expression duke@1: */ duke@1: public static JCTree skipParens(JCTree tree) { duke@1: if (tree.getTag() == JCTree.PARENS) duke@1: return skipParens((JCParens)tree); duke@1: else duke@1: return tree; duke@1: } duke@1: duke@1: /** Return the types of a list of trees. duke@1: */ duke@1: public static List types(List trees) { duke@1: ListBuffer ts = new ListBuffer(); duke@1: for (List l = trees; l.nonEmpty(); l = l.tail) duke@1: ts.append(l.head.type); duke@1: return ts.toList(); duke@1: } duke@1: duke@1: /** If this tree is an identifier or a field or a parameterized type, duke@1: * return its name, otherwise return null. duke@1: */ duke@1: public static Name name(JCTree tree) { duke@1: switch (tree.getTag()) { duke@1: case JCTree.IDENT: duke@1: return ((JCIdent) tree).name; duke@1: case JCTree.SELECT: duke@1: return ((JCFieldAccess) tree).name; duke@1: case JCTree.TYPEAPPLY: duke@1: return name(((JCTypeApply) tree).clazz); duke@1: default: duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: /** If this tree is a qualified identifier, its return fully qualified name, duke@1: * otherwise return null. duke@1: */ duke@1: public static Name fullName(JCTree tree) { duke@1: tree = skipParens(tree); duke@1: switch (tree.getTag()) { duke@1: case JCTree.IDENT: duke@1: return ((JCIdent) tree).name; duke@1: case JCTree.SELECT: duke@1: Name sname = fullName(((JCFieldAccess) tree).selected); duke@1: return sname == null ? null : sname.append('.', name(tree)); duke@1: default: duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: public static Symbol symbolFor(JCTree node) { duke@1: node = skipParens(node); duke@1: switch (node.getTag()) { duke@1: case JCTree.CLASSDEF: duke@1: return ((JCClassDecl) node).sym; duke@1: case JCTree.METHODDEF: duke@1: return ((JCMethodDecl) node).sym; duke@1: case JCTree.VARDEF: duke@1: return ((JCVariableDecl) node).sym; duke@1: default: duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: /** If this tree is an identifier or a field, return its symbol, duke@1: * otherwise return null. duke@1: */ duke@1: public static Symbol symbol(JCTree tree) { duke@1: tree = skipParens(tree); duke@1: switch (tree.getTag()) { duke@1: case JCTree.IDENT: duke@1: return ((JCIdent) tree).sym; duke@1: case JCTree.SELECT: duke@1: return ((JCFieldAccess) tree).sym; duke@1: case JCTree.TYPEAPPLY: duke@1: return symbol(((JCTypeApply) tree).clazz); duke@1: default: duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: /** Return true if this is a nonstatic selection. */ duke@1: public static boolean nonstaticSelect(JCTree tree) { duke@1: tree = skipParens(tree); duke@1: if (tree.getTag() != JCTree.SELECT) return false; duke@1: JCFieldAccess s = (JCFieldAccess) tree; duke@1: Symbol e = symbol(s.selected); duke@1: return e == null || (e.kind != Kinds.PCK && e.kind != Kinds.TYP); duke@1: } duke@1: duke@1: /** If this tree is an identifier or a field, set its symbol, otherwise skip. duke@1: */ duke@1: public static void setSymbol(JCTree tree, Symbol sym) { duke@1: tree = skipParens(tree); duke@1: switch (tree.getTag()) { duke@1: case JCTree.IDENT: duke@1: ((JCIdent) tree).sym = sym; break; duke@1: case JCTree.SELECT: duke@1: ((JCFieldAccess) tree).sym = sym; break; duke@1: default: duke@1: } duke@1: } duke@1: duke@1: /** If this tree is a declaration or a block, return its flags field, duke@1: * otherwise return 0. duke@1: */ duke@1: public static long flags(JCTree tree) { duke@1: switch (tree.getTag()) { duke@1: case JCTree.VARDEF: duke@1: return ((JCVariableDecl) tree).mods.flags; duke@1: case JCTree.METHODDEF: duke@1: return ((JCMethodDecl) tree).mods.flags; duke@1: case JCTree.CLASSDEF: duke@1: return ((JCClassDecl) tree).mods.flags; duke@1: case JCTree.BLOCK: duke@1: return ((JCBlock) tree).flags; duke@1: default: duke@1: return 0; duke@1: } duke@1: } duke@1: duke@1: /** Return first (smallest) flag in `flags': duke@1: * pre: flags != 0 duke@1: */ duke@1: public static long firstFlag(long flags) { duke@1: int flag = 1; duke@1: while ((flag & StandardFlags) != 0 && (flag & flags) == 0) duke@1: flag = flag << 1; duke@1: return flag; duke@1: } duke@1: duke@1: /** Return flags as a string, separated by " ". duke@1: */ duke@1: public static String flagNames(long flags) { duke@1: return Flags.toString(flags & StandardFlags).trim(); duke@1: } duke@1: duke@1: /** Operator precedences values. duke@1: */ duke@1: public static final int duke@1: notExpression = -1, // not an expression duke@1: noPrec = 0, // no enclosing expression duke@1: assignPrec = 1, duke@1: assignopPrec = 2, duke@1: condPrec = 3, duke@1: orPrec = 4, duke@1: andPrec = 5, duke@1: bitorPrec = 6, duke@1: bitxorPrec = 7, duke@1: bitandPrec = 8, duke@1: eqPrec = 9, duke@1: ordPrec = 10, duke@1: shiftPrec = 11, duke@1: addPrec = 12, duke@1: mulPrec = 13, duke@1: prefixPrec = 14, duke@1: postfixPrec = 15, duke@1: precCount = 16; duke@1: duke@1: duke@1: /** Map operators to their precedence levels. duke@1: */ duke@1: public static int opPrec(int op) { duke@1: switch(op) { duke@1: case JCTree.POS: duke@1: case JCTree.NEG: duke@1: case JCTree.NOT: duke@1: case JCTree.COMPL: duke@1: case JCTree.PREINC: duke@1: case JCTree.PREDEC: return prefixPrec; duke@1: case JCTree.POSTINC: duke@1: case JCTree.POSTDEC: duke@1: case JCTree.NULLCHK: return postfixPrec; duke@1: case JCTree.ASSIGN: return assignPrec; duke@1: case JCTree.BITOR_ASG: duke@1: case JCTree.BITXOR_ASG: duke@1: case JCTree.BITAND_ASG: duke@1: case JCTree.SL_ASG: duke@1: case JCTree.SR_ASG: duke@1: case JCTree.USR_ASG: duke@1: case JCTree.PLUS_ASG: duke@1: case JCTree.MINUS_ASG: duke@1: case JCTree.MUL_ASG: duke@1: case JCTree.DIV_ASG: duke@1: case JCTree.MOD_ASG: return assignopPrec; duke@1: case JCTree.OR: return orPrec; duke@1: case JCTree.AND: return andPrec; duke@1: case JCTree.EQ: duke@1: case JCTree.NE: return eqPrec; duke@1: case JCTree.LT: duke@1: case JCTree.GT: duke@1: case JCTree.LE: duke@1: case JCTree.GE: return ordPrec; duke@1: case JCTree.BITOR: return bitorPrec; duke@1: case JCTree.BITXOR: return bitxorPrec; duke@1: case JCTree.BITAND: return bitandPrec; duke@1: case JCTree.SL: duke@1: case JCTree.SR: duke@1: case JCTree.USR: return shiftPrec; duke@1: case JCTree.PLUS: duke@1: case JCTree.MINUS: return addPrec; duke@1: case JCTree.MUL: duke@1: case JCTree.DIV: duke@1: case JCTree.MOD: return mulPrec; duke@1: case JCTree.TYPETEST: return ordPrec; duke@1: default: throw new AssertionError(); duke@1: } duke@1: } duke@1: duke@1: static Tree.Kind tagToKind(int tag) { duke@1: switch (tag) { duke@1: // Postfix expressions duke@1: case JCTree.POSTINC: // _ ++ duke@1: return Tree.Kind.POSTFIX_INCREMENT; duke@1: case JCTree.POSTDEC: // _ -- duke@1: return Tree.Kind.POSTFIX_DECREMENT; duke@1: duke@1: // Unary operators duke@1: case JCTree.PREINC: // ++ _ duke@1: return Tree.Kind.PREFIX_INCREMENT; duke@1: case JCTree.PREDEC: // -- _ duke@1: return Tree.Kind.PREFIX_DECREMENT; duke@1: case JCTree.POS: // + duke@1: return Tree.Kind.UNARY_PLUS; duke@1: case JCTree.NEG: // - duke@1: return Tree.Kind.UNARY_MINUS; duke@1: case JCTree.COMPL: // ~ duke@1: return Tree.Kind.BITWISE_COMPLEMENT; duke@1: case JCTree.NOT: // ! duke@1: return Tree.Kind.LOGICAL_COMPLEMENT; duke@1: duke@1: // Binary operators duke@1: duke@1: // Multiplicative operators duke@1: case JCTree.MUL: // * duke@1: return Tree.Kind.MULTIPLY; duke@1: case JCTree.DIV: // / duke@1: return Tree.Kind.DIVIDE; duke@1: case JCTree.MOD: // % duke@1: return Tree.Kind.REMAINDER; duke@1: duke@1: // Additive operators duke@1: case JCTree.PLUS: // + duke@1: return Tree.Kind.PLUS; duke@1: case JCTree.MINUS: // - duke@1: return Tree.Kind.MINUS; duke@1: duke@1: // Shift operators duke@1: case JCTree.SL: // << duke@1: return Tree.Kind.LEFT_SHIFT; duke@1: case JCTree.SR: // >> duke@1: return Tree.Kind.RIGHT_SHIFT; duke@1: case JCTree.USR: // >>> duke@1: return Tree.Kind.UNSIGNED_RIGHT_SHIFT; duke@1: duke@1: // Relational operators duke@1: case JCTree.LT: // < duke@1: return Tree.Kind.LESS_THAN; duke@1: case JCTree.GT: // > duke@1: return Tree.Kind.GREATER_THAN; duke@1: case JCTree.LE: // <= duke@1: return Tree.Kind.LESS_THAN_EQUAL; duke@1: case JCTree.GE: // >= duke@1: return Tree.Kind.GREATER_THAN_EQUAL; duke@1: duke@1: // Equality operators duke@1: case JCTree.EQ: // == duke@1: return Tree.Kind.EQUAL_TO; duke@1: case JCTree.NE: // != duke@1: return Tree.Kind.NOT_EQUAL_TO; duke@1: duke@1: // Bitwise and logical operators duke@1: case JCTree.BITAND: // & duke@1: return Tree.Kind.AND; duke@1: case JCTree.BITXOR: // ^ duke@1: return Tree.Kind.XOR; duke@1: case JCTree.BITOR: // | duke@1: return Tree.Kind.OR; duke@1: duke@1: // Conditional operators duke@1: case JCTree.AND: // && duke@1: return Tree.Kind.CONDITIONAL_AND; duke@1: case JCTree.OR: // || duke@1: return Tree.Kind.CONDITIONAL_OR; duke@1: duke@1: // Assignment operators duke@1: case JCTree.MUL_ASG: // *= duke@1: return Tree.Kind.MULTIPLY_ASSIGNMENT; duke@1: case JCTree.DIV_ASG: // /= duke@1: return Tree.Kind.DIVIDE_ASSIGNMENT; duke@1: case JCTree.MOD_ASG: // %= duke@1: return Tree.Kind.REMAINDER_ASSIGNMENT; duke@1: case JCTree.PLUS_ASG: // += duke@1: return Tree.Kind.PLUS_ASSIGNMENT; duke@1: case JCTree.MINUS_ASG: // -= duke@1: return Tree.Kind.MINUS_ASSIGNMENT; duke@1: case JCTree.SL_ASG: // <<= duke@1: return Tree.Kind.LEFT_SHIFT_ASSIGNMENT; duke@1: case JCTree.SR_ASG: // >>= duke@1: return Tree.Kind.RIGHT_SHIFT_ASSIGNMENT; duke@1: case JCTree.USR_ASG: // >>>= duke@1: return Tree.Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT; duke@1: case JCTree.BITAND_ASG: // &= duke@1: return Tree.Kind.AND_ASSIGNMENT; duke@1: case JCTree.BITXOR_ASG: // ^= duke@1: return Tree.Kind.XOR_ASSIGNMENT; duke@1: case JCTree.BITOR_ASG: // |= duke@1: return Tree.Kind.OR_ASSIGNMENT; duke@1: duke@1: // Null check (implementation detail), for example, __.getClass() duke@1: case JCTree.NULLCHK: duke@1: return Tree.Kind.OTHER; duke@1: duke@1: default: duke@1: return null; duke@1: } duke@1: } jjg@308: jjg@308: /** jjg@308: * Returns the underlying type of the tree if it is annotated type, jjg@308: * or the tree itself otherwise jjg@308: */ jjg@308: public static JCExpression typeIn(JCExpression tree) { jjg@308: switch (tree.getTag()) { jjg@308: case JCTree.ANNOTATED_TYPE: jjg@308: return ((JCAnnotatedType)tree).underlyingType; jjg@308: case JCTree.IDENT: /* simple names */ jjg@308: case JCTree.TYPEIDENT: /* primitive name */ jjg@308: case JCTree.SELECT: /* qualified name */ jjg@308: case JCTree.TYPEARRAY: /* array types */ jjg@308: case JCTree.WILDCARD: /* wild cards */ jjg@308: case JCTree.TYPEPARAMETER: /* type parameters */ jjg@308: case JCTree.TYPEAPPLY: /* parameterized types */ jjg@308: return tree; jjg@308: default: jjg@308: throw new AssertionError("Unexpected type tree: " + tree); jjg@308: } jjg@308: } jjg@470: jjg@470: public static JCTree innermostType(JCTree type) { jjg@470: switch (type.getTag()) { jjg@470: case JCTree.TYPEARRAY: jjg@470: return innermostType(((JCArrayTypeTree)type).elemtype); jjg@470: case JCTree.WILDCARD: jjg@470: return innermostType(((JCWildcard)type).inner); jjg@470: case JCTree.ANNOTATED_TYPE: jjg@470: return innermostType(((JCAnnotatedType)type).underlyingType); jjg@470: default: jjg@470: return type; jjg@470: } jjg@470: } duke@1: }