@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); } } }
/** * Calculates expression value with object as a context for path expressions. * * @since 1.1 */ public Object evaluate(Object o) { return ASTCompiler.compile(this).evaluateASTChain(o); }