Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   ***** BEGIN LICENSE BLOCK *****
   * Version: CPL 1.0/GPL 2.0/LGPL 2.1
   *
   * The contents of this file are subject to the Common Public
   * License Version 1.0 (the "License"); you may not use this file
   * except in compliance with the License. You may obtain a copy of
   * the License at http://www.eclipse.org/legal/cpl-v10.html
   *
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
  *
  * Copyright (C) 2007 Charles O Nutter <headius@headius.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the CPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the CPL, the GPL or the LGPL.
  ***** END LICENSE BLOCK *****/
 
 package org.jruby.compiler;
 
 import java.util.Set;

Author(s):
headius
 
 public class ASTInspector {
 
     private static final Logger LOG = LoggerFactory.getLogger("ASTInspector");
 
     private final boolean dump;
     private final String name;
     public ASTInspector() {
          = false;
          = null;
     }
 
     public ASTInspector(String nameboolean dump) {
         this. = name;
         this. = dump;
     }
 
     private static final boolean DEBUG = false;
 
     public static final int BLOCK_ARG = 0x1; // block argument to the method
     public static final int CLOSURE = 0x2; // closure present
     public static final int CLASS = 0x4; // class present
     public static final int METHOD = 0x8; // method table mutations, def, defs, undef, alias
     public static final int EVAL = 0x10; // likely call to eval
     public static final int FRAME_AWARE = 0x20; // makes calls that are aware of the frame
     public static final int FRAME_SELF = 0x40; // makes calls that are aware of the frame's self
     public static final int FRAME_VISIBILITY = 0x80; // makes calls that are aware of the frame's visibility
     public static final int FRAME_BLOCK = 0x100; // makes calls that are aware of the frame's block
     public static final int FRAME_NAME = 0x200; // makes calls that are aware of the frame's name
     public static final int BACKREF = 0x400; // makes calls that set or get backref
     public static final int LASTLINE = 0x800; // makes calls that set or get lastline
     public static final int FRAME_CLASS = 0x1000; // makes calls that are aware of the frame's class
     public static final int OPT_ARGS = 0x2000; // optional arguments to the method
     public static final int REST_ARG = 0x4000; // rest arg to the method
     public static final int SCOPE_AWARE = 0x8000; // makes calls that are aware of the scope
     public static final int ZSUPER = 0x10000; // makes a zero-argument super call
     public static final int CONSTANT = 0x20000; // accesses or sets constants
     public static final int CLASS_VAR = 0x40000; // accesses or sets class variables
     public static final int SUPER = 0x80000; // makes normal super call
     public static final int RETRY = 0x100000; // contains a retry
 
     private static final String[] MODIFIER_NAMES = {
         "BLOCK""CLOSURE""CLASS""METHOD""EVAL""FRAME_AWARE""FRAME_SELF",
         "FRAME_VISIBILITY""FRAME_BLOCK""FRAME_NAME""BACKREF""LASTLINE""FRAME_CLASS",
         "OPT_ARGS""REST_ARG""SCOPE_AWARE""ZSUPER""CONSTANT""CLASS_VAR",
         "SUPER""RETRY"
     };
     
     private int flags;
     
     // pragmas
    private boolean noFrame;
    
    public static final Set<StringFRAME_AWARE_METHODS = Collections.synchronizedSet(new HashSet<String>());
    public static final Set<StringSCOPE_AWARE_METHODS = Collections.synchronizedSet(new HashSet<String>());
    public static void addFrameAwareMethods(String... methods) {
        if (.debug("Adding frame-aware method names: {}", Arrays.toString(methods));
        .addAll(Arrays.asList(methods));
    }
    
    public static void addScopeAwareMethods(String... methods) {
        if (.debug("Adding scope-aware method names: {}", Arrays.toString(methods));
        .addAll(Arrays.asList(methods));
    }
    public static final Set<StringPRAGMAS = Collections.synchronizedSet(new HashSet<String>());
    
    static {
        .add("eval");
        .add("module_eval");
        .add("class_eval");
        .add("instance_eval");
        .add("binding");
        .add("public");
        .add("private");
        .add("protected");
        .add("module_function");
        .add("block_given?");
        .add("iterator?");
        
        
        .add("__NOFRAME__");
    }
    
    public void disable() {
        if (.debug("[ASTInspector] {} DISABLED");
         = 0xFFFFFFFF;
    }
    public CallConfiguration getCallConfig() {
        if (!noFrame() && (hasFrameAwareMethods() || hasClosure() || .)) {
            // We're doing normal framed compilation or the method needs a frame
            if (hasClosure() || hasScopeAwareMethods()) {
                // The method also needs a scope, do both
                return .;
            } else {
                if (hasConstant() || hasMethod() || hasClass() || hasClassVar()) {
                    // The method doesn't need a scope, but has static scope needs; use a dummy scope
                    return .;
                } else {
                    // The method doesn't need a scope or static scope; frame only
                    return .;
                }
            }
        } else {
            if (hasClosure() || hasScopeAwareMethods()) {
                return .;
            } else {
                if (hasConstant() || hasMethod() || hasClass() || hasClassVar()) {
                    return .;
                } else {
                    return .;
                }
            }
        }
    }
    
    
Perform an inspection of a subtree or set of subtrees separate from the parent inspection, to make independent decisions based on that subtree(s).

Parameters:
nodes The child nodes to walk with a new inspector
Returns:
The new inspector resulting from the walk
    public ASTInspector subInspect(Node... nodes) {
        ASTInspector newInspector = new ASTInspector();
        
        for (Node node : nodes) {
            newInspector.inspect(node);
        }
        
        return newInspector;
    }
    
    public boolean getFlag(int modifier) {
        return ( & modifier) != 0;
    }
    public void setFlag(int modifier) {
        if () {
            .debug("[ASTInspector] {}\n\tset flag {}", Integer.toHexString(modifier));
        }
         |= modifier;
    }
    
    public void setFlag(Node nodeint modifier) {
        if () {
            .debug("[ASTInspector] {}\n\tset flag {} because of {} at {}", Integer.toHexString(modifier), node.getNodeType(), node.getPosition());
        }
         |= modifier;
    }
    
    
Integrate the results of a separate inspection into the state of this inspector.

Parameters:
other The other inspector whose state to integrate.
    public void integrate(ASTInspector other) {
         |= other.flags;
    }
    
    public void inspect(Node node) {
        if (.) {
            disable();
            // we still inspect since some nodes change state as a result (JRUBY-6836)
        }
        if (node == nullreturn;
        
        switch (node.getNodeType()) {
        case :
            setFlag(node);
            break;
        case :
            AndNode andNode = (AndNode)node;
            inspect(andNode.getFirstNode());
            inspect(andNode.getSecondNode());
            break;
        case :
            ArgsCatNode argsCatNode = (ArgsCatNode)node;
            inspect(argsCatNode.getFirstNode());
            inspect(argsCatNode.getSecondNode());
            break;
        case :
            ArgsPushNode argsPushNode = (ArgsPushNode)node;
            inspect(argsPushNode.getFirstNode());
            inspect(argsPushNode.getSecondNode());
            break;
        case :
            break;
        case :
        case :
        case :
        case :
        case :
        case :
        case :
            ListNode listNode = (ListNode)node;
            for (int i = 0; i < listNode.size(); i++) {
                inspect(listNode.get(i));
            }
            break;
        case :
            ArgsNode argsNode = (ArgsNode)node;
            if (argsNode.getBlock() != nullsetFlag(node);
            if (argsNode.getOptArgs() != null) {
                setFlag(node);
                inspect(argsNode.getOptArgs());
            }
            if (argsNode.getRestArg() == -2 || argsNode.getRestArg() >= 0) setFlag(node);
            break;
        case :
            AttrAssignNode attrAssignNode = (AttrAssignNode)node;
            inspect(attrAssignNode.getArgsNode());
            inspect(attrAssignNode.getReceiverNode());
            break;
        case :
            setFlag(node);
            break;
        case :
            inspect(((BeginNode)node).getBodyNode());
            break;
        case :
            break;
        case :
            BinaryOperatorNode binaryOperatorNode = (BinaryOperatorNode)node;
            inspect(binaryOperatorNode.getFirstNode());
            inspect(binaryOperatorNode.getSecondNode());
            break;
        case :
            break;
        case :
            BlockPassNode blockPassNode = (BlockPassNode)node;
            inspect(blockPassNode.getArgsNode());
            inspect(blockPassNode.getBodyNode());
            break;
        case :
            inspect(((BreakNode)node).getValueNode());
            break;
        case :
            CallNode callNode = (CallNode)node;
            inspect(callNode.getReceiverNode());
            // check for Proc.new, an especially magic method
            if (callNode.getName() == "new" &&
                    callNode.getReceiverNode() instanceof ConstNode &&
                    ((ConstNode)callNode.getReceiverNode()).getName() == "Proc") {
                // Proc.new needs the caller's block to instantiate a proc
                setFlag(node);
            }
            if (callNode.getArgsNode() == null && callNode.getIterNode() == null) {
                switch (callNode.getReceiverNode().getNodeType()) {
                    // no unary methods on literal numbers, symbols, or strings have frame/scope effects
                    case :
                    case :
                    case :
                    case :
                    case :
                        return;
                }
            }
        case :
            inspect(((IArgumentNode)node).getArgsNode());
            inspect(((BlockAcceptingNode)node).getIterNode());
        case :
            INameNode nameNode = (INameNode)node;
            if (.contains(nameNode.getName())) {
                setFlag(node);
                if (nameNode.getName().indexOf("eval") != -1) {
                    setFlag(node);
                }
            }
            if (.contains(nameNode.getName())) {
                setFlag(node);
            }
            break;
        case :
            CaseNode caseNode = (CaseNode)node;
            inspect(caseNode.getCaseNode());
            for (Node when : caseNode.getCases().childNodes()) inspect(when);
            inspect(caseNode.getElseNode());
            break;
        case :
            setFlag(node);
            ClassNode classNode = (ClassNode)node;
            inspect(classNode.getCPath());
            inspect(classNode.getSuperNode());
            break;
        case :
            setFlag(node);
            break;
        case :
            inspect(((AssignableNode)node).getValueNode());
            setFlag(node);
            break;
        case :
            inspect(((AssignableNode)node).getValueNode());
            setFlag(node);
            break;
        case :
            inspect(((AssignableNode)node).getValueNode());
            setFlag(node);
            break;
        case :
            inspect(((Colon2Node)node).getLeftNode());
            break;
        case :
            break;
        case :
            setFlag(node);
            break;
        case :
        case :
            setFlag(node);
            setFlag(node);
            break;
        case :
            switch (((DefinedNode)node).getExpressionNode().getNodeType()) {
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
                // ok, we have fast paths
                inspect(((DefinedNode)node).getExpressionNode());
                break;
            default:
                // long, slow way causes disabling
                // we still inspect because some nodes may change state (JRUBY-6836)
                inspect(((DefinedNode)node).getExpressionNode());
                disable();
            }
            break;
        case :
            DotNode dotNode = (DotNode)node;
            inspect(dotNode.getBeginNode());
            inspect(dotNode.getEndNode());
            break;
        case :
            inspect(((AssignableNode)node).getValueNode());
            break;
        case :
            break;
        case :
            inspect(((EnsureNode)node).getBodyNode());
            inspect(((EnsureNode)node).getEnsureNode());
            disable();
            break;
        case :
            break;
        case :
            inspect(((EvStrNode)node).getBody());
            break;
        case :
            break;
        case :
            break;
        case :
            inspect(((FlipNode)node).getBeginNode());
            inspect(((FlipNode)node).getEndNode());
            break;
        case :
            break;
        case :
            setFlag(node);
            setFlag(node);
            inspect(((ForNode)node).getIterNode());
            inspect(((ForNode)node).getBodyNode());
            inspect(((ForNode)node).getVarNode());
            break;
        case :
            GlobalAsgnNode globalAsgnNode = (GlobalAsgnNode)node;
            if (globalAsgnNode.getName().equals("$_")) {
                setFlag(node);
            } else if (globalAsgnNode.getName().equals("$~")) {
                setFlag(node);
            }
            inspect(globalAsgnNode.getValueNode());
            break;
        case :
        {
            String name = ((GlobalVarNode)node).getName();
            if (name.equals("$_") || name.equals("$LAST_READ_LINE")) {
                setFlag(node);
            } else if (name.equals("$~") ||
                    name.equals("$`") ||
                    name.equals("$'") ||
                    name.equals("$+") ||
                    name.equals("$LAST_MATCH_INFO") ||
                    name.equals("$PREMATCH") ||
                    name.equals("$POSTMATCH") ||
                    name.equals("$LAST_PAREN_MATCH")) {
                setFlag(node);
            }
            break;
        }
        case :
            HashNode hashNode = (HashNode)node;
            inspect(hashNode.getListNode());
            break;
        case :
            IfNode ifNode = (IfNode)node;
            inspect(ifNode.getCondition());
            inspect(ifNode.getThenBody());
            inspect(ifNode.getElseBody());
            break;
        case :
            inspect(((AssignableNode)node).getValueNode());
            break;
        case :
            break;
        case :
            IScopingNode iscopingNode = (IScopingNode)node;
            inspect(iscopingNode.getCPath());
            break;
        case :
            setFlag(node);
            break;
        case :
            setFlag(node);
            break;
        case :
            LocalAsgnNode localAsgnNode = (LocalAsgnNode)node;
            if (.contains(localAsgnNode.getName())) {
                if (localAsgnNode.getName().equals("__NOFRAME__")) {
                     = localAsgnNode.getValueNode() instanceof TrueNode;
                }
                break;
            }
            inspect(localAsgnNode.getValueNode());
            break;
        case :
            break;
        case :
            inspect(((MatchNode)node).getRegexpNode());
            setFlag(node);
            break;
        case :
            Match2Node match2Node = (Match2Node)node;
            inspect(match2Node.getReceiverNode());
            inspect(match2Node.getValueNode());
            setFlag(node);
            break;
        case :
            Match3Node match3Node = (Match3Node)node;
            inspect(match3Node.getReceiverNode());
            inspect(match3Node.getValueNode());
            setFlag(node);
            break;
        case :
            setFlag(node);
            inspect(((ModuleNode)node).getCPath());
            break;
        case :
            MultipleAsgn19Node multipleAsgn19Node = (MultipleAsgn19Node)node;
            inspect(multipleAsgn19Node.getPre());
            inspect(multipleAsgn19Node.getPost());
            inspect(multipleAsgn19Node.getRest());
            inspect(multipleAsgn19Node.getValueNode());
            break;
        case :
            MultipleAsgnNode multipleAsgnNode = (MultipleAsgnNode)node;
            inspect(multipleAsgnNode.getArgsNode());
            inspect(multipleAsgnNode.getHeadNode());
            inspect(multipleAsgnNode.getValueNode());
            break;
        case :
            inspect(((NewlineNode)node).getNextNode());
            break;
        case :
            inspect(((NextNode)node).getValueNode());
            break;
        case :
            break;
        case :
            inspect(((NotNode)node).getConditionNode());
            break;
        case :
            break;
        case :
            OpAsgnAndNode opAsgnAndNode = (OpAsgnAndNode)node;
            inspect(opAsgnAndNode.getFirstNode());
            inspect(opAsgnAndNode.getSecondNode());
            break;
        case :
            OpAsgnNode opAsgnNode = (OpAsgnNode)node;
            inspect(opAsgnNode.getReceiverNode());
            inspect(opAsgnNode.getValueNode());
            break;
        case :
            switch (((OpAsgnOrNode)node).getFirstNode().getNodeType()) {
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
            case :
                // ok, we have fast paths
                inspect(((OpAsgnOrNode)node).getSecondNode());
                break;
            default:
                // long, slow way causes disabling for defined
                inspect(((OpAsgnOrNode)node).getFirstNode());
                inspect(((OpAsgnOrNode)node).getSecondNode());
                disable();
            }
            break;
        case :
            OpElementAsgnNode opElementAsgnNode = (OpElementAsgnNode)node;
            inspect(opElementAsgnNode.getArgsNode());
            inspect(opElementAsgnNode.getReceiverNode());
            inspect(opElementAsgnNode.getValueNode());
            break;
        case :
            inspect(((OptArgNode)node).getValue());
            break;
        case :
            OrNode orNode = (OrNode)node;
            inspect(orNode.getFirstNode());
            inspect(orNode.getSecondNode());
            break;
        case :
            PostExeNode postExeNode = (PostExeNode)node;
            setFlag(node);
            setFlag(node);
            inspect(postExeNode.getBodyNode());
            inspect(postExeNode.getVarNode());
            break;
        case :
            PreExeNode preExeNode = (PreExeNode)node;
            setFlag(node);
            setFlag(node);
            inspect(preExeNode.getBodyNode());
            inspect(preExeNode.getVarNode());
            break;
        case :
            break;
        case :
            break;
        case :
            inspect(((RootNode)node).getBodyNode());
            if (((RootNode)node).getBodyNode() instanceof BlockNode) {
                BlockNode blockNode = (BlockNode)((RootNode)node).getBodyNode();
                if (blockNode.size() > 500) {
                    // method has more than 500 lines; we'll need to split it
                    // and therefore need to use a heap-based scope
                    setFlag(node);
                }
            }
            break;
        case :
            RescueBodyNode rescueBody = (RescueBodyNode)node;
            inspect(rescueBody.getExceptionNodes());
            inspect(rescueBody.getBodyNode());
            inspect(rescueBody.getOptRescueNode());
            break;
        case :
            RescueNode rescueNode = (RescueNode)node;
            inspect(rescueNode.getBodyNode());
            inspect(rescueNode.getElseNode());
            inspect(rescueNode.getRescueNode());
            disable();
            break;
        case :
            setFlag(node);
            break;
        case :
            inspect(((ReturnNode)node).getValueNode());
            break;
        case :
            setFlag(node);
            setFlag(node);
            SClassNode sclassNode = (SClassNode)node;
            inspect(sclassNode.getReceiverNode());
            break;
        case :
            break;
        case :
            break;
        case :
            inspect(((SplatNode)node).getValue());
            break;
        case :
            break;
        case :
            break;
        case :
            SuperNode superNode = (SuperNode)node;
            inspect(superNode.getArgsNode());
            inspect(superNode.getIterNode());
            setFlag(node);
            break;
        case :
            inspect(((SValueNode)node).getValue());
            break;
        case :
            break;
        case :
            inspect(((ToAryNode)node).getValue());
            break;
        case :
            break;
        case :
            setFlag(node);
            break;
        case :
            UntilNode untilNode = (UntilNode)node;
            ASTInspector untilInspector = subInspect(
                    untilNode.getConditionNode(), untilNode.getBodyNode());
            // a while node could receive non-local flow control from any of these:
            // * a closure within the loop
            // * an eval within the loop
            // * a block-arg-based proc called within the loop
            if (untilInspector.getFlag() || untilInspector.getFlag()) {
                untilNode.containsNonlocalFlow = true;
                
                // we set scope-aware to true to force heap-based locals
                setFlag(node);
            }
            integrate(untilInspector);
            break;
        case :
            break;
        case : {
            inspect(((WhenNode)node).getBodyNode());
            inspect(((WhenNode)node).getExpressionNodes());
            inspect(((WhenNode)node).getNextCase());
            // if any elements are not literals or are regexp, set backref
            Node expr = ((WhenNode)node).getExpressionNodes();
            if (!(expr instanceof ILiteralNode) || expr.getNodeType() == .) {
                setFlag(node);
            }
            break;
        }
        case :
            WhileNode whileNode = (WhileNode)node;
            ASTInspector whileInspector = subInspect(
                    whileNode.getConditionNode(), whileNode.getBodyNode());
            // a while node could receive non-local flow control from any of these:
            // * a closure within the loop
            // * an eval within the loop
            // * a block-arg-based proc called within the loop
            // * any case that disables optimization, like rescues and ensures
            if (whileInspector.getFlag() || whileInspector.getFlag() || getFlag()) {
                whileNode.containsNonlocalFlow = true;
                
                // we set scope-aware to true to force heap-based locals
                setFlag(node);
            }
            integrate(whileInspector);
            break;
        case :
            break;
        case :
            inspect(((YieldNode)node).getArgsNode());
            break;
        case :
            break;
        case :
            break;
        case :
            setFlag(node);
            setFlag(node);
            inspect(((ZSuperNode)node).getIterNode());
            break;
        default:
            // encountered a node we don't recognize, set everything to true to disable optz
            assert false : "All nodes should be accounted for in AST inspector: " + node;
            disable();
        }
    }
    public boolean hasClass() {
        return getFlag();
    }
    public boolean hasClosure() {
        return getFlag();
    }

    
Whether the tree under inspection contains any method-table mutations, including def, defs, undef, and alias.

Returns:
True if there are mutations, false otherwise
    public boolean hasMethod() {
        return getFlag();
    }
    public boolean hasFrameAwareMethods() {
        return getFlag(
                 |  |  |  |  |  |
                 |  |  | );
    }
    public boolean hasScopeAwareMethods() {
        return getFlag( |  | );
    }
    public boolean hasBlockArg() {
        return getFlag();
    }
    public boolean hasOptArgs() {
        return getFlag();
    }
    public boolean hasRestArg() {
        return getFlag();
    }
    
    public boolean hasConstant() {
        return getFlag();
    }
    
    public boolean hasClassVar() {
        return getFlag();
    }
    
    public boolean noFrame() {
        return ;
    }
New to GrepCode? Check out our FAQ X