public InterpreterContext ensureInstrsReady() { if (IRRuntimeHelpers.isDebug() && !displayedCFG) { LOG.info( "Executing '" + closure + "' (pushScope=" + pushScope + ", reuseParentScope=" + reuseParentScope); LOG.info(closure.debugOutput()); displayedCFG = true; } if (interpreterContext == null) { if (Options.IR_PRINT.load()) { ByteArrayOutputStream baos = IRDumper.printIR(closure, false); LOG.info( "Printing simple IR for " + closure.getName(), "\n" + new String(baos.toByteArray())); } interpreterContext = closure.getInterpreterContext(); fullInterpreterContext = interpreterContext; } return interpreterContext; }
private static void loadJRubyProperties(File dotfile) { FileInputStream fis = null; try { // update system properties with long form jruby properties from .jrubyrc Properties sysProps = System.getProperties(); Properties newProps = new Properties(); // load properties and re-set as jruby.* fis = new FileInputStream(dotfile); newProps.load(fis); for (Map.Entry entry : newProps.entrySet()) { sysProps.put("jruby." + entry.getKey(), entry.getValue()); } } catch (IOException ioe) { LOG.debug("exception loading " + dotfile, ioe); } catch (SecurityException se) { LOG.debug("exception loading " + dotfile, se); } finally { if (fis != null) try { fis.close(); } catch (Exception e) { } } }
protected void doDebug() { // FIXME: This is printing out IRScope CFG but JIT may be active and it might not reflect // currently executing. Move into JIT and into interp since they will be getting CFG from // different sources // FIXME: This is only printing out CFG once. If we keep applying more passes then we // will want to print out after those new passes. ensureInstrsReady(); LOG.info("Executing '" + method.getName() + "'"); if (!displayedCFG) { LOG.info(method.debugOutput()); displayedCFG = true; } }
private Instr[] prepareInstructionsForInterpretation() { checkRelinearization(); if (linearizedInstrArray != null) return linearizedInstrArray; // Already prepared try { buildLinearization(); // FIXME: compiler passes should have done this depends(linearization()); } catch (RuntimeException e) { LOG.error("Error linearizing cfg: ", e); CFG c = cfg(); LOG.error("\nGraph:\n" + c.toStringGraph()); LOG.error("\nInstructions:\n" + c.toStringInstrs()); throw e; } // Set up IPCs HashMap<Label, Integer> labelIPCMap = new HashMap<Label, Integer>(); List<Instr> newInstrs = new ArrayList<Instr>(); int ipc = 0; for (BasicBlock b : linearizedBBList) { labelIPCMap.put(b.getLabel(), ipc); List<Instr> bbInstrs = b.getInstrs(); int bbInstrsLength = bbInstrs.size(); for (int i = 0; i < bbInstrsLength; i++) { Instr instr = bbInstrs.get(i); if (instr instanceof Specializeable) { instr = ((Specializeable) instr).specializeForInterpretation(); bbInstrs.set(i, instr); } if (!(instr instanceof ReceiveSelfInstr)) { newInstrs.add(instr); ipc++; } } } // Set up label PCs setupLabelPCs(labelIPCMap); // Exit BB ipc cfg().getExitBB().getLabel().setTargetPC(ipc + 1); linearizedInstrArray = newInstrs.toArray(new Instr[newInstrs.size()]); return linearizedInstrArray; }
public IRubyObject call( ThreadContext context, IRubyObject self, RubyModule clazz, DynamicScope evalScope, Block block, String backtraceName) { if (IRRuntimeHelpers.isDebug()) { LOG.info("Graph:\n" + cfg().toStringGraph()); LOG.info("CFG:\n" + cfg().toStringInstrs()); } // FIXME: Do not push new empty arg array in every time return Interpreter.INTERPRET_EVAL( context, self, this, clazz, new IRubyObject[] {}, backtraceName, block, null); }
void finish(boolean close) throws BadDescriptorException, IOException { synchronized (refCounter) { // if refcount is at or below zero, we're no longer valid if (refCounter.get() <= 0) { throw new BadDescriptorException(); } // if channel is already closed, we're no longer valid if (!channel.isOpen()) { throw new BadDescriptorException(); } // otherwise decrement and possibly close as normal if (close) { int count = refCounter.decrementAndGet(); if (DEBUG) LOG.info("Descriptor for fileno {} refs: {}", internalFileno, count); if (count <= 0) { // if we're the last referrer, close the channel try { channel.close(); } finally { unregisterDescriptor(internalFileno); } } } } }
/** Run any necessary passes to get the IR ready for compilation */ public Tuple<Instr[], Map<Integer, Label[]>> prepareForCompilation() { // Build CFG and run compiler passes, if necessary if (getCFG() == null) runCompilerPasses(); // Add this always since we dont re-JIT a previously // JIT-ted closure. But, check if there are other // smarts available to us and eliminate adding this // code to every closure there is. // // Add a global ensure block to catch uncaught breaks // and throw a LocalJumpError. if (this instanceof IRClosure && ((IRClosure) this).addGEBForUncaughtBreaks()) { this.relinearizeCFG = true; } try { buildLinearization(); // FIXME: compiler passes should have done this depends(linearization()); } catch (RuntimeException e) { LOG.error("Error linearizing cfg: ", e); CFG c = cfg(); LOG.error("\nGraph:\n" + c.toStringGraph()); LOG.error("\nInstructions:\n" + c.toStringInstrs()); throw e; } // Set up IPCs // FIXME: Would be nice to collapse duplicate labels; for now, using Label[] HashMap<Integer, Label[]> ipcLabelMap = new HashMap<Integer, Label[]>(); List<Instr> newInstrs = new ArrayList<Instr>(); int ipc = 0; for (BasicBlock b : linearizedBBList) { Label l = b.getLabel(); ipcLabelMap.put(ipc, catLabels(ipcLabelMap.get(ipc), l)); for (Instr i : b.getInstrs()) { if (!(i instanceof ReceiveSelfInstr)) { newInstrs.add(i); ipc++; } } } return new Tuple<Instr[], Map<Integer, Label[]>>( newInstrs.toArray(new Instr[newInstrs.size()]), ipcLabelMap); }
public void setFlag(Node node, Flag modifier) { if (dump) { LOG.info( "[ASTInspector] " + name + "\n\tset flag " + modifier + " because of " + node.getNodeType() + " at " + node.getPosition()); } flags |= modifier.flag; }
private void finish(boolean close) throws BadDescriptorException, IOException { try { flushWrite(); if (DEBUG) LOG.info("Descriptor for fileno {} closed by stream", descriptor.getFileno()); } finally { buffer = EMPTY_BUFFER; // clear runtime so it doesn't get stuck in memory (JRUBY-2933) runtime = null; // finish descriptor descriptor.finish(close); } }
/** * Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open * channel but with a specified fileno. * * @param fileno The fileno to use for the new descriptor * @return A duplicate ChannelDescriptor based on this one */ public ChannelDescriptor dup2(int fileno) { synchronized (refCounter) { refCounter.incrementAndGet(); if (DEBUG) LOG.info("Reopen fileno {}, refs now: {}", fileno, refCounter.get()); return new ChannelDescriptor( channel, fileno, originalModes, fileDescriptor, refCounter, canBeSeekable, isInAppendMode); } }
/** Ensure close (especially flush) when we're finished with. */ @Override public void finalize() throws Throwable { super.finalize(); if (closedExplicitly) return; if (DEBUG) { LOG.info("finalize() for not explicitly closed stream"); } // FIXME: I got a bunch of NPEs when I didn't check for nulls here...HOW?! if (descriptor != null && descriptor.isOpen()) { // tidy up finish(autoclose); } }
// SSS FIXME: Extremely inefficient public int getEnsurerPC(Instr excInstr) { depends(cfg()); for (BasicBlock b : linearizedBBList) { for (Instr i : b.getInstrs()) { if (i == excInstr) { BasicBlock ensurerBB = cfg.getEnsurerBBFor(b); return (ensurerBB == null) ? -1 : ensurerBB.getLabel().getTargetPC(); } } } // SSS FIXME: Cannot happen! Throw runtime exception LOG.error("Fell through looking for ensurer ipc for " + excInstr); return -1; }
static void log(DefaultMethod method, String name, String message, String... reason) { String className = method.getImplementationClass().getBaseName(); if (className == null) className = "<anon class>"; StringBuilder builder = new StringBuilder(message + ":" + className + "." + name + " at " + method.getPosition()); if (reason.length > 0) { builder.append(" because of: \""); for (int i = 0; i < reason.length; i++) { builder.append(reason[i]); } builder.append('"'); } LOG.info(builder.toString()); }
/** * Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open * channel but with a specified fileno. This differs from the fileno version by making the target * descriptor into a new reference to the current descriptor's channel, closing what it originally * pointed to and preserving its original fileno. * * @param other the descriptor to dup this one into */ public void dup2Into(ChannelDescriptor other) throws BadDescriptorException, IOException { synchronized (refCounter) { refCounter.incrementAndGet(); if (DEBUG) LOG.info("Reopen fileno {}, refs now: {}", internalFileno, refCounter.get()); other.close(); other.channel = channel; other.originalModes = originalModes; other.fileDescriptor = fileDescriptor; other.refCounter = refCounter; other.canBeSeekable = canBeSeekable; other.readableChannel = readableChannel; other.writableChannel = writableChannel; other.seekableChannel = seekableChannel; } }
public static IRubyObject interpret(Ruby runtime, Node rootNode, IRubyObject self) { if (runtime.is1_9()) IRBuilder.setRubyVersion("1.9"); IRScriptBody root = (IRScriptBody) IRBuilder.createIRBuilder(runtime, runtime.getIRManager()) .buildRoot((RootNode) rootNode); // We get the live object ball rolling here. This give a valid value for the top // of this lexical tree. All new scope can then retrieve and set based on lexical parent. if (root.getStaticScope().getModule() == null) { // If an eval this may already be setup. root.getStaticScope().setModule(runtime.getObject()); } RubyModule currModule = root.getStaticScope().getModule(); // Scope state for root? IRStaticScopeFactory.newIRLocalScope(null).setModule(currModule); ThreadContext context = runtime.getCurrentContext(); try { runBeginEndBlocks( root.getBeginBlocks(), context, self, null); // FIXME: No temp vars yet...not needed? InterpretedIRMethod method = new InterpretedIRMethod(root, currModule); IRubyObject rv = method.call(context, self, currModule, "(root)", IRubyObject.NULL_ARRAY); runBeginEndBlocks( root.getEndBlocks(), context, self, null); // FIXME: No temp vars yet...not needed? if ((IRRuntimeHelpers.isDebug() || IRRuntimeHelpers.inProfileMode()) && interpInstrsCount > 10000) { LOG.info("-- Interpreted instructions: {}", interpInstrsCount); /* for (Operation o: opStats.keySet()) { System.out.println(o + " = " + opStats.get(o).count); } */ } return rv; } catch (IRBreakJump bj) { throw IRException.BREAK_LocalJumpError.getException(context.runtime); } }
public static IRubyObject interpretTop(Ruby runtime, IRScope scope, IRubyObject self) { assert scope instanceof IRScript : "Must be an IRScript scope at Top!!!"; IRScript root = (IRScript) scope; // We get the live object ball rolling here. This give a valid value for the top // of this lexical tree. All new scope can then retrieve and set based on lexical parent. if (root.getStaticScope().getModule() == null) { // If an eval this may already be setup. root.getStaticScope().setModule(runtime.getObject()); } RubyModule currModule = root.getStaticScope().getModule(); IRMethod rootMethod = root.getRootClass().getRootMethod(); InterpretedIRMethod method = new InterpretedIRMethod(rootMethod, currModule); ThreadContext context = runtime.getCurrentContext(); IRubyObject rv = method.call(context, self, currModule, "", IRubyObject.NULL_ARRAY); if (isDebug()) LOG.debug("-- Interpreted instructions: {}", interpInstrsCount); return rv; }
public static void saveToCodeCache( Ruby ruby, byte[] bytecode, String packageName, File cachedClassFile) { String codeCache = RubyInstanceConfig.JIT_CODE_CACHE; File codeCacheDir = new File(codeCache); if (!codeCacheDir.exists()) { ruby.getWarnings().warn("jruby.jit.codeCache directory " + codeCacheDir + " does not exist"); } else if (!codeCacheDir.isDirectory()) { ruby.getWarnings() .warn("jruby.jit.codeCache directory " + codeCacheDir + " is not a directory"); } else if (!codeCacheDir.canWrite()) { ruby.getWarnings().warn("jruby.jit.codeCache directory " + codeCacheDir + " is not writable"); } else { if (!new File(codeCache, packageName).isDirectory()) { boolean createdDirs = new File(codeCache, packageName).mkdirs(); if (!createdDirs) { ruby.getWarnings() .warn("could not create JIT cache dir: " + new File(codeCache, packageName)); } } // write to code cache FileOutputStream fos = null; try { if (RubyInstanceConfig.JIT_LOADING_DEBUG) LOG.info("writing jitted code to to " + cachedClassFile); fos = new FileOutputStream(cachedClassFile); fos.write(bytecode); } catch (Exception e) { e.printStackTrace(); // ignore } finally { try { fos.close(); } catch (Exception e) { } } } }
public static void ivarSet(VariableSite site, IRubyObject self, IRubyObject value) throws Throwable { RubyClass realClass = self.getMetaClass().getRealClass(); VariableAccessor accessor = realClass.getVariableAccessorForWrite(site.name()); // set variable value and fold by returning value MethodHandle setValue; boolean direct = false; if (accessor instanceof FieldVariableAccessor) { direct = true; int offset = ((FieldVariableAccessor) accessor).getOffset(); Class cls = REIFIED_OBJECT_CLASSES[offset]; setValue = findStatic(cls, "setVariableChecked", methodType(void.class, cls, Object.class)); setValue = explicitCastArguments( setValue, methodType(void.class, IRubyObject.class, IRubyObject.class)); } else { setValue = findStatic( accessor.getClass(), "setVariableChecked", methodType( void.class, RubyBasicObject.class, RubyClass.class, int.class, Object.class)); setValue = explicitCastArguments( setValue, methodType( void.class, IRubyObject.class, RubyClass.class, int.class, IRubyObject.class)); setValue = insertArguments(setValue, 1, realClass, accessor.getIndex()); } // prepare fallback MethodHandle fallback = null; if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tset on type " + self.getMetaClass().id + " failed (polymorphic)" + extractSourceInfo(site)); fallback = findStatic( Bootstrap.class, "ivarSetFail", methodType(void.class, VariableSite.class, IRubyObject.class, IRubyObject.class)); fallback = fallback.bindTo(site); site.setTarget(fallback); fallback.invokeExact(self, value); } else { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) { if (direct) { LOG.info( site.name() + "\tset field on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } else { LOG.info( site.name() + "\tset on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } } fallback = site.getTarget(); site.incrementChainCount(); } // prepare test MethodHandle test = findStatic( InvocationLinker.class, "testRealClass", methodType(boolean.class, int.class, IRubyObject.class)); test = insertArguments(test, 0, accessor.getClassId()); test = dropArguments(test, 1, IRubyObject.class); setValue = guardWithTest(test, setValue, fallback); if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tset on class " + self.getMetaClass().id + " bound directly" + extractSourceInfo(site)); site.setTarget(setValue); setValue.invokeExact(self, value); }
public static IRubyObject ivarGet(VariableSite site, IRubyObject self) throws Throwable { RubyClass realClass = self.getMetaClass().getRealClass(); VariableAccessor accessor = realClass.getVariableAccessorForRead(site.name()); // produce nil if the variable has not been initialize MethodHandle nullToNil = findStatic( Helpers.class, "nullToNil", methodType(IRubyObject.class, IRubyObject.class, IRubyObject.class)); nullToNil = insertArguments(nullToNil, 1, self.getRuntime().getNil()); nullToNil = explicitCastArguments(nullToNil, methodType(IRubyObject.class, Object.class)); // get variable value and filter with nullToNil MethodHandle getValue; boolean direct = false; if (accessor instanceof FieldVariableAccessor) { direct = true; int offset = ((FieldVariableAccessor) accessor).getOffset(); Class cls = REIFIED_OBJECT_CLASSES[offset]; getValue = lookup().findGetter(cls, "var" + offset, Object.class); getValue = explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class)); } else { getValue = findStatic( VariableAccessor.class, "getVariable", methodType(Object.class, RubyBasicObject.class, int.class)); getValue = explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class, int.class)); getValue = insertArguments(getValue, 1, accessor.getIndex()); } getValue = filterReturnValue(getValue, nullToNil); // prepare fallback MethodHandle fallback = null; if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tqet on type " + self.getMetaClass().id + " failed (polymorphic)" + extractSourceInfo(site)); fallback = findStatic( Bootstrap.class, "ivarGetFail", methodType(IRubyObject.class, VariableSite.class, IRubyObject.class)); fallback = fallback.bindTo(site); site.setTarget(fallback); return (IRubyObject) fallback.invokeWithArguments(self); } else { if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) { if (direct) { LOG.info( site.name() + "\tget field on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } else { LOG.info( site.name() + "\tget on type " + self.getMetaClass().id + " added to PIC" + extractSourceInfo(site)); } } fallback = site.getTarget(); site.incrementChainCount(); } // prepare test MethodHandle test = findStatic( InvocationLinker.class, "testRealClass", methodType(boolean.class, int.class, IRubyObject.class)); test = insertArguments(test, 0, accessor.getClassId()); getValue = guardWithTest(test, getValue, fallback); if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( site.name() + "\tget on class " + self.getMetaClass().id + " bound directly" + extractSourceInfo(site)); site.setTarget(getValue); return (IRubyObject) getValue.invokeExact(self); }
@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); } } }
public static void addScopeAwareMethods(String... methods) { if (DEBUG) LOG.debug("Adding scope-aware method names: {}", Arrays.toString(methods)); SCOPE_AWARE_METHODS.addAll(Arrays.asList(methods)); }
private void printError(String message) { LOG.error(message + "\nGraph:\n" + this + "\nInstructions:\n" + toStringInstrs()); }
public static IRubyObject interpret(ThreadContext context, CFG cfg, InterpreterContext interp) { Ruby runtime = context.getRuntime(); boolean inClosure = (cfg.getScope() instanceof IRClosure); boolean passThroughBreak = false; try { interp.setMethodExitLabel( cfg.getExitBB().getLabel()); // used by return and break instructions! Instr[] instrs = cfg.prepareForInterpretation(); int n = instrs.length; int ipc = 0; Instr lastInstr = null; while (ipc < n) { interpInstrsCount++; lastInstr = instrs[ipc]; if (isDebug()) LOG.debug("I: {}", lastInstr); try { Label jumpTarget = lastInstr.interpret(interp); ipc = (jumpTarget == null) ? ipc + 1 : jumpTarget.getTargetPC(); } // SSS FIXME: This only catches Ruby exceptions // What about Java exceptions? catch (org.jruby.exceptions.RaiseException re) { ipc = cfg.getRescuerPC(lastInstr); if (ipc == -1) throw re; // No one rescued exception, pass it on! interp.setException(re.getException()); } } // If a closure, and lastInstr was a return, have to return from the nearest method! IRubyObject rv = (IRubyObject) interp.getReturnValue(); if (lastInstr instanceof ReturnInstr && inClosure && !interp.inLambda()) { throw new IRReturnJump(((ReturnInstr) lastInstr).methodToReturnFrom, rv); } // If a closure, and lastInstr was a break, have to return from the nearest closure! else if (lastInstr instanceof BREAK_Instr) { if (!inClosure) throw runtime.newLocalJumpError(Reason.BREAK, rv, "unexpected break"); passThroughBreak = true; RuntimeHelpers.breakJump(context, rv); } return rv; } catch (JumpException.BreakJump bj) { if (passThroughBreak) throw bj; return (IRubyObject) bj.getValue(); } catch (IRReturnJump rj) { // - If we are in a lambda, stop propagating // - If not in a lambda // - if in a closure, pass it along // - if not in a closure, we got this return jump from a closure further up the call stack. // So, continue popping the call stack till we get to the right method if (!interp.inLambda() && (inClosure || (rj.methodToReturnFrom != cfg.getScope()))) throw rj; // pass it along return (IRubyObject) rj.returnValue; } finally { if (interp.getFrame() != null) { context.popFrame(); interp.setFrame(null); } if (interp.hasAllocatedDynamicScope()) context.postMethodScopeOnly(); } }
public void disable() { if (dump) LOG.debug("[ASTInspector] {} DISABLED", name); flags = 0xFFFFFFFF; }
private static void debug(RubyThread thread, String message) { if (DEBUG) LOG.debug(Thread.currentThread() + "(" + thread.status + "): " + message); }
private static IRubyObject interpret( ThreadContext context, IRubyObject self, IRScope scope, Visibility visibility, RubyModule implClass, IRubyObject[] args, Block block, Block.Type blockType) { Instr[] instrs = scope.getInstrsForInterpretation(); // The base IR may not have been processed yet if (instrs == null) instrs = scope.prepareForInterpretation(blockType == Block.Type.LAMBDA); int numTempVars = scope.getTemporaryVariableSize(); Object[] temp = numTempVars > 0 ? new Object[numTempVars] : null; int n = instrs.length; int ipc = 0; Instr instr = null; Object exception = null; int kwArgHashCount = (scope.receivesKeywordArgs() && args[args.length - 1] instanceof RubyHash) ? 1 : 0; DynamicScope currDynScope = context.getCurrentScope(); // Counter tpCount = null; // Init profiling this scope boolean debug = IRRuntimeHelpers.isDebug(); boolean profile = IRRuntimeHelpers.inProfileMode(); Integer scopeVersion = profile ? initProfiling(scope) : 0; // Enter the looooop! while (ipc < n) { instr = instrs[ipc]; ipc++; Operation operation = instr.getOperation(); if (debug) { LOG.info("I: {}", instr); interpInstrsCount++; } else if (profile) { if (operation.modifiesCode()) codeModificationsCount++; interpInstrsCount++; /* Counter cnt = opStats.get(operation); if (cnt == null) { cnt = new Counter(); opStats.put(operation, cnt); } cnt.count++; */ } try { switch (operation.opClass) { case ARG_OP: { receiveArg( context, instr, operation, args, kwArgHashCount, currDynScope, temp, exception, block); break; } case BRANCH_OP: { if (operation == Operation.JUMP) { ipc = ((JumpInstr) instr).getJumpTarget().getTargetPC(); } else { ipc = instr.interpretAndGetNewIPC(context, currDynScope, self, temp, ipc); } break; } case CALL_OP: { if (profile) updateCallSite(instr, scope, scopeVersion); processCall( context, instr, operation, scope, currDynScope, temp, self, block, blockType); break; } case BOOK_KEEPING_OP: { switch (operation) { case PUSH_FRAME: { context.preMethodFrameAndClass( implClass, scope.getName(), self, block, scope.getStaticScope()); context.setCurrentVisibility(visibility); break; } case PUSH_BINDING: { // SSS NOTE: Method scopes only! // // Blocks are a headache -- so, these instrs. are only added to IRMethods. // Blocks have more complicated logic for pushing a dynamic scope (see // InterpretedIRBlockBody) // Changed by DPR currDynScope = DynamicScope.newDynamicScope( scope.getStaticScope(), context.getCurrentScope().getDepth()); context.pushScope(currDynScope); break; } case CHECK_ARITY: ((CheckArityInstr) instr).checkArity(context.runtime, args.length); break; case POP_FRAME: context.popFrame(); context.popRubyClass(); break; case POP_BINDING: context.popScope(); break; case THREAD_POLL: if (profile) { // SSS: Not being used currently // tpCount.count++; globalThreadPollCount++; // 20K is arbitrary // Every 20K profile counts, spit out profile stats if (globalThreadPollCount % 20000 == 0) { analyzeProfile(); // outputProfileStats(); } } context.callThreadPoll(); break; case LINE_NUM: context.setLine(((LineNumberInstr) instr).lineNumber); break; case RECORD_END_BLOCK: ((RecordEndBlockInstr) instr).interpret(); break; } break; } case OTHER_OP: { Object result = null; switch (operation) { // --------- Return flavored instructions -------- case BREAK: { BreakInstr bi = (BreakInstr) instr; IRubyObject rv = (IRubyObject) bi.getReturnValue().retrieve(context, self, currDynScope, temp); // This also handles breaks in lambdas -- by converting them to a return return IRRuntimeHelpers.initiateBreak( context, scope, bi.getScopeToReturnTo().getScopeId(), rv, blockType); } case RETURN: { return (IRubyObject) retrieveOp( ((ReturnBase) instr).getReturnValue(), context, self, currDynScope, temp); } case NONLOCAL_RETURN: { NonlocalReturnInstr ri = (NonlocalReturnInstr) instr; IRubyObject rv = (IRubyObject) retrieveOp(ri.getReturnValue(), context, self, currDynScope, temp); ipc = n; // If not in a lambda, check if this was a non-local return if (!IRRuntimeHelpers.inLambda(blockType)) { IRRuntimeHelpers.initiateNonLocalReturn( context, scope, ri.methodToReturnFrom, rv); } return rv; } // ---------- Common instruction --------- case COPY: { CopyInstr c = (CopyInstr) instr; result = retrieveOp(c.getSource(), context, self, currDynScope, temp); setResult(temp, currDynScope, c.getResult(), result); break; } case GET_FIELD: { GetFieldInstr gfi = (GetFieldInstr) instr; IRubyObject object = (IRubyObject) gfi.getSource().retrieve(context, self, currDynScope, temp); VariableAccessor a = gfi.getAccessor(object); result = a == null ? null : (IRubyObject) a.get(object); if (result == null) { result = context.nil; } setResult(temp, currDynScope, gfi.getResult(), result); break; } case SEARCH_CONST: { SearchConstInstr sci = (SearchConstInstr) instr; result = sci.getCachedConst(); if (!sci.isCached(context, result)) result = sci.cache(context, currDynScope, self, temp); setResult(temp, currDynScope, sci.getResult(), result); break; } // ---------- All the rest --------- default: result = instr.interpret(context, currDynScope, self, temp, block); setResult(temp, currDynScope, instr, result); break; } break; } } } catch (Throwable t) { // Unrescuable: // IRReturnJump, ThreadKill, RubyContinuation, MainExitException, etc. // These cannot be rescued -- only run ensure blocks // // Others: // IRBreakJump, Ruby exceptions, errors, and other java exceptions. // These can be rescued -- run rescue blocks if (debug) LOG.info( "in scope: " + scope + ", caught Java throwable: " + t + "; excepting instr: " + instr); ipc = (t instanceof Unrescuable) ? scope.getEnsurerPC(instr) : scope.getRescuerPC(instr); if (debug) LOG.info("ipc for rescuer/ensurer: " + ipc); if (ipc == -1) { Helpers.throwException((Throwable) t); } else { exception = t; } } } // Control should never get here! // SSS FIXME: But looks like BEGIN/END blocks get here -- needs fixing return null; }
public void setFlag(Flag modifier) { if (dump) { LOG.info("[ASTInspector] " + name + "\n\tset flag " + modifier); } flags |= modifier.flag; }
/** * Update the given call site using the new target, wrapping with appropriate guard and * argument-juggling logic. Return a handle suitable for invoking with the site's original method * type. */ MethodHandle updateInvocationTarget( MethodHandle target, IRubyObject self, RubyModule testClass, CacheEntry entry, SwitchPoint switchPoint) { if (target == null || clearCount > Options.INVOKEDYNAMIC_MAXFAIL.load() || (!hasSeenType(testClass.id) && seenTypesCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load())) { setTarget(target = prepareBinder().invokeVirtualQuiet(lookup(), "fail")); } else { MethodHandle fallback; MethodHandle gwt; // if we've cached no types, and the site is bound and we haven't seen this new type... if (seenTypesCount() > 0 && getTarget() != null && !hasSeenType(testClass.id)) { // stack it up into a PIC if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info(methodName + "\tadded to PIC " + logMethod(entry.method)); fallback = getTarget(); } else { // wipe out site with this new type and method String bind = boundOnce ? "rebind" : "bind"; if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info( methodName + "\ttriggered site #" + siteID + " " + bind); // + " (" + file() + ":" + line() + ")"); fallback = this.fallback; clearTypes(); } addType(testClass.id); SmartHandle test; SmartBinder selfTest = SmartBinder.from(signature.asFold(boolean.class)).permute("self"); if (self instanceof RubySymbol || self instanceof RubyFixnum || self instanceof RubyFloat || self instanceof RubyNil || self instanceof RubyBoolean.True || self instanceof RubyBoolean.False) { test = selfTest .insert(1, "selfJavaType", self.getClass()) .cast(boolean.class, Object.class, Class.class) .invoke(TEST_CLASS); } else { test = SmartBinder.from(signature.changeReturn(boolean.class)) .permute("self") .insert(0, "selfClass", RubyClass.class, testClass) .invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testType"); } gwt = MethodHandles.guardWithTest(test.handle(), target, fallback); // wrap in switchpoint for mutation invalidation gwt = switchPoint.guardWithTest(gwt, fallback); setTarget(gwt); } return target; }