
org.jruby.ir.interpreter.SimpleMethodInterpreterEngine Maven / Gradle / Ivy
package org.jruby.ir.interpreter;
import java.util.HashMap;
import java.util.Map;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.common.IRubyWarnings;
import org.jruby.ir.OpClass;
import org.jruby.ir.Operation;
import org.jruby.ir.instructions.CopyInstr;
import org.jruby.ir.instructions.GetFieldInstr;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.JumpInstr;
import org.jruby.ir.instructions.LineNumberInstr;
import org.jruby.ir.instructions.PutFieldInstr;
import org.jruby.ir.instructions.ReceivePreReqdArgInstr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.instructions.ReturnBase;
import org.jruby.ir.instructions.RuntimeHelperCall;
import org.jruby.ir.instructions.SearchConstInstr;
import org.jruby.ir.instructions.specialized.OneOperandArgNoBlockCallInstr;
import org.jruby.ir.instructions.specialized.OneOperandArgNoBlockNoResultCallInstr;
import org.jruby.ir.instructions.specialized.ZeroOperandArgNoBlockCallInstr;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.ivars.VariableAccessor;
import org.jruby.runtime.opto.ConstantCache;
/**
* An attempt at a minimal subset of instrs for small simple methods.
*/
public class SimpleMethodInterpreterEngine extends InterpreterEngine {
public static Map OPERATIONS = new HashMap() {{
put(Operation.RECV_PRE_REQD_ARG, true);
put(Operation.CHECK_ARITY, true);
put(Operation.RETURN, true);
put(Operation.LINE_NUM, true);
put(Operation.RECV_SELF, true);
put(Operation.RECV_JRUBY_EXC, true);
put(Operation.THROW, true);
put(Operation.PUSH_METHOD_FRAME, true);
put(Operation.POP_METHOD_FRAME, true);
put(Operation.PUSH_METHOD_BINDING, true);
put(Operation.POP_BINDING, true);
put(Operation.NORESULT_CALL_1O, true);
put(Operation.SEARCH_CONST, true);
put(Operation.INHERITANCE_SEARCH_CONST, true);
put(Operation.NORESULT_CALL, true);
put(Operation.CALL, true);
put(Operation.CALL_0O, true);
put(Operation.CALL_1O, true);
put(Operation.B_FALSE, true);
put(Operation.B_NIL, true);
put(Operation.BNE, true);
put(Operation.LOAD_IMPLICIT_CLOSURE, true);
put(Operation.COPY, true);
put(Operation.JUMP, true);
put(Operation.RUNTIME_HELPER, true);
put(Operation.GET_FIELD, true);
put(Operation.PUT_FIELD, true);
put(Operation.BUILD_COMPOUND_STRING, true);
put(Operation.CONST_MISSING, true);
}};
@Override
public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, InterpreterContext interpreterContext, RubyModule implClass, String name, Block blockArg) {
// Just use any interp since it will contain no recvs
return interpret(context, block, self, interpreterContext, implClass, name, (IRubyObject) null, blockArg);
}
@Override
public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, InterpreterContext interpreterContext, RubyModule implClass, String name, IRubyObject arg1, Block blockArg) {
Instr[] instrs = interpreterContext.getInstructions();
Object[] temp = interpreterContext.allocateTemporaryVariables();
int n = instrs.length;
int ipc = 0;
Object exception = null;
Block.Type blockType = block == null ? null : block.type;
StaticScope currScope = interpreterContext.getStaticScope();
DynamicScope currDynScope = context.getCurrentScope();
// Init profiling this scope
boolean debug = IRRuntimeHelpers.isDebug();
// Enter the looooop!
while (ipc < n) {
Instr instr = instrs[ipc];
ipc++;
Operation operation = instr.getOperation();
if (debug) {
Interpreter.LOG.info("I: {}", instr);
Interpreter.interpInstrsCount++;
}
try {
switch (operation) {
case RECV_PRE_REQD_ARG:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), arg1);
break;
case CHECK_ARITY:
break;
case RETURN:
return (IRubyObject) retrieveOp(((ReturnBase) instr).getReturnValue(), context, self, currDynScope, currScope, temp);
case LINE_NUM:
context.setLine(((LineNumberInstr) instr).lineNumber);
break;
case RECV_SELF:
break;
case RECV_JRUBY_EXC:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), exception);
break;
case THROW:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case PUSH_METHOD_FRAME:
context.preMethodFrameOnly(implClass, name, self, blockArg);
// Only the top-level script scope has PRIVATE visibility.
// This is already handled as part of Interpreter.execute above.
// Everything else is PUBLIC by default.
context.setCurrentVisibility(Visibility.PUBLIC);
break;
case POP_METHOD_FRAME:
context.popFrame();
break;
case PUSH_METHOD_BINDING:
// IMPORTANT: Preserve this update of currDynScope.
// This affects execution of all instructions in this scope
// which will now use the updated value of currDynScope.
currDynScope = interpreterContext.newDynamicScope(context);
context.pushScope(currDynScope);
break;
case POP_BINDING:
context.popScope();
break;
case NORESULT_CALL_1O: {
OneOperandArgNoBlockNoResultCallInstr call = (OneOperandArgNoBlockNoResultCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
call.getCallSite().call(context, self, r, o);
break;
}
case SEARCH_CONST: {
SearchConstInstr sci = (SearchConstInstr) instr;
ConstantCache cache = sci.getConstantCache();
Object result;
if (!ConstantCache.isCached(cache)) {
result = sci.cache(context, currScope, currDynScope, self, temp);
} else {
result = cache.value;
}
setResult(temp, currDynScope, sci.getResult(), result);
break;
}
case INHERITANCE_SEARCH_CONST:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(),
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case NORESULT_CALL:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case CALL:
setResult(temp, currDynScope, instr,
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case CALL_0O: {
ZeroOperandArgNoBlockCallInstr call = (ZeroOperandArgNoBlockCallInstr)instr;
IRubyObject r = (IRubyObject)retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r));
break;
}
case CALL_1O: {
OneOperandArgNoBlockCallInstr call = (OneOperandArgNoBlockCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r, o));
break;
}
case B_NIL:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_FALSE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_TRUE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case BNE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case LOAD_IMPLICIT_CLOSURE:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), blockArg);
break;
case COPY: // NO INTERP
setResult(temp, currDynScope, ((CopyInstr) instr).getResult(),
retrieveOp(((CopyInstr) instr).getSource(), context, self, currDynScope, currScope, temp));
break;
case JUMP: // NO INTERP
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
break;
case RUNTIME_HELPER: { // NO INTERP
RuntimeHelperCall rhc = (RuntimeHelperCall)instr;
setResult(temp, currDynScope, rhc.getResult(),
rhc.callHelper(context, currScope, currDynScope, self, temp, blockType));
break;
}
case GET_FIELD: { // NO INTERP
GetFieldInstr gfi = (GetFieldInstr)instr;
IRubyObject object = (IRubyObject)gfi.getSource().retrieve(context, self, currScope, currDynScope, temp);
VariableAccessor a = gfi.getAccessor(object);
Object result = a == null ? null : (IRubyObject)a.get(object);
if (result == null) {
if (context.runtime.isVerbose()) {
context.runtime.getWarnings().warning(IRubyWarnings.ID.IVAR_NOT_INITIALIZED, "instance variable " + gfi.getRef() + " not initialized");
}
result = context.nil;
}
setResult(temp, currDynScope, gfi.getResult(), result);
break;
}
case PUT_FIELD: {
PutFieldInstr putField = (PutFieldInstr) instr;
IRubyObject object = (IRubyObject) putField.getTarget().retrieve(context, self, currScope, currDynScope, temp);
// We store instance variable offsets on the real class, since instance var tables are associated with the
// natural type of an object.
RubyClass clazz = object.getMetaClass().getRealClass();
// FIXME: Should add this as a field for instruction
clazz.getVariableAccessorForWrite(putField.getRef()).set(object,
putField.getValue().retrieve(context, self, currScope, currDynScope, temp));
break;
}
case BUILD_COMPOUND_STRING: case CONST_MISSING:
default:
if (instr.getOperation().opClass == OpClass.BRANCH_OP) {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
} else {
Object result = instr.interpret(context, currScope, currDynScope, self, temp);
if (instr instanceof ResultInstr) {
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), result);
}
}
}
} catch (Throwable t) {
ipc = instr.getRPC();
if (debug) {
Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr);
Interpreter.LOG.info("ipc for rescuer: " + ipc);
}
if (ipc == -1) {
Helpers.throwException(t);
} else {
exception = t;
}
}
}
throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly");
}
@Override
public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, InterpreterContext interpreterContext, RubyModule implClass, String name, IRubyObject arg1, IRubyObject arg2, Block blockArg) {
Instr[] instrs = interpreterContext.getInstructions();
Object[] temp = interpreterContext.allocateTemporaryVariables();
int n = instrs.length;
int ipc = 0;
Object exception = null;
Block.Type blockType = block == null ? null : block.type;
StaticScope currScope = interpreterContext.getStaticScope();
DynamicScope currDynScope = context.getCurrentScope();
// Init profiling this scope
boolean debug = IRRuntimeHelpers.isDebug();
// Enter the looooop!
while (ipc < n) {
Instr instr = instrs[ipc];
ipc++;
Operation operation = instr.getOperation();
if (debug) {
Interpreter.LOG.info("I: {}", instr);
Interpreter.interpInstrsCount++;
}
try {
switch (operation) {
case RECV_PRE_REQD_ARG:{
int argIndex = ((ReceivePreReqdArgInstr) instr).getArgIndex();
IRubyObject arg = null;
switch (argIndex) {
case 0: arg = arg1; break;
case 1: arg = arg2; break;
}
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), arg);
break;
}
case CHECK_ARITY:
break;
case RETURN:
return (IRubyObject) retrieveOp(((ReturnBase) instr).getReturnValue(), context, self, currDynScope, currScope, temp);
case LINE_NUM:
context.setLine(((LineNumberInstr) instr).lineNumber);
break;
case RECV_SELF:
break;
case RECV_JRUBY_EXC:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), exception);
break;
case THROW:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case PUSH_METHOD_FRAME:
context.preMethodFrameOnly(implClass, name, self, blockArg);
// Only the top-level script scope has PRIVATE visibility.
// This is already handled as part of Interpreter.execute above.
// Everything else is PUBLIC by default.
context.setCurrentVisibility(Visibility.PUBLIC);
break;
case POP_METHOD_FRAME:
context.popFrame();
break;
case PUSH_METHOD_BINDING:
// IMPORTANT: Preserve this update of currDynScope.
// This affects execution of all instructions in this scope
// which will now use the updated value of currDynScope.
currDynScope = interpreterContext.newDynamicScope(context);
context.pushScope(currDynScope);
break;
case POP_BINDING:
context.popScope();
break;
case NORESULT_CALL_1O: {
OneOperandArgNoBlockNoResultCallInstr call = (OneOperandArgNoBlockNoResultCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
call.getCallSite().call(context, self, r, o);
break;
}
case SEARCH_CONST: {
SearchConstInstr sci = (SearchConstInstr) instr;
ConstantCache cache = sci.getConstantCache();
Object result;
if (!ConstantCache.isCached(cache)) {
result = sci.cache(context, currScope, currDynScope, self, temp);
} else {
result = cache.value;
}
setResult(temp, currDynScope, sci.getResult(), result);
break;
}
case INHERITANCE_SEARCH_CONST:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(),
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case NORESULT_CALL:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case CALL:
setResult(temp, currDynScope, instr,
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case CALL_0O: {
ZeroOperandArgNoBlockCallInstr call = (ZeroOperandArgNoBlockCallInstr)instr;
IRubyObject r = (IRubyObject)retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r));
break;
}
case CALL_1O: {
OneOperandArgNoBlockCallInstr call = (OneOperandArgNoBlockCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r, o));
break;
}
case B_NIL:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_FALSE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_TRUE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case BNE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case LOAD_IMPLICIT_CLOSURE:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), blockArg);
break;
case COPY: // NO INTERP
setResult(temp, currDynScope, ((CopyInstr) instr).getResult(),
retrieveOp(((CopyInstr) instr).getSource(), context, self, currDynScope, currScope, temp));
break;
case JUMP: // NO INTERP
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
break;
case RUNTIME_HELPER: { // NO INTERP
RuntimeHelperCall rhc = (RuntimeHelperCall)instr;
setResult(temp, currDynScope, rhc.getResult(),
rhc.callHelper(context, currScope, currDynScope, self, temp, blockType));
break;
}
case GET_FIELD: { // NO INTERP
GetFieldInstr gfi = (GetFieldInstr)instr;
IRubyObject object = (IRubyObject)gfi.getSource().retrieve(context, self, currScope, currDynScope, temp);
VariableAccessor a = gfi.getAccessor(object);
Object result = a == null ? null : (IRubyObject)a.get(object);
if (result == null) {
if (context.runtime.isVerbose()) {
context.runtime.getWarnings().warning(IRubyWarnings.ID.IVAR_NOT_INITIALIZED, "instance variable " + gfi.getRef() + " not initialized");
}
result = context.nil;
}
setResult(temp, currDynScope, gfi.getResult(), result);
break;
}
case PUT_FIELD: {
PutFieldInstr putField = (PutFieldInstr) instr;
IRubyObject object = (IRubyObject) putField.getTarget().retrieve(context, self, currScope, currDynScope, temp);
// We store instance variable offsets on the real class, since instance var tables are associated with the
// natural type of an object.
RubyClass clazz = object.getMetaClass().getRealClass();
// FIXME: Should add this as a field for instruction
clazz.getVariableAccessorForWrite(putField.getRef()).set(object,
putField.getValue().retrieve(context, self, currScope, currDynScope, temp));
break;
}
case BUILD_COMPOUND_STRING: case CONST_MISSING:
default:
if (instr.getOperation().opClass == OpClass.BRANCH_OP) {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
} else {
Object result = instr.interpret(context, currScope, currDynScope, self, temp);
if (instr instanceof ResultInstr) {
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), result);
}
}
}
} catch (Throwable t) {
ipc = instr.getRPC();
if (debug) {
Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr);
Interpreter.LOG.info("ipc for rescuer: " + ipc);
}
if (ipc == -1) {
Helpers.throwException(t);
} else {
exception = t;
}
}
}
throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly");
}
@Override
public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, InterpreterContext interpreterContext, RubyModule implClass, String name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, Block blockArg) {
Instr[] instrs = interpreterContext.getInstructions();
Object[] temp = interpreterContext.allocateTemporaryVariables();
int n = instrs.length;
int ipc = 0;
Object exception = null;
Block.Type blockType = block == null ? null : block.type;
StaticScope currScope = interpreterContext.getStaticScope();
DynamicScope currDynScope = context.getCurrentScope();
// Init profiling this scope
boolean debug = IRRuntimeHelpers.isDebug();
// Enter the looooop!
while (ipc < n) {
Instr instr = instrs[ipc];
ipc++;
Operation operation = instr.getOperation();
if (debug) {
Interpreter.LOG.info("I: {}", instr);
Interpreter.interpInstrsCount++;
}
try {
switch (operation) {
case RECV_PRE_REQD_ARG:{
int argIndex = ((ReceivePreReqdArgInstr) instr).getArgIndex();
IRubyObject arg = null;
switch (argIndex) {
case 0: arg = arg1; break;
case 1: arg = arg2; break;
case 2: arg = arg3; break;
}
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), arg);
break;
}
case CHECK_ARITY:
break;
case RETURN:
return (IRubyObject) retrieveOp(((ReturnBase) instr).getReturnValue(), context, self, currDynScope, currScope, temp);
case LINE_NUM:
context.setLine(((LineNumberInstr) instr).lineNumber);
break;
case RECV_SELF:
break;
case RECV_JRUBY_EXC:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), exception);
break;
case THROW:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case PUSH_METHOD_FRAME:
context.preMethodFrameOnly(implClass, name, self, blockArg);
// Only the top-level script scope has PRIVATE visibility.
// This is already handled as part of Interpreter.execute above.
// Everything else is PUBLIC by default.
context.setCurrentVisibility(Visibility.PUBLIC);
break;
case POP_METHOD_FRAME:
context.popFrame();
break;
case PUSH_METHOD_BINDING:
// IMPORTANT: Preserve this update of currDynScope.
// This affects execution of all instructions in this scope
// which will now use the updated value of currDynScope.
currDynScope = interpreterContext.newDynamicScope(context);
context.pushScope(currDynScope);
break;
case POP_BINDING:
context.popScope();
break;
case NORESULT_CALL_1O: {
OneOperandArgNoBlockNoResultCallInstr call = (OneOperandArgNoBlockNoResultCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
call.getCallSite().call(context, self, r, o);
break;
}
case SEARCH_CONST: {
SearchConstInstr sci = (SearchConstInstr) instr;
ConstantCache cache = sci.getConstantCache();
Object result;
if (!ConstantCache.isCached(cache)) {
result = sci.cache(context, currScope, currDynScope, self, temp);
} else {
result = cache.value;
}
setResult(temp, currDynScope, sci.getResult(), result);
break;
}
case INHERITANCE_SEARCH_CONST:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(),
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case NORESULT_CALL:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case CALL:
setResult(temp, currDynScope, instr,
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case CALL_0O: {
ZeroOperandArgNoBlockCallInstr call = (ZeroOperandArgNoBlockCallInstr)instr;
IRubyObject r = (IRubyObject)retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r));
break;
}
case CALL_1O: {
OneOperandArgNoBlockCallInstr call = (OneOperandArgNoBlockCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r, o));
break;
}
case B_NIL:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_FALSE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_TRUE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case BNE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case LOAD_IMPLICIT_CLOSURE:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), blockArg);
break;
case COPY: // NO INTERP
setResult(temp, currDynScope, ((CopyInstr) instr).getResult(),
retrieveOp(((CopyInstr) instr).getSource(), context, self, currDynScope, currScope, temp));
break;
case JUMP: // NO INTERP
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
break;
case RUNTIME_HELPER: { // NO INTERP
RuntimeHelperCall rhc = (RuntimeHelperCall)instr;
setResult(temp, currDynScope, rhc.getResult(),
rhc.callHelper(context, currScope, currDynScope, self, temp, blockType));
break;
}
case GET_FIELD: { // NO INTERP
GetFieldInstr gfi = (GetFieldInstr)instr;
IRubyObject object = (IRubyObject)gfi.getSource().retrieve(context, self, currScope, currDynScope, temp);
VariableAccessor a = gfi.getAccessor(object);
Object result = a == null ? null : (IRubyObject)a.get(object);
if (result == null) {
if (context.runtime.isVerbose()) {
context.runtime.getWarnings().warning(IRubyWarnings.ID.IVAR_NOT_INITIALIZED, "instance variable " + gfi.getRef() + " not initialized");
}
result = context.nil;
}
setResult(temp, currDynScope, gfi.getResult(), result);
break;
}
case PUT_FIELD: {
PutFieldInstr putField = (PutFieldInstr) instr;
IRubyObject object = (IRubyObject) putField.getTarget().retrieve(context, self, currScope, currDynScope, temp);
// We store instance variable offsets on the real class, since instance var tables are associated with the
// natural type of an object.
RubyClass clazz = object.getMetaClass().getRealClass();
// FIXME: Should add this as a field for instruction
clazz.getVariableAccessorForWrite(putField.getRef()).set(object,
putField.getValue().retrieve(context, self, currScope, currDynScope, temp));
break;
}
case BUILD_COMPOUND_STRING: case CONST_MISSING:
default:
if (instr.getOperation().opClass == OpClass.BRANCH_OP) {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
} else {
Object result = instr.interpret(context, currScope, currDynScope, self, temp);
if (instr instanceof ResultInstr) {
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), result);
}
}
}
} catch (Throwable t) {
ipc = instr.getRPC();
if (debug) {
Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr);
Interpreter.LOG.info("ipc for rescuer: " + ipc);
}
if (ipc == -1) {
Helpers.throwException(t);
} else {
exception = t;
}
}
}
throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly");
}
@Override
public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, InterpreterContext interpreterContext, RubyModule implClass, String name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, IRubyObject arg4, Block blockArg) {
Instr[] instrs = interpreterContext.getInstructions();
Object[] temp = interpreterContext.allocateTemporaryVariables();
int n = instrs.length;
int ipc = 0;
Object exception = null;
Block.Type blockType = block == null ? null : block.type;
StaticScope currScope = interpreterContext.getStaticScope();
DynamicScope currDynScope = context.getCurrentScope();
// Init profiling this scope
boolean debug = IRRuntimeHelpers.isDebug();
// Enter the looooop!
while (ipc < n) {
Instr instr = instrs[ipc];
ipc++;
Operation operation = instr.getOperation();
if (debug) {
Interpreter.LOG.info("I: {}", instr);
Interpreter.interpInstrsCount++;
}
try {
switch (operation) {
case RECV_PRE_REQD_ARG: {
int argIndex = ((ReceivePreReqdArgInstr) instr).getArgIndex();
IRubyObject arg = null;
switch (argIndex) {
case 0: arg = arg1; break;
case 1: arg = arg2; break;
case 2: arg = arg3; break;
case 3: arg = arg4; break;
}
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), arg);
break;
}
case CHECK_ARITY:
break;
case RETURN:
return (IRubyObject) retrieveOp(((ReturnBase) instr).getReturnValue(), context, self, currDynScope, currScope, temp);
case LINE_NUM:
context.setLine(((LineNumberInstr) instr).lineNumber);
break;
case RECV_SELF:
break;
case RECV_JRUBY_EXC:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), exception);
break;
case THROW:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case PUSH_METHOD_FRAME:
context.preMethodFrameOnly(implClass, name, self, blockArg);
// Only the top-level script scope has PRIVATE visibility.
// This is already handled as part of Interpreter.execute above.
// Everything else is PUBLIC by default.
context.setCurrentVisibility(Visibility.PUBLIC);
break;
case POP_METHOD_FRAME:
context.popFrame();
break;
case PUSH_METHOD_BINDING:
// IMPORTANT: Preserve this update of currDynScope.
// This affects execution of all instructions in this scope
// which will now use the updated value of currDynScope.
currDynScope = interpreterContext.newDynamicScope(context);
context.pushScope(currDynScope);
break;
case POP_BINDING:
context.popScope();
break;
case NORESULT_CALL_1O: {
OneOperandArgNoBlockNoResultCallInstr call = (OneOperandArgNoBlockNoResultCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
call.getCallSite().call(context, self, r, o);
break;
}
case SEARCH_CONST: {
SearchConstInstr sci = (SearchConstInstr) instr;
ConstantCache cache = sci.getConstantCache();
Object result;
if (!ConstantCache.isCached(cache)) {
result = sci.cache(context, currScope, currDynScope, self, temp);
} else {
result = cache.value;
}
setResult(temp, currDynScope, sci.getResult(), result);
break;
}
case INHERITANCE_SEARCH_CONST:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(),
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case NORESULT_CALL:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case CALL:
setResult(temp, currDynScope, instr,
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case CALL_0O: {
ZeroOperandArgNoBlockCallInstr call = (ZeroOperandArgNoBlockCallInstr)instr;
IRubyObject r = (IRubyObject)retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r));
break;
}
case CALL_1O: {
OneOperandArgNoBlockCallInstr call = (OneOperandArgNoBlockCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r, o));
break;
}
case B_NIL:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_FALSE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_TRUE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case BNE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case LOAD_IMPLICIT_CLOSURE:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), blockArg);
break;
case COPY: // NO INTERP
setResult(temp, currDynScope, ((CopyInstr) instr).getResult(),
retrieveOp(((CopyInstr) instr).getSource(), context, self, currDynScope, currScope, temp));
break;
case JUMP: // NO INTERP
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
break;
case RUNTIME_HELPER: { // NO INTERP
RuntimeHelperCall rhc = (RuntimeHelperCall)instr;
setResult(temp, currDynScope, rhc.getResult(),
rhc.callHelper(context, currScope, currDynScope, self, temp, blockType));
break;
}
case GET_FIELD: { // NO INTERP
GetFieldInstr gfi = (GetFieldInstr)instr;
IRubyObject object = (IRubyObject)gfi.getSource().retrieve(context, self, currScope, currDynScope, temp);
VariableAccessor a = gfi.getAccessor(object);
Object result = a == null ? null : (IRubyObject)a.get(object);
if (result == null) {
if (context.runtime.isVerbose()) {
context.runtime.getWarnings().warning(IRubyWarnings.ID.IVAR_NOT_INITIALIZED, "instance variable " + gfi.getRef() + " not initialized");
}
result = context.nil;
}
setResult(temp, currDynScope, gfi.getResult(), result);
break;
}
case PUT_FIELD: {
PutFieldInstr putField = (PutFieldInstr) instr;
IRubyObject object = (IRubyObject) putField.getTarget().retrieve(context, self, currScope, currDynScope, temp);
// We store instance variable offsets on the real class, since instance var tables are associated with the
// natural type of an object.
RubyClass clazz = object.getMetaClass().getRealClass();
// FIXME: Should add this as a field for instruction
clazz.getVariableAccessorForWrite(putField.getRef()).set(object,
putField.getValue().retrieve(context, self, currScope, currDynScope, temp));
break;
}
case BUILD_COMPOUND_STRING: case CONST_MISSING:
default:
if (instr.getOperation().opClass == OpClass.BRANCH_OP) {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
} else {
Object result = instr.interpret(context, currScope, currDynScope, self, temp);
if (instr instanceof ResultInstr) {
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), result);
}
}
}
} catch (Throwable t) {
ipc = instr.getRPC();
if (debug) {
Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr);
Interpreter.LOG.info("ipc for rescuer: " + ipc);
}
if (ipc == -1) {
Helpers.throwException(t);
} else {
exception = t;
}
}
}
throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly");
}
@Override
public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, InterpreterContext interpreterContext, RubyModule implClass, String name, IRubyObject[] args, Block blockArg) {
Instr[] instrs = interpreterContext.getInstructions();
Object[] temp = interpreterContext.allocateTemporaryVariables();
int n = instrs.length;
int ipc = 0;
Object exception = null;
Block.Type blockType = block == null ? null : block.type;
StaticScope currScope = interpreterContext.getStaticScope();
DynamicScope currDynScope = context.getCurrentScope();
// Init profiling this scope
boolean debug = IRRuntimeHelpers.isDebug();
// Enter the looooop!
while (ipc < n) {
Instr instr = instrs[ipc];
ipc++;
Operation operation = instr.getOperation();
if (debug) {
Interpreter.LOG.info("I: {}", instr);
Interpreter.interpInstrsCount++;
}
try {
switch (operation) {
case RECV_PRE_REQD_ARG:
int argIndex = ((ReceivePreReqdArgInstr)instr).getArgIndex();
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), IRRuntimeHelpers.getPreArgSafe(context, args, argIndex));
break;
case CHECK_ARITY:
break;
case RETURN:
return (IRubyObject) retrieveOp(((ReturnBase) instr).getReturnValue(), context, self, currDynScope, currScope, temp);
case LINE_NUM:
context.setLine(((LineNumberInstr) instr).lineNumber);
break;
case RECV_SELF:
break;
case RECV_JRUBY_EXC:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), exception);
break;
case THROW:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case PUSH_METHOD_FRAME:
context.preMethodFrameOnly(implClass, name, self, blockArg);
// Only the top-level script scope has PRIVATE visibility.
// This is already handled as part of Interpreter.execute above.
// Everything else is PUBLIC by default.
context.setCurrentVisibility(Visibility.PUBLIC);
break;
case POP_METHOD_FRAME:
context.popFrame();
break;
case PUSH_METHOD_BINDING:
// IMPORTANT: Preserve this update of currDynScope.
// This affects execution of all instructions in this scope
// which will now use the updated value of currDynScope.
currDynScope = interpreterContext.newDynamicScope(context);
context.pushScope(currDynScope);
break;
case POP_BINDING:
context.popScope();
break;
case NORESULT_CALL_1O: {
OneOperandArgNoBlockNoResultCallInstr call = (OneOperandArgNoBlockNoResultCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
call.getCallSite().call(context, self, r, o);
break;
}
case SEARCH_CONST: {
SearchConstInstr sci = (SearchConstInstr) instr;
ConstantCache cache = sci.getConstantCache();
Object result;
if (!ConstantCache.isCached(cache)) {
result = sci.cache(context, currScope, currDynScope, self, temp);
} else {
result = cache.value;
}
setResult(temp, currDynScope, sci.getResult(), result);
break;
}
case INHERITANCE_SEARCH_CONST:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(),
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case NORESULT_CALL:
instr.interpret(context, currScope, currDynScope, self, temp);
break;
case CALL:
setResult(temp, currDynScope, instr,
instr.interpret(context, currScope, currDynScope, self, temp));
break;
case CALL_0O: {
ZeroOperandArgNoBlockCallInstr call = (ZeroOperandArgNoBlockCallInstr)instr;
IRubyObject r = (IRubyObject)retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r));
break;
}
case CALL_1O: {
OneOperandArgNoBlockCallInstr call = (OneOperandArgNoBlockCallInstr) instr;
IRubyObject r = (IRubyObject) retrieveOp(call.getReceiver(), context, self, currDynScope, currScope, temp);
IRubyObject o = (IRubyObject) call.getArg1().retrieve(context, self, currScope, currDynScope, temp);
setResult(temp, currDynScope, call.getResult(), call.getCallSite().call(context, self, r, o));
break;
}
case B_NIL:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_FALSE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case B_TRUE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case BNE:
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
break;
case LOAD_IMPLICIT_CLOSURE:
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), blockArg);
break;
case COPY: // NO INTERP
setResult(temp, currDynScope, ((CopyInstr) instr).getResult(),
retrieveOp(((CopyInstr) instr).getSource(), context, self, currDynScope, currScope, temp));
break;
case JUMP: // NO INTERP
ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC();
break;
case RUNTIME_HELPER: { // NO INTERP
RuntimeHelperCall rhc = (RuntimeHelperCall)instr;
setResult(temp, currDynScope, rhc.getResult(),
rhc.callHelper(context, currScope, currDynScope, self, temp, blockType));
break;
}
case GET_FIELD: { // NO INTERP
GetFieldInstr gfi = (GetFieldInstr)instr;
IRubyObject object = (IRubyObject)gfi.getSource().retrieve(context, self, currScope, currDynScope, temp);
VariableAccessor a = gfi.getAccessor(object);
Object result = a == null ? null : (IRubyObject)a.get(object);
if (result == null) {
if (context.runtime.isVerbose()) {
context.runtime.getWarnings().warning(IRubyWarnings.ID.IVAR_NOT_INITIALIZED, "instance variable " + gfi.getRef() + " not initialized");
}
result = context.nil;
}
setResult(temp, currDynScope, gfi.getResult(), result);
break;
}
case PUT_FIELD: {
PutFieldInstr putField = (PutFieldInstr) instr;
IRubyObject object = (IRubyObject) putField.getTarget().retrieve(context, self, currScope, currDynScope, temp);
// We store instance variable offsets on the real class, since instance var tables are associated with the
// natural type of an object.
RubyClass clazz = object.getMetaClass().getRealClass();
// FIXME: Should add this as a field for instruction
clazz.getVariableAccessorForWrite(putField.getRef()).set(object,
putField.getValue().retrieve(context, self, currScope, currDynScope, temp));
break;
}
case BUILD_COMPOUND_STRING: case CONST_MISSING:
default:
if (instr.getOperation().opClass == OpClass.BRANCH_OP) {
ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
} else {
Object result = instr.interpret(context, currScope, currDynScope, self, temp);
if (instr instanceof ResultInstr) {
setResult(temp, currDynScope, ((ResultInstr) instr).getResult(), result);
}
}
}
} catch (Throwable t) {
ipc = instr.getRPC();
if (debug) {
Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr);
Interpreter.LOG.info("ipc for rescuer: " + ipc);
}
if (ipc == -1) {
Helpers.throwException(t);
} else {
exception = t;
}
}
}
throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy