package org.jruby.runtime;
import java.io.ByteArrayOutputStream;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.compiler.Compilable;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
public class InterpretedIRBlockBody extends IRBlockBody implements Compilable<InterpreterContext> {
private static final Logger LOG = LoggerFactory.getLogger(InterpretedIRBlockBody.class);
protected boolean pushScope;
protected boolean reuseParentScope;
private boolean displayedCFG = false;
private int callCount = 0;
private InterpreterContext interpreterContext;
private InterpreterContext fullInterpreterContext;
public InterpretedIRBlockBody(IRClosure closure, Signature signature) {
super(closure, signature);
this.pushScope = true;
this.reuseParentScope = false;
if (closure.getManager().getInstanceConfig().getJitThreshold() == -1) setCallCount(-1);
}
@Override
public void setCallCount(int callCount) {
this.callCount = callCount;
}
@Override
public void completeBuild(InterpreterContext interpreterContext) {
this.fullInterpreterContext = interpreterContext;
this.displayedCFG = false;
}
@Override
public IRScope getIRScope() {
return closure;
}
@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return closure.getArgumentDescriptors();
}
public InterpreterContext ensureInstrsReady() {
if (IRRuntimeHelpers.isDebug() && !displayedCFG) {
LOG.info("Executing '" + closure + "' (pushScope=" + pushScope + ", reuseParentScope=" + reuseParentScope);
LOG.info(closure.debugOutput());
displayedCFG = true;
}
InterpreterContext ic = interpreterContext;
if (ic == null) {
if (IRRuntimeHelpers.shouldPrintIR(closure.getStaticScope().getModule().getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(closure, false);
LOG.info("Printing simple IR for " + closure.getId() + ":\n" + new String(baos.toByteArray()));
}
ic = closure.getInterpreterContext();
interpreterContext = ic;
}
return ic;
}
@Override
public String getOwnerName() {
return null;
}
@Override
public String getName() {
return null;
}
@Override
public boolean canCallDirect() {
return fullInterpreterContext != null && fullInterpreterContext.hasExplicitCallProtocol();
}
@Override
protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
ensureInstrsReady();
return Interpreter.INTERPRET_BLOCK(context, block, null, fullInterpreterContext, args, block.getBinding().getMethod(), blockArg);
}
@Override
protected IRubyObject yieldDirect(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
ensureInstrsReady();
return Interpreter.INTERPRET_BLOCK(context, block, self, fullInterpreterContext, args, block.getBinding().getMethod(), Block.NULL_BLOCK);
}
@Override
protected IRubyObject commonYieldPath(ThreadContext context, Block block, Block.Type type, IRubyObject[] args, IRubyObject self, Block blockArg) {
if (callCount >= 0) promoteToFullBuild(context);
InterpreterContext ic = ensureInstrsReady();
Binding binding = block.getBinding();
Visibility oldVis = binding.getFrame().getVisibility();
Frame prevFrame = context.preYieldNoScope(binding);
DynamicScope actualScope = binding.getDynamicScope();
if (ic.pushNewDynScope()) {
context.pushScope(block.allocScope(actualScope));
} else if (ic.reuseParentDynScope()) {
context.pushScope(actualScope);
}
self = IRRuntimeHelpers.updateBlockState(block, self);
try {
return Interpreter.INTERPRET_BLOCK(context, block, self, ic, args, binding.getMethod(), blockArg);
}
finally {
postYield(context, ic, binding, oldVis, prevFrame);
}
}
private void promoteToFullBuild(ThreadContext context) {
final Ruby runtime = context.runtime;
if (runtime.isBooting() && !Options.JIT_KERNEL.load()) return;
if (this.callCount < 0) return;
if (this.callCount++ >= runtime.getInstanceConfig().getJitThreshold()) {
synchronized (this) {
if (this.callCount >= 0) {
this.callCount = Integer.MIN_VALUE;
runtime.getJITCompiler().buildThresholdReached(context, this);
}
}
}
}
public RubyModule getImplementationClass() {
return closure.getStaticScope().getModule();
}
}