int n = instrs.length;
int ipc = 0;
Instr instr = null;
Object exception = null;
Ruby runtime = context.runtime;
DynamicScope currDynScope = context.getCurrentScope();
// Set up thread-poll counter for this scope
Counter tpCount = null;
if (profile) {
tpCount = scopeThreadPollCounts.get(scope);
if (tpCount == null) {
tpCount = new Counter();
scopeThreadPollCounts.put(scope, tpCount);
}
}
// Enter the looooop!
while (ipc < n) {
instr = instrs[ipc];
Operation operation = instr.getOperation();
if (debug) {
LOG.info("I: {}", instr);
interpInstrsCount++;
} else if (profile) {
if (operation.modifiesCode()) codeModificationsCount++;
interpInstrsCount++;
}
try {
Variable resultVar = null;
Object result = null;
switch(operation) {
// ----------- Control-transfer instructions -------------
case JUMP: {
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
break;
}
case MODULE_GUARD:
case JUMP_INDIRECT:
case B_TRUE:
case B_FALSE:
case B_NIL:
case B_UNDEF:
case BEQ:
case BNE: {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, self, temp, ipc);
break;
}
// ------------- Arg-receive instructions ------------
case RECV_PRE_REQD_ARG: {
ReceivePreReqdArgInstr ra = (ReceivePreReqdArgInstr)instr;
int argIndex = ra.getArgIndex();
result = (argIndex < args.length) ? args[argIndex] : context.nil; // SSS FIXME: This check is only required for closures, not methods
resultVar = ra.getResult();
ipc++;
break;
}
case RECV_POST_REQD_ARG: {
ReceivePostReqdArgInstr ra = (ReceivePostReqdArgInstr)instr;
result = ra.receivePostReqdArg(args);
if (result == null) result = context.nil; // For blocks
resultVar = ra.getResult();
ipc++;
break;
}
case RECV_OPT_ARG: {
ReceiveOptArgBase ra = (ReceiveOptArgBase)instr;
result = ra.receiveOptArg(args);
resultVar = ra.getResult();
ipc++;
break;
}
case RECV_REST_ARG: {
ReceiveRestArgBase ra = (ReceiveRestArgBase)instr;
result = ra.receiveRestArg(runtime, args);
resultVar = ra.getResult();
ipc++;
break;
}
case RECV_CLOSURE: {
result = block == Block.NULL_BLOCK ? context.nil : runtime.newProc(Block.Type.PROC, block);
resultVar = ((ResultInstr)instr).getResult();
ipc++;
break;
}
case RECV_EXCEPTION: {
ReceiveExceptionInstr rei = (ReceiveExceptionInstr)instr;
result = (exception instanceof RaiseException && rei.checkType) ? ((RaiseException)exception).getException() : exception;
resultVar = rei.getResult();
ipc++;
break;
}
// --------- 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(), rv, blockType);
}
case RETURN: {
return (IRubyObject)((ReturnBase)instr).getReturnValue().retrieve(context, self, currDynScope, temp);
}
case NONLOCAL_RETURN: {
NonlocalReturnInstr ri = (NonlocalReturnInstr)instr;
IRubyObject rv = (IRubyObject)ri.getReturnValue().retrieve(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;
}
// --------- Bookkeeping instructions --------
case CHECK_ARITY: {
((CheckArityInstr)instr).checkArity(runtime, args.length);
ipc++;
break;
}
case PUSH_FRAME: {
context.preMethodFrameAndClass(implClass, scope.getName(), self, block, scope.getStaticScope());
context.setCurrentVisibility(visibility);
ipc++;
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)
currDynScope = DynamicScope.newDynamicScope(scope.getStaticScope());
context.pushScope(currDynScope);
ipc++;
break;
}
case POP_FRAME: {
context.popFrame();
context.popRubyClass();
ipc++;
break;
}
case POP_BINDING: {
context.popScope();
ipc++;
break;
}
case THREAD_POLL: {
if (profile) {
tpCount.count++;
globalThreadPollCount++;
// SSS: Uncomment this to analyze profile
// Every 10K profile counts, spit out profile stats
// if (globalThreadPollCount % 10000 == 0) analyzeProfile(); //outputProfileStats();
}
context.callThreadPoll();
ipc++;
break;
}
case LINE_NUM: {
context.setLine(((LineNumberInstr)instr).lineNumber);
ipc++;
break;
}
case RUNTIME_HELPER: {
ipc++;
resultVar = ((ResultInstr)instr).getResult();
result = ((RuntimeHelperCall)instr).callHelper(context, currDynScope, self, temp, scope, blockType);
break;
}
// ---------- Common instruction ---------
case COPY: {
CopyInstr c = (CopyInstr)instr;
result = c.getSource().retrieve(context, self, currDynScope, temp);
resultVar = ((ResultInstr)instr).getResult();
ipc++;
break;
}
// ---------- All the rest ---------
default:
ipc++;
if (instr instanceof ResultInstr) resultVar = ((ResultInstr)instr).getResult();
result = instr.interpret(context, currDynScope, self, temp, block);
break;
}
if (resultVar != null) {
if (resultVar instanceof TemporaryVariable) {
temp[((TemporaryVariable)resultVar).offset] = result;
}
else {
LocalVariable lv = (LocalVariable)resultVar;
currDynScope.setValue((IRubyObject) result, lv.getLocation(), lv.getScopeDepth());
}
}
} catch (Throwable t) {
// Unrescuable:
// IRReturnJump, ThreadKill, RubyContinuation, MainExitException, etc.