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(); } }