예제 #1
0
  /**
   * Perform an inspection of a subtree or set of subtrees separate from the parent inspection, to
   * make independent decisions based on that subtree(s).
   *
   * @param nodes The child nodes to walk with a new inspector
   * @return The new inspector resulting from the walk
   */
  public ASTInspector subInspect(Node... nodes) {
    ASTInspector newInspector = new ASTInspector(name, dump);

    for (Node node : nodes) {
      newInspector.inspect(node);
    }

    return newInspector;
  }
예제 #2
0
  public void inspect(Node node) {
    if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
      disable();
      // we still inspect since some nodes change state as a result (JRUBY-6836)
    }

    if (node == null) return;

    switch (node.getNodeType()) {
      case ALIASNODE:
        setFlag(node, METHOD);
        break;
      case ANDNODE:
        AndNode andNode = (AndNode) node;
        inspect(andNode.getFirstNode());
        inspect(andNode.getSecondNode());
        break;
      case ARGSCATNODE:
        ArgsCatNode argsCatNode = (ArgsCatNode) node;
        inspect(argsCatNode.getFirstNode());
        inspect(argsCatNode.getSecondNode());
        break;
      case ARGSPUSHNODE:
        ArgsPushNode argsPushNode = (ArgsPushNode) node;
        inspect(argsPushNode.getFirstNode());
        inspect(argsPushNode.getSecondNode());
        break;
      case ARGUMENTNODE:
        break;
      case ARRAYNODE:
      case BLOCKNODE:
      case DREGEXPNODE:
      case DSTRNODE:
      case DSYMBOLNODE:
      case DXSTRNODE:
      case LISTNODE:
        ListNode listNode = (ListNode) node;
        for (int i = 0; i < listNode.size(); i++) {
          inspect(listNode.get(i));
        }
        break;
      case ARGSNODE:
        ArgsNode argsNode = (ArgsNode) node;
        if (argsNode.getBlock() != null) setFlag(node, BLOCK_ARG);
        if (argsNode.getOptArgs() != null) {
          setFlag(node, OPT_ARGS);
          inspect(argsNode.getOptArgs());
        }
        if (argsNode.getRestArg() == -2 || argsNode.getRestArg() >= 0) setFlag(node, REST_ARG);
        break;
      case ATTRASSIGNNODE:
        AttrAssignNode attrAssignNode = (AttrAssignNode) node;
        inspect(attrAssignNode.getArgsNode());
        inspect(attrAssignNode.getReceiverNode());
        break;
      case BACKREFNODE:
        setFlag(node, BACKREF);
        break;
      case BEGINNODE:
        inspect(((BeginNode) node).getBodyNode());
        break;
      case BIGNUMNODE:
        break;
      case BINARYOPERATORNODE:
        BinaryOperatorNode binaryOperatorNode = (BinaryOperatorNode) node;
        inspect(binaryOperatorNode.getFirstNode());
        inspect(binaryOperatorNode.getSecondNode());
        break;
      case BLOCKARGNODE:
        break;
      case BLOCKPASSNODE:
        BlockPassNode blockPassNode = (BlockPassNode) node;
        inspect(blockPassNode.getArgsNode());
        inspect(blockPassNode.getBodyNode());
        break;
      case BREAKNODE:
        inspect(((BreakNode) node).getValueNode());
        break;
      case CALLNODE:
        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, FRAME_BLOCK);
        }
        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 FIXNUMNODE:
            case FLOATNODE:
            case BIGNUMNODE:
            case STRNODE:
            case SYMBOLNODE:
              return;
          }
        }
      case FCALLNODE:
        inspect(((IArgumentNode) node).getArgsNode());
        inspect(((BlockAcceptingNode) node).getIterNode());
      case VCALLNODE:
        INameNode nameNode = (INameNode) node;
        if (FRAME_AWARE_METHODS.contains(nameNode.getName())) {
          setFlag(node, FRAME_AWARE);
          if (nameNode.getName().indexOf("eval") != -1) {
            setFlag(node, EVAL);
          }
        }
        if (SCOPE_AWARE_METHODS.contains(nameNode.getName())) {
          setFlag(node, SCOPE_AWARE);
        }
        break;
      case CASENODE:
        CaseNode caseNode = (CaseNode) node;
        inspect(caseNode.getCaseNode());
        if (caseNode.getCases().size() > Options.COMPILE_OUTLINE_CASECOUNT.load()) {
          // if more than N cases, disable; we'll compile them as separate bodies
          // see BaseBodyCompiler#compiledSequencedConditional and ASTCompiler#compileCase
          disable();
          return;
        } else {
          for (Node when : caseNode.getCases().childNodes()) {
            inspect(when);
          }
          inspect(caseNode.getElseNode());
        }
        break;
      case CLASSNODE:
        setFlag(node, CLASS);
        ClassNode classNode = (ClassNode) node;
        inspect(classNode.getCPath());
        inspect(classNode.getSuperNode());
        break;
      case CLASSVARNODE:
        setFlag(node, CLASS_VAR);
        break;
      case CONSTDECLNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CONSTANT);
        break;
      case CLASSVARASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CLASS_VAR);
        break;
      case CLASSVARDECLNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CLASS_VAR);
        break;
      case COLON2NODE:
        inspect(((Colon2Node) node).getLeftNode());
        break;
      case COLON3NODE:
        break;
      case CONSTNODE:
        setFlag(node, CONSTANT);
        break;
      case DEFNNODE:
      case DEFSNODE:
        setFlag(node, METHOD);
        setFlag(node, FRAME_VISIBILITY);
        setFlag(node, SCOPE_AWARE);
        break;
      case DEFINEDNODE:
        switch (((DefinedNode) node).getExpressionNode().getNodeType()) {
          case CLASSVARASGNNODE:
          case CLASSVARDECLNODE:
          case CONSTDECLNODE:
          case DASGNNODE:
          case GLOBALASGNNODE:
          case LOCALASGNNODE:
          case MULTIPLEASGNNODE:
          case OPASGNNODE:
          case OPELEMENTASGNNODE:
          case DVARNODE:
          case FALSENODE:
          case TRUENODE:
          case LOCALVARNODE:
          case INSTVARNODE:
          case BACKREFNODE:
          case SELFNODE:
          case VCALLNODE:
          case YIELDNODE:
          case GLOBALVARNODE:
          case CONSTNODE:
          case FCALLNODE:
          case CLASSVARNODE:
            // 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 = (DotNode) node;
        inspect(dotNode.getBeginNode());
        inspect(dotNode.getEndNode());
        break;
      case DASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        break;
      case DVARNODE:
        break;
      case ENSURENODE:
        inspect(((EnsureNode) node).getBodyNode());
        inspect(((EnsureNode) node).getEnsureNode());
        disable();
        break;
      case ENCODINGNODE:
        break;
      case EVSTRNODE:
        inspect(((EvStrNode) node).getBody());
        break;
      case FALSENODE:
        break;
      case FIXNUMNODE:
        break;
      case FLIPNODE:
        inspect(((FlipNode) node).getBeginNode());
        inspect(((FlipNode) node).getEndNode());
        break;
      case FLOATNODE:
        break;
      case FORNODE:
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(((ForNode) node).getIterNode());
        inspect(((ForNode) node).getBodyNode());
        inspect(((ForNode) node).getVarNode());
        break;
      case GLOBALASGNNODE:
        GlobalAsgnNode globalAsgnNode = (GlobalAsgnNode) node;
        if (globalAsgnNode.getName().equals("$_")) {
          setFlag(node, LASTLINE);
        } else if (globalAsgnNode.getName().equals("$~")) {
          setFlag(node, BACKREF);
        }
        inspect(globalAsgnNode.getValueNode());
        break;
      case GLOBALVARNODE:
        {
          String name = ((GlobalVarNode) node).getName();
          if (name.equals("$_") || name.equals("$LAST_READ_LINE")) {
            setFlag(node, LASTLINE);
          } 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, BACKREF);
          }
          break;
        }
      case HASHNODE:
        HashNode hashNode = (HashNode) node;
        inspect(hashNode.getListNode());
        break;
      case IFNODE:
        IfNode ifNode = (IfNode) node;
        inspect(ifNode.getCondition());
        inspect(ifNode.getThenBody());
        inspect(ifNode.getElseBody());
        break;
      case INSTASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        break;
      case INSTVARNODE:
        break;
      case ISCOPINGNODE:
        IScopingNode iscopingNode = (IScopingNode) node;
        inspect(iscopingNode.getCPath());
        break;
      case ITERNODE:
        setFlag(node, CLOSURE);
        break;
      case LAMBDANODE:
        setFlag(node, CLOSURE);
        break;
      case LOCALASGNNODE:
        LocalAsgnNode localAsgnNode = (LocalAsgnNode) node;
        if (PRAGMAS.contains(localAsgnNode.getName())) {
          if (localAsgnNode.getName().equals("__NOFRAME__")) {
            noFrame = localAsgnNode.getValueNode() instanceof TrueNode;
          }
          break;
        }
        inspect(localAsgnNode.getValueNode());
        break;
      case LOCALVARNODE:
        break;
      case MATCHNODE:
        inspect(((MatchNode) node).getRegexpNode());
        setFlag(node, BACKREF);
        break;
      case MATCH2NODE:
        Match2Node match2Node = (Match2Node) node;
        inspect(match2Node.getReceiverNode());
        inspect(match2Node.getValueNode());
        setFlag(node, BACKREF);
        if (match2Node instanceof Match2CaptureNode) {
          // additionally need scope, to set local vars
          // FIXME: this can be done without heap scope
          setFlag(node, SCOPE_AWARE);
        }
        break;
      case MATCH3NODE:
        Match3Node match3Node = (Match3Node) node;
        inspect(match3Node.getReceiverNode());
        inspect(match3Node.getValueNode());
        setFlag(node, BACKREF);
        break;
      case MODULENODE:
        setFlag(node, CLASS);
        inspect(((ModuleNode) node).getCPath());
        break;
      case MULTIPLEASGN19NODE:
        MultipleAsgn19Node multipleAsgn19Node = (MultipleAsgn19Node) node;
        inspect(multipleAsgn19Node.getPre());
        inspect(multipleAsgn19Node.getPost());
        inspect(multipleAsgn19Node.getRest());
        inspect(multipleAsgn19Node.getValueNode());
        break;
      case MULTIPLEASGNNODE:
        MultipleAsgnNode multipleAsgnNode = (MultipleAsgnNode) node;
        inspect(multipleAsgnNode.getArgsNode());
        inspect(multipleAsgnNode.getHeadNode());
        inspect(multipleAsgnNode.getValueNode());
        break;
      case NEWLINENODE:
        inspect(((NewlineNode) node).getNextNode());
        break;
      case NEXTNODE:
        inspect(((NextNode) node).getValueNode());
        break;
      case NILNODE:
        break;
      case NOTNODE:
        inspect(((NotNode) node).getConditionNode());
        break;
      case NTHREFNODE:
        break;
      case OPASGNANDNODE:
        OpAsgnAndNode opAsgnAndNode = (OpAsgnAndNode) node;
        inspect(opAsgnAndNode.getFirstNode());
        inspect(opAsgnAndNode.getSecondNode());
        break;
      case OPASGNNODE:
        OpAsgnNode opAsgnNode = (OpAsgnNode) node;
        inspect(opAsgnNode.getReceiverNode());
        inspect(opAsgnNode.getValueNode());
        break;
      case OPASGNORNODE:
        switch (((OpAsgnOrNode) node).getFirstNode().getNodeType()) {
          case CLASSVARASGNNODE:
          case CLASSVARDECLNODE:
          case CONSTDECLNODE:
          case DASGNNODE:
          case GLOBALASGNNODE:
          case LOCALASGNNODE:
          case MULTIPLEASGNNODE:
          case OPASGNNODE:
          case OPELEMENTASGNNODE:
          case DVARNODE:
          case FALSENODE:
          case TRUENODE:
          case LOCALVARNODE:
          case INSTVARNODE:
          case BACKREFNODE:
          case SELFNODE:
          case VCALLNODE:
          case YIELDNODE:
          case GLOBALVARNODE:
          case CONSTNODE:
          case FCALLNODE:
          case CLASSVARNODE:
            // 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 = (OpElementAsgnNode) node;
        inspect(opElementAsgnNode.getArgsNode());
        inspect(opElementAsgnNode.getReceiverNode());
        inspect(opElementAsgnNode.getValueNode());
        break;
      case OPTARGNODE:
        inspect(((OptArgNode) node).getValue());
        break;
      case ORNODE:
        OrNode orNode = (OrNode) node;
        inspect(orNode.getFirstNode());
        inspect(orNode.getSecondNode());
        break;
      case POSTEXENODE:
        PostExeNode postExeNode = (PostExeNode) node;
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(postExeNode.getBodyNode());
        inspect(postExeNode.getVarNode());
        break;
      case PREEXENODE:
        PreExeNode preExeNode = (PreExeNode) node;
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(preExeNode.getBodyNode());
        inspect(preExeNode.getVarNode());
        break;
      case REDONODE:
        break;
      case REGEXPNODE:
        break;
      case ROOTNODE:
        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, SCOPE_AWARE);
          }
        }
        break;
      case RESCUEBODYNODE:
        RescueBodyNode rescueBody = (RescueBodyNode) node;
        inspect(rescueBody.getExceptionNodes());
        inspect(rescueBody.getBodyNode());
        inspect(rescueBody.getOptRescueNode());
        break;
      case RESCUENODE:
        RescueNode rescueNode = (RescueNode) node;
        inspect(rescueNode.getBodyNode());
        inspect(rescueNode.getElseNode());
        inspect(rescueNode.getRescueNode());
        disable();
        break;
      case RETRYNODE:
        setFlag(node, RETRY);
        break;
      case RETURNNODE:
        inspect(((ReturnNode) node).getValueNode());
        break;
      case SCLASSNODE:
        setFlag(node, CLASS);
        setFlag(node, FRAME_AWARE);
        SClassNode sclassNode = (SClassNode) node;
        inspect(sclassNode.getReceiverNode());
        break;
      case SCOPENODE:
        break;
      case SELFNODE:
        break;
      case SPLATNODE:
        inspect(((SplatNode) node).getValue());
        break;
      case STARNODE:
        break;
      case STRNODE:
        break;
      case SUPERNODE:
        SuperNode superNode = (SuperNode) node;
        inspect(superNode.getArgsNode());
        inspect(superNode.getIterNode());
        setFlag(node, SUPER);
        break;
      case SVALUENODE:
        inspect(((SValueNode) node).getValue());
        break;
      case SYMBOLNODE:
        break;
      case TOARYNODE:
        inspect(((ToAryNode) node).getValue());
        break;
      case TRUENODE:
        break;
      case UNDEFNODE:
        setFlag(node, METHOD);
        break;
      case UNTILNODE:
        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(CLOSURE) || untilInspector.getFlag(EVAL)) {
          untilNode.containsNonlocalFlow = true;

          // we set scope-aware to true to force heap-based locals
          setFlag(node, SCOPE_AWARE);
        }
        integrate(untilInspector);
        break;
      case VALIASNODE:
        break;
      case WHENNODE:
        {
          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() == NodeType.REGEXPNODE) {
            setFlag(node, BACKREF);
          }
          break;
        }
      case WHILENODE:
        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(CLOSURE) || whileInspector.getFlag(EVAL) || getFlag(BLOCK_ARG)) {
          whileNode.containsNonlocalFlow = true;

          // we set scope-aware to true to force heap-based locals
          setFlag(node, SCOPE_AWARE);
        }
        integrate(whileInspector);
        break;
      case XSTRNODE:
        break;
      case YIELDNODE:
        inspect(((YieldNode) node).getArgsNode());
        break;
      case ZARRAYNODE:
        break;
      case ZEROARGNODE:
        break;
      case ZSUPERNODE:
        setFlag(node, SCOPE_AWARE);
        setFlag(node, ZSUPER);
        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();
    }
  }