Skip to content

Commit

Permalink
Use new ScopeType.isBlock() when we want to know if it is what we thi…
Browse files Browse the repository at this point in the history
…nk of as

a traditional Ruby block vs what IR Scope types we found convenient to
also represent as closures (like eval).
Do not add LJE checks to For or Evals
Simplify checkForLJE a bit more.
enebo committed Mar 23, 2017
1 parent 023f6a4 commit 8253c47
Showing 3 changed files with 29 additions and 18 deletions.
14 changes: 8 additions & 6 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -3691,12 +3691,14 @@ public Operand buildReturn(ReturnNode returnNode) {
Operand retVal = build(returnNode.getValueNode());

if (scope instanceof IRClosure) {
// If 'm' is a block scope, a return returns from the closest enclosing method.
// If this happens to be a module body, the runtime throws a local jump error if the
// closure is a proc. If the closure is a lambda, then this becomes a normal return.
boolean maybeLambda = scope.getNearestMethod() == null;
addInstr(new CheckForLJEInstr(maybeLambda));
addInstr(new NonlocalReturnInstr(retVal, maybeLambda ? "--none--" : scope.getNearestMethod().getName()));
// Closures return behavior has several cases (which depend on runtime state):
// 1. closure in method (return). !method (error) except if in define_method (return)
// 2. lambda (return) [dynamic] // FIXME: I believe ->() can be static and omit LJE check.
// 3. migrated closure (LJE) [dynamic]
// 4. eval/for (return) [static]
boolean notDefinedWithinMethod = scope.getNearestMethod() == null;
if (!(scope instanceof IREvalScript) && !(scope instanceof IRFor)) addInstr(new CheckForLJEInstr(notDefinedWithinMethod));
addInstr(new NonlocalReturnInstr(retVal, notDefinedWithinMethod ? "--none--" : scope.getNearestMethod().getName()));
} else if (scope.isModuleBody()) {
IRMethod sm = scope.getNearestMethod();

5 changes: 5 additions & 0 deletions core/src/main/java/org/jruby/ir/IRScopeType.java
Original file line number Diff line number Diff line change
@@ -12,6 +12,11 @@ public static IRScopeType fromOrdinal(int ordinal) {
return VALUES[ordinal];
}

// Is this a block type of closure vs an instance of IRClosure (which is also a for/eval internally)?
public boolean isBlock() {
return this == CLOSURE;
}

public boolean isEval() {
return this == EVAL_SCRIPT;
}
28 changes: 16 additions & 12 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -79,24 +79,28 @@ public static boolean inProc(Block.Type blockType) {
return blockType == Block.Type.PROC;
}

public static void checkForLJE(ThreadContext context, DynamicScope dynScope, boolean maybeLambda, Block.Type blockType) {
if (IRRuntimeHelpers.inLambda(blockType)) return; // break/return in lambda will return from lambda (no LJE possible).
// FIXME: ENEBO: If we inline this instr then dynScope will be for the inlined dynscope and that scope could be many things.
// CheckForLJEInstr.clone should convert this as appropriate based on what it is being inlined into.
public static void checkForLJE(ThreadContext context, DynamicScope dynScope, boolean notDefinedWithinMethod, Block.Type blockType) {
if (inLambda(blockType)) return; // break/return in lambda unconditionally a return.

StaticScope entryScope = dynScope.getStaticScope();
IRScopeType entryScopeType = entryScope.getScopeType();
// Is our proc in something unreturnable (e.g. module/class) or has it migrated (lexical parent method not in stack any more)?
if (notDefinedWithinMethod || !context.scopeExistsOnCallStack(getContainingMethodsDynamicScope(dynScope))) {
throw IRException.RETURN_LocalJumpError.getException(context.runtime);
}
}

// Finds dynamic method this proc exists in or null if it is not within one.
private static DynamicScope getContainingMethodsDynamicScope(DynamicScope dynScope) {
for (; dynScope != null; dynScope = dynScope.getParentScope()) {
StaticScope currentScope = dynScope.getStaticScope();
IRScopeType currentScopeType = currentScope.getScopeType();
StaticScope scope = dynScope.getStaticScope();
IRScopeType scopeType = scope.getScopeType();

if (currentScopeType.isMethodType()) break;
if (currentScope.isArgumentScope() && currentScopeType.isClosureType() && currentScopeType != IRScopeType.EVAL_SCRIPT) return;
// We hit a method boundary (actual method or a define_method closure).
if (scopeType.isMethodType() || scopeType.isBlock() && scope.isArgumentScope()) return dynScope;
}

if (entryScopeType.isClosureType() && entryScopeType != IRScopeType.EVAL_SCRIPT && (maybeLambda || !context.scopeExistsOnCallStack(dynScope))) {
// Cannot return from the call that we have long since exited.
throw IRException.RETURN_LocalJumpError.getException(context.runtime);
}
return null;
}

/*

0 comments on commit 8253c47

Please sign in to comment.