Skip to content

Commit

Permalink
Showing 11 changed files with 323 additions and 183 deletions.
Original file line number Diff line number Diff line change
@@ -34,90 +34,4 @@ public static PrepareBlockArgsInstr decode(IRReaderDecoder d) {
public void visit(IRVisitor visitor) {
visitor.PrepareBlockArgsInstr(this);
}

protected IRubyObject[] toAry(ThreadContext context, IRubyObject[] args) {
if (args.length == 1 && args[0].respondsTo("to_ary")) {
IRubyObject newAry = Helpers.aryToAry(args[0]);
if (newAry.isNil()) {
args = new IRubyObject[] { args[0] };
} else if (newAry instanceof RubyArray) {
args = ((RubyArray) newAry).toJavaArray();
} else {
throw context.runtime.newTypeError(args[0].getType().getName() + "#to_ary should return Array");
}
}
return args;
}

protected IRubyObject[] prepareProcArgs(ThreadContext context, Block b, IRubyObject[] args) {
if (args.length == 1) {
int arityValue = b.getBody().getSignature().arityValue();
return IRRuntimeHelpers.convertValueIntoArgArray(context, args[0], arityValue, b.type == Block.Type.NORMAL && args[0] instanceof RubyArray);
} else {
return args;
}
}

public IRubyObject[] prepareBlockArgs(ThreadContext context, Block b, IRubyObject[] args) {
// This is the placeholder for scenarios
// not handled by specialized instructions.
if (args == null) {
return IRubyObject.NULL_ARRAY;
}

boolean isProcCall = context.getCurrentBlockType() == Block.Type.PROC;
if (isProcCall) {
return prepareProcArgs(context, b, args);
}

boolean isLambda = b.type == Block.Type.LAMBDA;
if (isLambda && isProcCall) {
return args;
}

BlockBody body = b.getBody();
Signature sig = body.getSignature();

// blockArity == 0 and 1 have been handled in the specialized instructions
// This test is when we only have opt / rest arg (either keyword or non-keyword)
// but zero required args.
if (sig.arityValue() == -1) {
return args;
}

// We get here only when we have both required and optional/rest args
// (keyword or non-keyword in either case).
// So, convert a single value to an array if possible.
args = toAry(context, args);

// Nothing more to do for lambdas
if (isLambda) {
return args;
}

// Deal with keyword args that needs special handling
int needsKwargs = sig.hasKwargs() ? 1 - sig.getRequiredKeywordForArityCount() : 0;
int required = sig.required();
int actual = args.length;
if (needsKwargs == 0 || required > actual) {
// Nothing to do if we have fewer args in args than what is required
// The required arg instructions will return nil in those cases.
return args;
}

if (sig.isFixed() && required > 0 && required+needsKwargs != actual) {
// Make sure we have a ruby-hash
IRubyObject[] newArgs = Arrays.copyOf(args, required+needsKwargs);
if (actual < required+needsKwargs) {
// Not enough args and we need an empty {} for kwargs processing.
newArgs[newArgs.length - 1] = RubyHash.newHash(context.runtime);
} else {
// We have more args than we need and kwargs is always the last arg.
newArgs[newArgs.length - 1] = args[args.length - 1];
}
args = newArgs;
}

return args;
}
}
Original file line number Diff line number Diff line change
@@ -23,36 +23,6 @@ public static PrepareFixedBlockArgsInstr decode(IRReaderDecoder d) {
return new PrepareFixedBlockArgsInstr();
}

public IRubyObject[] prepareBlockArgs(ThreadContext context, Block b, IRubyObject[] args) {
if (args == null) {
return IRubyObject.NULL_ARRAY;
}

boolean isProcCall = context.getCurrentBlockType() == Block.Type.PROC;
if (isProcCall) {
return prepareProcArgs(context, b, args);
}

boolean isLambda = b.type == Block.Type.LAMBDA;
if (isLambda && isProcCall) {
return args;
}

// SSS FIXME: This check here is not required as long as
// the single-instruction cases always uses PreapreSingleBlockArgInstr
// But, including this here for robustness for now.
if (b.getBody().getSignature().arityValue() == 1) {
return args;
}

// Since we have more than 1 required arg,
// convert a single value to an array if possible.
args = toAry(context, args);

// If there are insufficient args, ReceivePreReqdInstr will return nil
return args;
}

@Override
public void visit(IRVisitor visitor) {
visitor.PrepareFixedBlockArgsInstr(this);
Original file line number Diff line number Diff line change
@@ -22,25 +22,6 @@ public Instr clone(CloneInfo ii) {
public static PrepareSingleBlockArgInstr decode(IRReaderDecoder d) {
return new PrepareSingleBlockArgInstr();
}

public IRubyObject[] prepareBlockArgs(ThreadContext context, Block b, IRubyObject[] args) {
if (args == null) args = IRubyObject.NULL_ARRAY;

// Deal with proc calls
if (context.getCurrentBlockType() == Block.Type.PROC) {
if (args.length == 0) {
args = context.runtime.getSingleNilArray();
} else if (args.length == 1) {
args = prepareProcArgs(context, b, args);
} else {
args = new IRubyObject[] { args[0] };
}
}

// Nothing more to do! Hurray!
// If there are insufficient args, ReceivePreReqdInstr will return nil
return args;
}

@Override
public void visit(IRVisitor visitor) {
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ public InterpreterContext(IRScope scope, List<Instr> instructions) {
this.temporaryVariablecount = scope.getTemporaryVariablesCount();
this.instructions = instructions != null ? prepareBuildInstructions(instructions) : null;
this.hasExplicitCallProtocol = scope.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
// FIXME: Centralize this out of InterpreterContext
this.reuseParentDynScope = scope.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
this.pushNewDynScope = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !reuseParentDynScope;
this.popDynScope = this.pushNewDynScope || this.reuseParentDynScope;
36 changes: 7 additions & 29 deletions core/src/main/java/org/jruby/ir/interpreter/InterpreterEngine.java
Original file line number Diff line number Diff line change
@@ -17,7 +17,6 @@
import org.jruby.ir.instructions.LineNumberInstr;
import org.jruby.ir.instructions.NonlocalReturnInstr;
import org.jruby.ir.instructions.PopBlockFrameInstr;
import org.jruby.ir.instructions.PrepareBlockArgsInstr;
import org.jruby.ir.instructions.PushBlockFrameInstr;
import org.jruby.ir.instructions.ReceiveArgBase;
import org.jruby.ir.instructions.ReceivePostReqdArgInstr;
@@ -58,8 +57,6 @@
import org.jruby.ir.operands.Variable;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.EvalType;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Frame;
@@ -106,17 +103,6 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
return interpret(context, block, self, interpreterContext, implClass, name, new IRubyObject[] {arg1, arg2, arg3, arg4}, blockArg);
}

private DynamicScope getNewBlockScope(ThreadContext context, Block block, InterpreterContext interpreterContext) {
DynamicScope newScope = block.getBinding().getDynamicScope();
if (interpreterContext.pushNewDynScope()) return block.allocScope(newScope);

// Reuse! We can avoid the push only if surrounding vars aren't referenced!
if (interpreterContext.reuseParentDynScope()) return newScope;

// No change
return null;
}

public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self,
InterpreterContext interpreterContext, RubyModule implClass,
String name, IRubyObject[] args, Block blockArg) {
@@ -187,27 +173,19 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
context.pushScope(currDynScope);
break;
case PUSH_BLOCK_BINDING:
DynamicScope newScope = getNewBlockScope(context, block, interpreterContext);
if (newScope != null) {
currDynScope = newScope;
context.pushScope(currDynScope);
}
currDynScope = IRRuntimeHelpers.pushBlockDynamicScopeIfNeeded(context, block, interpreterContext.pushNewDynScope(), interpreterContext.reuseParentDynScope());
break;
case UPDATE_BLOCK_STATE:
if (self == null || block.getEvalType() == EvalType.BINDING_EVAL) {
// Update self to the binding's self
Binding b = block.getBinding();
self = b.getSelf();
b.getFrame().setSelf(self);
}
// Clear block's eval type
block.setEvalType(EvalType.NONE);
self = IRRuntimeHelpers.updateBlockState(block, self);
break;
case PREPARE_SINGLE_BLOCK_ARG:
args = IRRuntimeHelpers.prepareSingleBlockArgs(context, block, args);
break;
case PREPARE_FIXED_BLOCK_ARGS:
args = IRRuntimeHelpers.prepareFixedBlockArgs(context, block, args);
break;
case PREPARE_BLOCK_ARGS:
args = ((PrepareBlockArgsInstr)instr).prepareBlockArgs(context, block, args);
if (block.type == Block.Type.LAMBDA) block.getBody().getSignature().checkArity(context.runtime, args);
args = IRRuntimeHelpers.prepareBlockArgs(context, block, args);
break;
default:
processBookKeepingOp(context, block, instr, operation, name, args, self, blockArg, implClass, currDynScope, temp, currScope);
Original file line number Diff line number Diff line change
@@ -92,10 +92,10 @@ public Object execute(IRScope scope, Object... data) {
if (scope instanceof IRClosure) {
savedViz = scope.createTemporaryVariable();
savedFrame = scope.createTemporaryVariable();
entryBB.addInstr(new SaveBindingVisibilityInstr(savedViz));
entryBB.addInstr(new PushBlockFrameInstr(savedFrame, scope.getName()));
if (requireBinding) entryBB.addInstr(new PushBlockBindingInstr());
entryBB.addInstr(new UpdateBlockExecutionStateInstr(Self.SELF));
entryBB.insertInstr(0, new SaveBindingVisibilityInstr(savedViz));
entryBB.insertInstr(1, new PushBlockFrameInstr(savedFrame, scope.getName()));
entryBB.insertInstr(2, new UpdateBlockExecutionStateInstr(Self.SELF));
if (requireBinding) entryBB.insertInstr(3, new PushBlockBindingInstr());
Signature sig = ((IRClosure)scope).getSignature();

// If it doesn't need any args, no arg preparation involved!
Original file line number Diff line number Diff line change
@@ -86,6 +86,10 @@ public void insertInstr(Instr i) {
instrs.add(0, i);
}

public void insertInstr(int index, Instr i) {
instrs.add(index, i);
}

public List<Instr> getInstrs() {
return instrs;
}
177 changes: 177 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.util.Arrays;

public class IRRuntimeHelpers {
private static final Logger LOG = LoggerFactory.getLogger("IRRuntimeHelpers");
@@ -793,6 +794,7 @@ public static RubyModule findInstanceMethodContainer(ThreadContext context, Dyna
case MODULE_BODY:
case CLASS_BODY:
case METACLASS_BODY:
case SCRIPT_BODY:
return (RubyModule) self;

case INSTANCE_METHOD:
@@ -1465,4 +1467,179 @@ public static RubyFixnum getArgScopeDepth(ThreadContext context, StaticScope cur
}
return context.runtime.newFixnum(i);
}

public static IRubyObject[] toAry(ThreadContext context, IRubyObject[] args) {
if (args.length == 1 && args[0].respondsTo("to_ary")) {
IRubyObject newAry = Helpers.aryToAry(args[0]);
if (newAry.isNil()) {
args = new IRubyObject[] { args[0] };
} else if (newAry instanceof RubyArray) {
args = ((RubyArray) newAry).toJavaArray();
} else {
throw context.runtime.newTypeError(args[0].getType().getName() + "#to_ary should return Array");
}
}
return args;
}

public static IRubyObject[] prepareProcArgs(ThreadContext context, Block b, IRubyObject[] args) {
if (args.length == 1) {
int arityValue = b.getBody().getSignature().arityValue();
return IRRuntimeHelpers.convertValueIntoArgArray(context, args[0], arityValue, b.type == Block.Type.NORMAL && args[0] instanceof RubyArray);
} else {
return args;
}
}

@JIT
public static IRubyObject[] prepareBlockArgs(ThreadContext context, Block block, IRubyObject[] args) {
// This is the placeholder for scenarios
// not handled by specialized instructions.
if (args == null) {
return IRubyObject.NULL_ARRAY;
}

boolean isProcCall = context.getCurrentBlockType() == Block.Type.PROC;
if (isProcCall) {
return prepareProcArgs(context, block, args);
}

boolean isLambda = block.type == Block.Type.LAMBDA;
if (isLambda && isProcCall) {
return args;
}

BlockBody body = block.getBody();
org.jruby.runtime.Signature sig = body.getSignature();

// blockArity == 0 and 1 have been handled in the specialized instructions
// This test is when we only have opt / rest arg (either keyword or non-keyword)
// but zero required args.
if (sig.arityValue() == -1) {
return args;
}

// We get here only when we have both required and optional/rest args
// (keyword or non-keyword in either case).
// So, convert a single value to an array if possible.
args = toAry(context, args);

// Nothing more to do for lambdas
if (isLambda) {
return args;
}

// Deal with keyword args that needs special handling
int needsKwargs = sig.hasKwargs() ? 1 - sig.getRequiredKeywordForArityCount() : 0;
int required = sig.required();
int actual = args.length;
if (needsKwargs == 0 || required > actual) {
// Nothing to do if we have fewer args in args than what is required
// The required arg instructions will return nil in those cases.
return args;
}

if (sig.isFixed() && required > 0 && required+needsKwargs != actual) {
// Make sure we have a ruby-hash
IRubyObject[] newArgs = Arrays.copyOf(args, required + needsKwargs);
if (actual < required+needsKwargs) {
// Not enough args and we need an empty {} for kwargs processing.
newArgs[newArgs.length - 1] = RubyHash.newHash(context.runtime);
} else {
// We have more args than we need and kwargs is always the last arg.
newArgs[newArgs.length - 1] = args[args.length - 1];
}
args = newArgs;
}

if (block.type == Block.Type.LAMBDA) block.getBody().getSignature().checkArity(context.runtime, args);

return args;
}

public static IRubyObject[] prepareFixedBlockArgs(ThreadContext context, Block block, IRubyObject[] args) {
if (args == null) {
return IRubyObject.NULL_ARRAY;
}

boolean isProcCall = context.getCurrentBlockType() == Block.Type.PROC;
if (isProcCall) {
return IRRuntimeHelpers.prepareProcArgs(context, block, args);
}

boolean isLambda = block.type == Block.Type.LAMBDA;
if (isLambda && isProcCall) {
return args;
}

// SSS FIXME: This check here is not required as long as
// the single-instruction cases always uses PreapreSingleBlockArgInstr
// But, including this here for robustness for now.
if (block.getBody().getSignature().arityValue() == 1) {
return args;
}

// Since we have more than 1 required arg,
// convert a single value to an array if possible.
args = IRRuntimeHelpers.toAry(context, args);

if (block.type == Block.Type.LAMBDA) block.getBody().getSignature().checkArity(context.runtime, args);

// If there are insufficient args, ReceivePreReqdInstr will return nil
return args;
}

public static IRubyObject[] prepareSingleBlockArgs(ThreadContext context, Block block, IRubyObject[] args) {
if (args == null) args = IRubyObject.NULL_ARRAY;

// Deal with proc calls
if (context.getCurrentBlockType() == Block.Type.PROC) {
if (args.length == 0) {
args = context.runtime.getSingleNilArray();
} else if (args.length == 1) {
args = prepareProcArgs(context, block, args);
} else {
args = new IRubyObject[] { args[0] };
}
}

if (block.type == Block.Type.LAMBDA) block.getBody().getSignature().checkArity(context.runtime, args);

// Nothing more to do! Hurray!
// If there are insufficient args, ReceivePreReqdInstr will return nil
return args;
}

private static DynamicScope getNewBlockScope(Block block, boolean pushNewDynScope, boolean reuseParentDynScope) {
DynamicScope newScope = block.getBinding().getDynamicScope();
if (pushNewDynScope) return block.allocScope(newScope);

// Reuse! We can avoid the push only if surrounding vars aren't referenced!
if (reuseParentDynScope) return newScope;

// No change
return null;
}

@JIT
public static DynamicScope pushBlockDynamicScopeIfNeeded(ThreadContext context, Block block, boolean pushNewDynScope, boolean reuseParentDynScope) {
DynamicScope newScope = getNewBlockScope(block, pushNewDynScope, reuseParentDynScope);
if (newScope != null) {
context.pushScope(newScope);
}
return newScope;
}

@JIT
public static IRubyObject updateBlockState(Block block, IRubyObject self) {
if (self == null || block.getEvalType() == EvalType.BINDING_EVAL) {
// Update self to the binding's self
Binding b = block.getBinding();
self = b.getSelf();
b.getFrame().setSelf(self);
}
// Clear block's eval type
block.setEvalType(EvalType.NONE);
return self;
}
}
14 changes: 13 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
Original file line number Diff line number Diff line change
@@ -132,6 +132,10 @@ public void loadContext() {
adapter.aload(signature.argOffset("context"));
}

public void loadSelfBlock() {
adapter.aload(signature.argOffset(JVMVisitor.SELF_BLOCK_NAME));
}

public void loadStaticScope() {
adapter.aload(signature.argOffset("scope"));
}
@@ -145,7 +149,7 @@ public void loadArgs() {
}

public void loadBlock() {
adapter.aload(signature.argOffset("block"));
adapter.aload(signature.argOffset(JVMVisitor.BLOCK_ARG_NAME));
}

public void loadFrameClass() {
@@ -170,6 +174,14 @@ public void loadBlockType() {
}
}

public void storeSelf() {
adapter.astore(signature.argOffset("self"));
}

public void storeArgs() {
adapter.astore(signature.argOffset("args"));
}

public void storeLocal(int i) {
adapter.astore(i);
}
112 changes: 101 additions & 11 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -58,6 +58,16 @@ public class JVMVisitor extends IRVisitor {
private static final Logger LOG = LoggerFactory.getLogger("JVMVisitor");
public static final String DYNAMIC_SCOPE = "$dynamicScope";
private static final boolean DEBUG = false;
public static final String BLOCK_ARG_NAME = "blockArg";
public static final String SELF_BLOCK_NAME = "selfBlock";

private static final Signature METHOD_SIGNATURE_BASE = Signature
.returning(IRubyObject.class)
.appendArgs(new String[]{"context", "scope", "self", BLOCK_ARG_NAME, "class", "callName"}, ThreadContext.class, StaticScope.class, IRubyObject.class, Block.class, RubyModule.class, String.class);

public static final Signature CLOSURE_SIGNATURE = Signature
.returning(IRubyObject.class)
.appendArgs(new String[]{"context", SELF_BLOCK_NAME, "scope", "self", "args", BLOCK_ARG_NAME, "superName", "type"}, ThreadContext.class, Block.class, StaticScope.class, IRubyObject.class, IRubyObject[].class, Block.class, String.class, Block.Type.class);

public JVMVisitor() {
this.jvm = Options.COMPILE_INVOKEDYNAMIC.load() ? new JVM7() : new JVM6();
@@ -143,12 +153,17 @@ public void emitScope(IRScope scope, String name, Signature signature, boolean s
jvm.cls().visitField(Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_VOLATILE, scopeField, ci(IRScope.class), null, null).visitEnd();
}

// Some scopes (closures, module/class bodies) do not have explicit call protocol yet.
// Unconditionally load current dynamic scope for those bodies.
if (!scope.hasExplicitCallProtocol()) {
// No call protocol, dynscope has been prepared for us
jvmMethod().loadContext();
jvmMethod().invokeVirtual(Type.getType(ThreadContext.class), Method.getMethod("org.jruby.runtime.DynamicScope getCurrentScope()"));
jvmStoreLocal(DYNAMIC_SCOPE);
} else if (scope instanceof IRClosure) {
// just load scope from context
// FIXME: don't do this if we won't need the scope
jvmMethod().loadContext();
jvmAdapter().invokevirtual(p(ThreadContext.class), "getCurrentScope", sig(DynamicScope.class));
jvmStoreLocal(DYNAMIC_SCOPE);
}

IRBytecodeAdapter m = jvmMethod();
@@ -198,10 +213,6 @@ public void emitScope(IRScope scope, String name, Signature signature, boolean s
jvm.popmethod();
}

private static final Signature METHOD_SIGNATURE_BASE = Signature
.returning(IRubyObject.class)
.appendArgs(new String[]{"context", "scope", "self", "block", "class", "callName"}, ThreadContext.class, StaticScope.class, IRubyObject.class, Block.class, RubyModule.class, String.class);

public static final Signature signatureFor(IRScope method, boolean aritySplit) {
if (aritySplit) {
StaticScope argScope = method.getStaticScope();
@@ -224,10 +235,6 @@ public static final Signature signatureFor(IRScope method, boolean aritySplit) {
return METHOD_SIGNATURE_BASE.insertArgs(3, new String[]{"args"}, IRubyObject[].class);
}

public static final Signature CLOSURE_SIGNATURE = Signature
.returning(IRubyObject.class)
.appendArgs(new String[]{"context", "scope", "self", "args", "block", "superName", "type"}, ThreadContext.class, StaticScope.class, IRubyObject.class, IRubyObject[].class, Block.class, String.class, Block.Type.class);

public void emitScriptBody(IRScriptBody script) {
// Note: no index attached because there should be at most one script body per .class
String name = JavaNameMangler.encodeScopeForBacktrace(script);
@@ -1390,12 +1397,46 @@ public void PopBindingInstr(PopBindingInstr popbindinginstr) {
jvmMethod().invokeVirtual(Type.getType(ThreadContext.class), Method.getMethod("void popScope()"));
}

@Override
public void PopBlockFrameInstr(PopBlockFrameInstr instr) {
jvmMethod().loadContext();
visit(instr.getFrame());
jvmAdapter().invokevirtual(p(ThreadContext.class), "postYieldNoScope", sig(void.class, Frame.class));
}

@Override
public void PopMethodFrameInstr(PopMethodFrameInstr popframeinstr) {
jvmMethod().loadContext();
jvmMethod().invokeVirtual(Type.getType(ThreadContext.class), Method.getMethod("void postMethodFrameOnly()"));
}

@Override
public void PrepareBlockArgsInstr(PrepareBlockArgsInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmMethod().loadArgs();
jvmMethod().invokeIRHelper("prepareBlockArgs", sig(IRubyObject[].class, ThreadContext.class, Block.class, IRubyObject[].class));
jvmMethod().storeArgs();
}

@Override
public void PrepareFixedBlockArgsInstr(PrepareFixedBlockArgsInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmMethod().loadArgs();
jvmMethod().invokeIRHelper("prepareFixedBlockArgs", sig(IRubyObject[].class, ThreadContext.class, Block.class, IRubyObject[].class));
jvmMethod().storeArgs();
}

@Override
public void PrepareSingleBlockArgInstr(PrepareSingleBlockArgInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmMethod().loadArgs();
jvmMethod().invokeIRHelper("prepareSingleBlockArgs", sig(IRubyObject[].class, ThreadContext.class, Block.class, IRubyObject[].class));
jvmMethod().storeArgs();
}

@Override
public void ProcessModuleBodyInstr(ProcessModuleBodyInstr processmodulebodyinstr) {
jvmMethod().loadContext();
@@ -1405,6 +1446,31 @@ public void ProcessModuleBodyInstr(ProcessModuleBodyInstr processmodulebodyinstr
jvmStoreLocal(processmodulebodyinstr.getResult());
}

@Override
public void PushBlockBindingInstr(PushBlockBindingInstr instr) {
IRScope scope = jvm.methodData().scope;
// FIXME: Centralize this out of InterpreterContext
boolean reuseParentDynScope = scope.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
boolean pushNewDynScope = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !reuseParentDynScope;
boolean popDynScope = pushNewDynScope || reuseParentDynScope;

jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmAdapter().ldc(pushNewDynScope);
jvmAdapter().ldc(reuseParentDynScope);
jvmMethod().invokeIRHelper("pushBlockDynamicScopeIfNeeded", sig(DynamicScope.class, ThreadContext.class, Block.class, boolean.class, boolean.class));
jvmStoreLocal(DYNAMIC_SCOPE);
}

@Override
public void PushBlockFrameInstr(PushBlockFrameInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmAdapter().invokevirtual(p(Block.class), "getBinding", sig(Binding.class));
jvmAdapter().invokevirtual(p(ThreadContext.class), "preYieldNoScope", sig(Frame.class, Binding.class));
jvmStoreLocal(instr.getResult());
}

@Override
public void PushMethodBindingInstr(PushMethodBindingInstr pushbindinginstr) {
jvmMethod().loadContext();
@@ -1488,7 +1554,7 @@ public void PutGlobalVarInstr(PutGlobalVarInstr putglobalvarinstr) {
@Override
public void ReifyClosureInstr(ReifyClosureInstr reifyclosureinstr) {
jvmMethod().loadRuntime();
jvmLoadLocal("$block");
jvmLoadLocal("$blockArg");
jvmMethod().invokeIRHelper("newProc", sig(IRubyObject.class, Ruby.class, Block.class));
jvmStoreLocal(reifyclosureinstr.getResult());
}
@@ -1622,6 +1688,14 @@ public void RestArgMultipleAsgnInstr(RestArgMultipleAsgnInstr restargmultipleasg
jvmStoreLocal(restargmultipleasgninstr.getResult());
}

@Override
public void RestoreBindingVisibilityInstr(RestoreBindingVisibilityInstr instr) {
jvmMethod().loadSelfBlock();
jvmAdapter().invokevirtual(p(Block.class), "getBinding", sig(Binding.class));
visit(instr.getVisibility());
jvmAdapter().invokevirtual(p(Binding.class), "setVisibility", sig(void.class, Visibility.class));
}

@Override
public void RuntimeHelperCall(RuntimeHelperCall runtimehelpercall) {
switch (runtimehelpercall.getHelperMethod()) {
@@ -1731,6 +1805,14 @@ public void RuntimeHelperCall(RuntimeHelperCall runtimehelpercall) {
}
}

@Override
public void SaveBindingVisibilityInstr(SaveBindingVisibilityInstr instr) {
jvmMethod().loadSelfBlock();
jvmAdapter().invokevirtual(p(Block.class), "getBinding", sig(Binding.class));
jvmAdapter().invokevirtual(p(Binding.class), "getVisibility", sig(Visibility.class));
jvmStoreLocal(instr.getResult());
}

@Override
public void ToggleBacktraceInstr(ToggleBacktraceInstr instr) {
jvmMethod().loadContext();
@@ -1859,6 +1941,14 @@ public void UnresolvedSuperInstr(UnresolvedSuperInstr unresolvedsuperinstr) {
superCommon(name, unresolvedsuperinstr, args, definingModule, containsArgSplat, closure);
}

@Override
public void UpdateBlockExecutionStateInstr (UpdateBlockExecutionStateInstr instr) {
jvmMethod().loadSelfBlock();
jvmMethod().loadSelf();
jvmMethod().invokeIRHelper("updateBlockState", sig(IRubyObject.class, Block.class, IRubyObject.class));
jvmMethod().storeSelf();
}

@Override
public void YieldInstr(YieldInstr yieldinstr) {
jvmMethod().loadContext();
19 changes: 16 additions & 3 deletions core/src/main/java/org/jruby/runtime/CompiledIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSig
this.reuseParentScope = closure.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
this.pushScope = !closure.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !this.reuseParentScope;
this.usesKwargs = closure.receivesKeywordArgs();
this.hasCallProtocolIR = closure.getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);

// Done in the interpreter (WrappedIRClosure) but we do it here
closure.getStaticScope().determineModule();
@@ -33,12 +34,24 @@ public ArgumentDescriptor[] getArgumentDescriptors() {

@Override
protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
throw new RuntimeException("callDirect not implemented in CompiledIRBlockBody. Implement me!");
context.setCurrentBlockType(Block.Type.PROC);
try {
return (IRubyObject)handle.invokeExact(context, block, getStaticScope(), (IRubyObject)null, args, blockArg, block.getBinding().getMethod(), block.type);
} catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

@Override
protected IRubyObject yieldDirect(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
throw new RuntimeException("yieldDirect not implemented in CompiledIRBlockBody. Implement me!");
context.setCurrentBlockType(Block.Type.NORMAL);
try {
return (IRubyObject)handle.invokeExact(context, block, getStaticScope(), self, args, Block.NULL_BLOCK, block.getBinding().getMethod(), block.type);
} catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

protected IRubyObject commonYieldPath(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
@@ -70,7 +83,7 @@ protected IRubyObject commonYieldPath(ThreadContext context, Block block, IRubyO
if (usesKwargs) IRRuntimeHelpers.frobnicateKwargsArgument(context, getSignature().required(), args);

try {
return (IRubyObject)handle.invokeExact(context, getStaticScope(), self, args, blockArg, binding.getMethod(), block.type);
return (IRubyObject)handle.invokeExact(context, block, getStaticScope(), self, args, blockArg, binding.getMethod(), block.type);
} catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached

0 comments on commit f9e3367

Please sign in to comment.