public InterpretedMethod( RubyModule implementationClass, StaticScope staticScope, Node body, String name, ArgsNode argsNode, Visibility visibility, ISourcePosition position) { super(implementationClass, visibility, CallConfiguration.FrameFullScopeFull, name); this.body = body; this.staticScope = staticScope; this.argsNode = argsNode; this.position = position; // we get these out ahead of time this.file = position.getFile(); this.line = position.getLine(); ASTInspector inspector = new ASTInspector(); inspector.inspect(body); inspector.inspect(argsNode); // This optimization is temporarily disabled because of the complications // arising from moving backref/lastline into scope and not being able // to accurately detect that situation. // if (inspector.hasClosure() || inspector.hasScopeAwareMethods() || // staticScope.getNumberOfVariables() != 0) { // // must have scope needsScope = true; // } else { // needsScope = false; // } assert argsNode != null; }
/** * 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; }
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(); } }
public CompiledBlockCallback19 compileBlock19( ThreadContext context, StandardASMCompiler asmCompiler, final IterNode iterNode) { final ASTCompiler19 astCompiler = new ASTCompiler19(); final StaticScope scope = iterNode.getScope(); asmCompiler.startScript(scope); final ArgsNode argsNode = (ArgsNode) iterNode.getVarNode(); // create the closure class and instantiate it final CompilerCallback closureBody = new CompilerCallback() { public void call(BodyCompiler context) { if (iterNode.getBodyNode() != null) { astCompiler.compile(iterNode.getBodyNode(), context, true); } else { context.loadNil(); } } }; // create the closure class and instantiate it final CompilerCallback closureArgs = new CompilerCallback() { public void call(BodyCompiler context) { // FIXME: This is temporary since the variable compilers assume we want // args already on stack for assignment. We just pop and continue with // 1.9 args logic. context.consumeCurrentValue(); // args value context.consumeCurrentValue(); // passed block if (iterNode.getVarNode() != null) { if (iterNode instanceof LambdaNode) { final int required = argsNode.getRequiredArgsCount(); final int opt = argsNode.getOptionalArgsCount(); final int rest = argsNode.getRestArg(); context.getVariableCompiler().checkMethodArity(required, opt, rest); astCompiler.compileMethodArgs(argsNode, context, true); } else { astCompiler.compileMethodArgs(argsNode, context, true); } } } }; ASTInspector inspector = new ASTInspector(); inspector.inspect(iterNode.getBodyNode()); inspector.inspect(iterNode.getVarNode()); NodeType argsNodeId = BlockBody.getArgumentTypeWackyHack(iterNode); int scopeIndex = asmCompiler.getCacheCompiler().reserveStaticScope(); ChildScopedBodyCompiler closureCompiler = new ChildScopedBodyCompiler19( asmCompiler, "__file__", asmCompiler.getClassname(), inspector, scope, scopeIndex); closureCompiler.beginMethod(argsNodeId == null ? null : closureArgs, scope); closureBody.call(closureCompiler); closureCompiler.endBody(); // __file__ method to call static version SkinnyMethodAdapter method = new SkinnyMethodAdapter( asmCompiler.getClassVisitor(), ACC_PUBLIC, "__file__", getMethodSignature(4), null, null); method.start(); // invoke static __file__ method.aload(THIS); method.aload(THREADCONTEXT_INDEX); method.aload(SELF_INDEX); method.aload(ARGS_INDEX); method.aload(ARGS_INDEX + 1); // block method.invokestatic( asmCompiler.getClassname(), "__file__", asmCompiler.getStaticMethodSignature(asmCompiler.getClassname(), 4)); method.areturn(); method.end(); asmCompiler.endScript(false, false); byte[] bytes = asmCompiler.getClassByteArray(); Class blockClass = new JRubyClassLoader(context.runtime.getJRubyClassLoader()) .defineClass(asmCompiler.getClassname(), bytes); try { final AbstractScript script = (AbstractScript) blockClass.newInstance(); script.setRootScope(scope); return new CompiledBlockCallback19() { @Override public IRubyObject call( ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) { return script.__file__(context, self, args, block); } @Override public String getFile() { return iterNode.getPosition().getFile(); } @Override public int getLine() { return iterNode.getPosition().getLine(); } }; } catch (Exception e) { throw new RuntimeException(e); } }
public CompiledBlockCallback compileBlock( ThreadContext context, StandardASMCompiler asmCompiler, final IterNode iterNode) { final ASTCompiler astCompiler = new ASTCompiler(); final StaticScope scope = iterNode.getScope(); asmCompiler.startScript(scope); // create the closure class and instantiate it final CompilerCallback closureBody = new CompilerCallback() { public void call(BodyCompiler context) { if (iterNode.getBodyNode() != null) { astCompiler.compile(iterNode.getBodyNode(), context, true); } else { context.loadNil(); } } }; // create the closure class and instantiate it final CompilerCallback closureArgs = new CompilerCallback() { public void call(BodyCompiler context) { if (iterNode.getVarNode() != null) { astCompiler.compileAssignment(iterNode.getVarNode(), context); } else { context.consumeCurrentValue(); } if (iterNode.getBlockVarNode() != null) { astCompiler.compileAssignment(iterNode.getBlockVarNode(), context); } else { context.consumeCurrentValue(); } } }; ASTInspector inspector = new ASTInspector(); inspector.inspect(iterNode.getBodyNode()); inspector.inspect(iterNode.getVarNode()); int scopeIndex = asmCompiler.getCacheCompiler().reserveStaticScope(); ChildScopedBodyCompiler closureCompiler = new ChildScopedBodyCompiler( asmCompiler, "__file__", asmCompiler.getClassname(), inspector, scope, scopeIndex); closureCompiler.beginMethod(closureArgs, scope); closureBody.call(closureCompiler); closureCompiler.endBody(); // __file__ method with [] args; no-op SkinnyMethodAdapter method = new SkinnyMethodAdapter( asmCompiler.getClassVisitor(), ACC_PUBLIC, "__file__", getMethodSignature(4), null, null); method.start(); method.aload(SELF_INDEX); method.areturn(); method.end(); // __file__ method to call static version method = new SkinnyMethodAdapter( asmCompiler.getClassVisitor(), ACC_PUBLIC, "__file__", getMethodSignature(1), null, null); method.start(); // invoke static __file__ method.aload(THIS); method.aload(THREADCONTEXT_INDEX); method.aload(SELF_INDEX); method.aload(ARGS_INDEX); method.aload(ARGS_INDEX + 1); // block method.invokestatic( asmCompiler.getClassname(), "__file__", getStaticMethodSignature(asmCompiler.getClassname(), 1)); method.areturn(); method.end(); asmCompiler.endScript(false, false); byte[] bytes = asmCompiler.getClassByteArray(); Class blockClass = new JRubyClassLoader(context.runtime.getJRubyClassLoader()) .defineClass(asmCompiler.getClassname(), bytes); try { final AbstractScript script = (AbstractScript) blockClass.newInstance(); script.setRootScope(scope); return new CompiledBlockCallback() { @Override public IRubyObject call( ThreadContext context, IRubyObject self, IRubyObject args, Block block) { return script.__file__(context, self, args, block); } @Override public String getFile() { return "blah"; } @Override public int getLine() { return -1; } }; } catch (Exception e) { throw new RuntimeException(e); } }
@SuppressWarnings("unchecked") protected void compile() { if (bytecode != null) return; // check if we have a cached compiled version on disk String codeCache = RubyInstanceConfig.JIT_CODE_CACHE; File cachedClassFile = new File(codeCache + "/" + className + ".class"); if (codeCache != null && cachedClassFile.exists()) { FileInputStream fis = null; try { if (RubyInstanceConfig.JIT_LOADING_DEBUG) LOG.info("loading cached code from: " + cachedClassFile); fis = new FileInputStream(cachedClassFile); bytecode = new byte[(int) fis.getChannel().size()]; fis.read(bytecode); name = new ClassReader(bytecode).getClassName(); return; } catch (Exception e) { // ignore and proceed to compile } finally { try { fis.close(); } catch (Exception e) { } } } // Time the compilation long start = System.nanoTime(); asmCompiler.startScript(staticScope); final ASTCompiler compiler = ruby.getInstanceConfig().newCompiler(); CompilerCallback args = new CompilerCallback() { public void call(BodyCompiler context) { compiler.compileArgs(argsNode, context, true); } }; ASTInspector inspector = new ASTInspector(); if (ruby.getInstanceConfig().isJitDumping()) { inspector = new ASTInspector(className, true); } // check args first, since body inspection can depend on args inspector.inspect(argsNode); inspector.inspect(bodyNode); BodyCompiler methodCompiler; if (bodyNode != null) { // we have a body, do a full-on method methodCompiler = asmCompiler.startFileMethod(args, staticScope, inspector); compiler.compileBody(bodyNode, methodCompiler, true); } else { // If we don't have a body, check for required or opt args // if opt args, they could have side effects // if required args, need to raise errors if too few args passed // otherwise, method does nothing, make it a nop if (argsNode != null && (argsNode.getRequiredArgsCount() > 0 || argsNode.getOptionalArgsCount() > 0)) { methodCompiler = asmCompiler.startFileMethod(args, staticScope, inspector); methodCompiler.loadNil(); } else { methodCompiler = asmCompiler.startFileMethod(null, staticScope, inspector); methodCompiler.loadNil(); jitCallConfig = CallConfiguration.FrameNoneScopeNone; } } methodCompiler.endBody(); asmCompiler.endScript(false, false); // if we haven't already decided on a do-nothing call if (jitCallConfig == null) { jitCallConfig = inspector.getCallConfig(); } bytecode = asmCompiler.getClassByteArray(); if (ruby.getInstanceConfig().isJitDumping()) { TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(System.out)); new ClassReader(bytecode).accept(tcv, 0); } if (bytecode.length > ruby.getInstanceConfig().getJitMaxSize()) { bytecode = null; throw new NotCompilableException( "JITed method size exceeds configured max of " + ruby.getInstanceConfig().getJitMaxSize()); } if (codeCache != null) { JITCompiler.saveToCodeCache(ruby, bytecode, packageName, cachedClassFile); } counts.compiledCount.incrementAndGet(); counts.compileTime.addAndGet(System.nanoTime() - start); counts.codeSize.addAndGet(bytecode.length); counts.averageCompileTime.set(counts.compileTime.get() / counts.compiledCount.get()); counts.averageCodeSize.set(counts.codeSize.get() / counts.compiledCount.get()); synchronized (counts) { if (counts.largestCodeSize.get() < bytecode.length) { counts.largestCodeSize.set(bytecode.length); } } }