@Override public IRubyObject interpret( Ruby runtime, ThreadContext context, IRubyObject self, Block aBlock) { // Serialization killed our dynamic scope. We can just create an empty one // since serialization cannot serialize an eval (which is the only thing // which is capable of having a non-empty dynamic scope). if (scope == null) { scope = DynamicScope.newDynamicScope(staticScope); } StaticScope theStaticScope = scope.getStaticScope(); // Each root node has a top-level scope that we need to push context.preScopedBody(scope); if (theStaticScope.getModule() == null) { theStaticScope.setModule(runtime.getObject()); } try { return bodyNode.interpret(runtime, context, self, aBlock); } finally { context.postScopedBody(); } }
/** * Evaluate the given string. * * @param context the current thread's context * @param self the self to evaluate under * @param src The string containing the text to be evaluated * @param file The filename to use when reporting errors during the evaluation * @param lineNumber that the eval supposedly starts from * @return An IRubyObject result from the evaluation */ public static IRubyObject evalSimple( ThreadContext context, IRubyObject self, RubyString src, String file, int lineNumber) { // this is ensured by the callers assert file != null; Ruby runtime = src.getRuntime(); // no binding, just eval in "current" frame (caller's frame) RubyString source = src.convertToString(); DynamicScope evalScope = context.getCurrentScope().getEvalScope(runtime); evalScope.getStaticScope().determineModule(); try { Node node = runtime.parseEval(source.getByteList(), file, evalScope, lineNumber); if (runtime.getInstanceConfig().getCompileMode() == CompileMode.OFFIR) { // SSS FIXME: AST interpreter passed both a runtime (which comes from the source string) // and the thread-context rather than fetch one from the other. Why is that? return Interpreter.interpretSimpleEval(runtime, file, lineNumber, "(eval)", node, self); } else { return INTERPRET_EVAL( runtime, context, file, lineNumber, node, "(eval)", self, Block.NULL_BLOCK); } } catch (JumpException.BreakJump bj) { throw runtime.newLocalJumpError( RubyLocalJumpError.Reason.BREAK, (IRubyObject) bj.getValue(), "unexpected break"); } catch (StackOverflowError soe) { throw runtime.newSystemStackError("stack level too deep", soe); } }
/** * Evaluate the given string under the specified binding object. If the binding is not a Proc or * Binding object (RubyProc or RubyBinding) throw an appropriate type error. * * @param context the thread context for the current thread * @param self the self against which eval was called; used as self in the eval in 1.9 mode * @param src The string containing the text to be evaluated * @param binding The binding object under which to perform the evaluation * @return An IRubyObject result from the evaluation */ public static IRubyObject evalWithBinding( ThreadContext context, IRubyObject self, IRubyObject src, Binding binding) { Ruby runtime = src.getRuntime(); DynamicScope evalScope; if (runtime.is1_9()) { // in 1.9, eval scopes are local to the binding evalScope = binding.getEvalScope(runtime); } else { // in 1.8, eval scopes are local to the parent scope evalScope = binding.getDynamicScope().getEvalScope(runtime); } // FIXME: This determine module is in a strange location and should somehow be in block evalScope.getStaticScope().determineModule(); Frame lastFrame = context.preEvalWithBinding(binding); try { // Binding provided for scope, use it RubyString source = src.convertToString(); Node node = runtime.parseEval(source.getByteList(), binding.getFile(), evalScope, binding.getLine()); Block block = binding.getFrame().getBlock(); if (runtime.getInstanceConfig().getCompileMode() == CompileMode.OFFIR) { // SSS FIXME: AST interpreter passed both a runtime (which comes from the source string) // and the thread-context rather than fetch one from the other. Why is that? return Interpreter.interpretBindingEval( runtime, binding.getFile(), binding.getLine(), binding.getMethod(), node, self, block); } else { return INTERPRET_EVAL( runtime, context, binding.getFile(), binding.getLine(), node, binding.getMethod(), self, block); } } catch (JumpException.BreakJump bj) { throw runtime.newLocalJumpError( RubyLocalJumpError.Reason.BREAK, (IRubyObject) bj.getValue(), "unexpected break"); } catch (JumpException.RedoJump rj) { throw runtime.newLocalJumpError( RubyLocalJumpError.Reason.REDO, (IRubyObject) rj.getValue(), "unexpected redo"); } catch (StackOverflowError soe) { throw runtime.newSystemStackError("stack level too deep", soe); } finally { context.postEvalWithBinding(binding, lastFrame); } }
@Override public Object interpret( ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) { int i = 0; while (!currDynScope.getStaticScope().isArgumentScope()) { currDynScope = currDynScope.getParentScope(); i++; } return context.runtime.newFixnum(i); }
private static void setResult( Object[] temp, DynamicScope currDynScope, Variable resultVar, Object result) { if (resultVar instanceof TemporaryVariable) { temp[((TemporaryVariable) resultVar).offset] = result; } else { LocalVariable lv = (LocalVariable) resultVar; currDynScope.setValue((IRubyObject) result, lv.getLocation(), lv.getScopeDepth()); } }
public RootNode(ISourcePosition position, DynamicScope scope, Node bodyNode) { super(position); assert bodyNode != null : "bodyNode is not null"; this.scope = scope; this.staticScope = scope.getStaticScope(); this.bodyNode = bodyNode; }
protected void pre( InterpreterContext ic, ThreadContext context, IRubyObject self, String name, Block block, RubyModule implClass) { // update call stacks (push: frame, class, scope, etc.) context.preMethodFrameOnly(implClass, name, self, block); if (ic.pushNewDynScope()) { context.pushScope(DynamicScope.newDynamicScope(ic.getStaticScope())); } }
private static Object retrieveOp( Operand r, ThreadContext context, IRubyObject self, DynamicScope currDynScope, Object[] temp) { Object res; if (r instanceof Self) { return self; } else if (r instanceof TemporaryVariable) { res = temp[((TemporaryVariable) r).offset]; return res == null ? context.nil : res; } else if (r instanceof LocalVariable) { LocalVariable lv = (LocalVariable) r; res = currDynScope.getValue(lv.getLocation(), lv.getScopeDepth()); return res == null ? context.nil : res; } else { return r.retrieve(context, self, currDynScope, temp); } }
@Override public Object retrieve( ThreadContext context, IRubyObject self, DynamicScope currDynScope, Object[] temp) { return currDynScope.getStaticScope(); }
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; }