Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c9851a67b44f
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 67caf6254383
Choose a head ref
  • 11 commits
  • 15 files changed
  • 1 contributor

Commits on Jan 9, 2016

  1. Specify exact types for block and argument.

    The other values for block do not appear to ever be passed.
    headius committed Jan 9, 2016
    Copy the full SHA
    ca9f8fc View commit details
  2. Copy the full SHA
    5785a2d View commit details
  3. Copy the full SHA
    686f0c4 View commit details
  4. Copy the full SHA
    749954e View commit details
  5. Copy the full SHA
    fcefa51 View commit details
  6. Copy the full SHA
    8f179fd View commit details
  7. Copy the full SHA
    e516ee9 View commit details
  8. Add static DUMMY Frame.

    headius committed Jan 9, 2016
    Copy the full SHA
    1d4d9f1 View commit details
  9. Copy the full SHA
    1312aab View commit details
  10. Copy the full SHA
    8511e2b View commit details

Commits on Jan 10, 2016

  1. Copy the full SHA
    67caf62 View commit details
28 changes: 28 additions & 0 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -1120,4 +1120,32 @@ public boolean isTopLocalVariableScope() {
public boolean isScriptScope() {
return false;
}

public boolean needsFrame() {
boolean bindingHasEscaped = bindingHasEscaped();
boolean requireFrame = bindingHasEscaped || usesEval();

for (IRFlags flag : getFlags()) {
switch (flag) {
case BINDING_HAS_ESCAPED:
case CAN_CAPTURE_CALLERS_BINDING:
case REQUIRES_FRAME:
case REQUIRES_VISIBILITY:
case USES_BACKREF_OR_LASTLINE:
case USES_EVAL:
case USES_ZSUPER:
requireFrame = true;
}
}

return requireFrame;
}

public boolean reuseParentScope() {
return getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
}

public boolean needsBinding() {
return reuseParentScope() || !getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jruby.ir.instructions;

import org.jruby.ir.IRScope;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Variable;
@@ -9,6 +10,8 @@
import org.jruby.ir.transformations.inlining.InlineCloneInfo;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;

import static org.jruby.ir.IRFlags.REQUIRES_FRAME;

/**
* Load the block passed to this scope via the on-heap frame (or similar cross-call structure).
* This is typically used to access the "yieldable" target for blocks and evals. Only used
@@ -40,6 +43,12 @@ public static LoadFrameClosureInstr decode(IRReaderDecoder d) {
return new LoadFrameClosureInstr(d.decodeVariable());
}

@Override
public boolean computeScopeFlags(IRScope scope) {
scope.getFlags().add(REQUIRES_FRAME);
return true;
}

@Override
public void visit(IRVisitor visitor) {
visitor.LoadFrameClosure(this);
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/ir/instructions/YieldInstr.java
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@@ -67,7 +68,7 @@ public static YieldInstr decode(IRReaderDecoder d) {
@Interp
@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
Object blk = getBlockArg().retrieve(context, self, currScope, currDynScope, temp);
Block blk = (Block)getBlockArg().retrieve(context, self, currScope, currDynScope, temp);
if (getYieldArg() == UndefinedValue.UNDEFINED) {
return IRRuntimeHelpers.yieldSpecific(context, blk);
} else {
Original file line number Diff line number Diff line change
@@ -70,24 +70,11 @@ public Object execute(IRScope scope, Object... data) {
// to allocate a dynamic scope for it and add binding push/pop instructions.
if (!explicitCallProtocolSupported(scope)) return null;

StoreLocalVarPlacementProblem slvpp = scope.getStoreLocalVarPlacementProblem();
boolean scopeHasLocalVarStores = false;
boolean bindingHasEscaped = scope.bindingHasEscaped();

CFG cfg = scope.getCFG();

if (slvpp != null && bindingHasEscaped) {
scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores();
} else {
// We dont require local-var load/stores to have been run.
// If it is not run, we go conservative and add push/pop binding instrs. everywhere
scopeHasLocalVarStores = bindingHasEscaped;
}

// For now, we always require frame for closures
boolean requireFrame = doesItRequireFrame(scope, bindingHasEscaped);
boolean reuseParentDynScope = scope.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
boolean requireBinding = reuseParentDynScope || !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
boolean requireFrame = scope.needsFrame();
boolean requireBinding = scope.needsBinding();

if (scope instanceof IRClosure || requireBinding || requireFrame) {
BasicBlock entryBB = cfg.getEntryBB();
@@ -198,25 +185,6 @@ public Object execute(IRScope scope, Object... data) {
return null;
}

private boolean doesItRequireFrame(IRScope scope, boolean bindingHasEscaped) {
boolean requireFrame = bindingHasEscaped || scope.usesEval();

for (IRFlags flag : scope.getFlags()) {
switch (flag) {
case BINDING_HAS_ESCAPED:
case CAN_CAPTURE_CALLERS_BINDING:
case REQUIRES_FRAME:
case REQUIRES_VISIBILITY:
case USES_BACKREF_OR_LASTLINE:
case USES_EVAL:
case USES_ZSUPER:
requireFrame = true;
}
}

return requireFrame;
}

@Override
public boolean invalidate(IRScope scope) {
// Cannot add call protocol instructions after we've added them once.
28 changes: 20 additions & 8 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -471,18 +471,12 @@ public static IRubyObject newProc(Ruby runtime, Block block) {
return (block == Block.NULL_BLOCK) ? runtime.getNil() : runtime.newProc(Block.Type.PROC, block);
}

public static IRubyObject yield(ThreadContext context, Object blk, Object yieldArg, boolean unwrapArray) {
if (blk instanceof RubyProc) blk = ((RubyProc)blk).getBlock();
if (blk instanceof RubyNil) blk = Block.NULL_BLOCK;
Block b = (Block)blk;
public static IRubyObject yield(ThreadContext context, Block b, IRubyObject yieldArg, boolean unwrapArray) {
IRubyObject yieldVal = (IRubyObject)yieldArg;
return (unwrapArray && (yieldVal instanceof RubyArray)) ? b.yieldArray(context, yieldVal, null) : b.yield(context, yieldVal);
}

public static IRubyObject yieldSpecific(ThreadContext context, Object blk) {
if (blk instanceof RubyProc) blk = ((RubyProc)blk).getBlock();
if (blk instanceof RubyNil) blk = Block.NULL_BLOCK;
Block b = (Block)blk;
public static IRubyObject yieldSpecific(ThreadContext context, Block b) {
return b.yieldSpecific(context);
}

@@ -1725,4 +1719,22 @@ public static RubyProc newSymbolProc(ThreadContext context, String symbol, Encod
public static RubyProc newSymbolProc(ThreadContext context, String symbol, String encoding) {
return newSymbolProc(context, symbol, retrieveJCodingsEncoding(context, encoding));
}

@JIT
public static IRubyObject[] singleBlockArgToArray(IRubyObject value) {
IRubyObject[] args;
if (value instanceof RubyArray) {
args = value.convertToArray().toJavaArray();
} else {
args = new IRubyObject[] { value };
}
return args;
}

@JIT
public static Block prepareBlock(ThreadContext context, IRubyObject self, DynamicScope scope, BlockBody body) {
Block block = new Block(body, context.currentBinding(self, scope));

return block;
}
}
24 changes: 24 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -10,13 +10,19 @@
import org.jruby.common.IRubyWarnings;
import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.internal.runtime.methods.*;
import org.jruby.ir.IRScope;
import org.jruby.ir.JIT;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Frame;
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.invokedynamic.GlobalSite;
import org.jruby.runtime.invokedynamic.MathLinker;
@@ -924,4 +930,22 @@ public static IRubyObject getGlobalFallback(GlobalSite site, ThreadContext conte
public static IRubyObject getGlobalUncached(GlobalVariable variable) throws Throwable {
return variable.getAccessor().getValue();
}

public static Handle prepareBlock() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "prepareBlock", sig(CallSite.class, Lookup.class, String.class, MethodType.class, MethodHandle.class, MethodHandle.class, long.class));
}

public static CallSite prepareBlock(Lookup lookup, String name, MethodType type, MethodHandle bodyHandle, MethodHandle scopeHandle, long encodedSignature) throws Throwable {
IRScope scope = (IRScope)scopeHandle.invokeExact();

CompiledIRBlockBody body = new CompiledIRBlockBody(bodyHandle, scope, encodedSignature);

return new ConstantCallSite(Binder.from(type).append(body).invokeStaticQuiet(lookup, Bootstrap.class, "prepareBlock"));
}

public static Block prepareBlock(ThreadContext context, IRubyObject self, DynamicScope scope, CompiledIRBlockBody body) throws Throwable {
Binding binding = context.currentBinding(self, scope);

return new Block(body, binding);
}
}
45 changes: 21 additions & 24 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
Original file line number Diff line number Diff line change
@@ -266,30 +266,6 @@ public org.objectweb.asm.Label newLabel() {
return new org.objectweb.asm.Label();
}

public void pushBlockBody(Handle handle, org.jruby.runtime.Signature signature, String className) {
// FIXME: too much bytecode
String cacheField = "blockBody" + getClassData().callSiteCount.getAndIncrement();
Label done = new Label();
adapter.getClassVisitor().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, cacheField, ci(CompiledIRBlockBody.class), null, null).visitEnd();
adapter.getstatic(getClassData().clsName, cacheField, ci(CompiledIRBlockBody.class));
adapter.dup();
adapter.ifnonnull(done);
{
adapter.pop();
adapter.newobj(p(CompiledIRBlockBody.class));
adapter.dup();

adapter.ldc(handle);
adapter.getstatic(className, handle.getName() + "_IRScope", ci(IRScope.class));
adapter.ldc(signature.encode());

adapter.invokespecial(p(CompiledIRBlockBody.class), "<init>", sig(void.class, java.lang.invoke.MethodHandle.class, IRScope.class, long.class));
adapter.dup();
adapter.putstatic(getClassData().clsName, cacheField, ci(CompiledIRBlockBody.class));
}
adapter.label(done);
}

/**
* Stack required: none
*
@@ -593,6 +569,27 @@ public void pushBlockBody(Handle handle, org.jruby.runtime.Signature signature,
*/
public abstract void setGlobalVariable(String name);

/**
* Yield argument list to a block.
*
* Stack required: context, block, argument
*/
public abstract void yield(boolean unwrap);

/**
* Yield to a block.
*
* Stack required: context, block
*/
public abstract void yieldSpecific();

/**
* Prepare a block for a subsequent call.
*
* Stack required: context, self, dynamicScope
*/
public abstract void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, String className);

public SkinnyMethodAdapter adapter;
private int variableCount = 0;
private Map<Integer, Type> variableTypes = new HashMap<Integer, Type>();
43 changes: 43 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -26,11 +26,15 @@
import org.jruby.RubySymbol;
import org.jruby.compiler.NotCompilableException;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.CallType;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.MethodIndex;
import org.jruby.runtime.ThreadContext;
@@ -40,6 +44,7 @@
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.RegexpOptions;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -811,5 +816,43 @@ public void setGlobalVariable(String name) {
invokeHelper("setGlobalVariable", sig(IRubyObject.class, IRubyObject.class, Ruby.class, String.class));
}

@Override
public void yield(boolean unwrap) {
adapter.ldc(unwrap);
invokeIRHelper("yield", sig(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class, boolean.class));
}

@Override
public void yieldSpecific() {
invokeIRHelper("yieldSpecific", sig(IRubyObject.class, ThreadContext.class, Block.class));
}

@Override
public void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, String className) {
// FIXME: too much bytecode
String cacheField = "blockBody" + getClassData().callSiteCount.getAndIncrement();
Label done = new Label();
adapter.getClassVisitor().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, cacheField, ci(CompiledIRBlockBody.class), null, null).visitEnd();
adapter.getstatic(getClassData().clsName, cacheField, ci(CompiledIRBlockBody.class));
adapter.dup();
adapter.ifnonnull(done);
{
adapter.pop();
adapter.newobj(p(CompiledIRBlockBody.class));
adapter.dup();

adapter.ldc(handle);
adapter.getstatic(className, handle.getName() + "_IRScope", ci(IRScope.class));
adapter.ldc(signature.encode());

adapter.invokespecial(p(CompiledIRBlockBody.class), "<init>", sig(void.class, java.lang.invoke.MethodHandle.class, IRScope.class, long.class));
adapter.dup();
adapter.putstatic(getClassData().clsName, cacheField, ci(CompiledIRBlockBody.class));
}
adapter.label(done);

invokeIRHelper("prepareBlock", sig(Block.class, ThreadContext.class, IRubyObject.class, DynamicScope.class, BlockBody.class));
}

private final Map<Object, String> cacheFieldNames = new HashMap<>();
}
23 changes: 23 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter7.java
Original file line number Diff line number Diff line change
@@ -15,21 +15,27 @@
import org.jruby.RubyString;
import org.jruby.compiler.NotCompilableException;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CallType;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.RegexpOptions;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;

import java.math.BigInteger;

import static org.jruby.util.CodegenUtils.ci;
import static org.jruby.util.CodegenUtils.p;
import static org.jruby.util.CodegenUtils.params;
import static org.jruby.util.CodegenUtils.sig;

@@ -296,4 +302,21 @@ public void checkpoint() {
sig(void.class, ThreadContext.class),
Bootstrap.checkpointHandle());
}

@Override
public void yield(boolean unwrap) {
adapter.invokedynamic("yield", sig(JVM.OBJECT, params(ThreadContext.class, Block.class, JVM.OBJECT)), YieldSite.BOOTSTRAP, unwrap ? 1 : 0);
}

@Override
public void yieldSpecific() {
adapter.invokedynamic("yieldSpecific", sig(JVM.OBJECT, params(ThreadContext.class, Block.class)), YieldSite.BOOTSTRAP, 0);
}

@Override
public void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, String className) {
Handle scopeHandle = new Handle(Opcodes.H_GETSTATIC, getClassData().clsName, handle.getName() + "_IRScope", ci(IRScope.class));
long encodedSignature = signature.encode();
adapter.invokedynamic(handle.getName(), sig(Block.class, ThreadContext.class, IRubyObject.class, DynamicScope.class), Bootstrap.prepareBlock(), handle, scopeHandle, encodedSignature);
}
}
21 changes: 6 additions & 15 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -1994,11 +1994,10 @@ public void YieldInstr(YieldInstr yieldinstr) {
visit(yieldinstr.getBlockArg());

if (yieldinstr.getYieldArg() == UndefinedValue.UNDEFINED) {
jvmMethod().invokeIRHelper("yieldSpecific", sig(IRubyObject.class, ThreadContext.class, Object.class));
jvmMethod().yieldSpecific();
} else {
visit(yieldinstr.getYieldArg());
jvmAdapter().ldc(yieldinstr.isUnwrapArray());
jvmMethod().invokeIRHelper("yield", sig(IRubyObject.class, ThreadContext.class, Object.class, Object.class, boolean.class));
jvmMethod().yield(yieldinstr.isUnwrapArray());
}

jvmStoreLocal(yieldinstr.getResult());
@@ -2345,19 +2344,11 @@ public void UnexecutableNil(UnexecutableNil unexecutablenil) {
public void WrappedIRClosure(WrappedIRClosure wrappedirclosure) {
IRClosure closure = wrappedirclosure.getClosure();

jvmAdapter().newobj(p(Block.class));
jvmAdapter().dup();

jvmMethod().pushBlockBody(closure.getHandle(), closure.getSignature(), jvm.clsData().clsName);

{ // prepare binding
jvmMethod().loadContext();
visit(closure.getSelf());
jvmLoadLocal(DYNAMIC_SCOPE);
jvmAdapter().invokevirtual(p(ThreadContext.class), "currentBinding", sig(Binding.class, IRubyObject.class, DynamicScope.class));
}
jvmMethod().loadContext();
visit(closure.getSelf());
jvmLoadLocal(DYNAMIC_SCOPE);

jvmAdapter().invokespecial(p(Block.class), "<init>", sig(void.class, BlockBody.class, Binding.class));
jvmMethod().prepareBlock(closure.getHandle(), closure.getSignature(), jvm.clsData().clsName);
}

private SkinnyMethodAdapter jvmAdapter() {
133 changes: 133 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/YieldSite.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.jruby.ir.targets;

import com.headius.invokebinder.Binder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;

import static org.jruby.util.CodegenUtils.p;
import static org.jruby.util.CodegenUtils.sig;

/**
* Created by headius on 1/8/16.
*/
public class YieldSite extends MutableCallSite {
private final boolean unwrap;

public YieldSite(MethodType type, boolean unwrap) {
super(type);

this.unwrap = unwrap;
}

public static final Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(YieldSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class));

public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int unwrap) throws Throwable {
YieldSite site = new YieldSite(type, unwrap == 1 ? true : false);

MethodHandle handle = Binder.from(type)
.prepend(YieldSite.class, site)
.invokeVirtual(lookup, name);

site.setTarget(handle);

return site;
}

public IRubyObject yield(ThreadContext context, Block block, IRubyObject arg) throws Throwable {
MethodHandle handle = getHandleForBlock(block);

if (!unwrap && handle != null) {
MethodHandle test, target, fallback;

fallback = getTarget();

target = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class)
.foldVoid(SET_NORMAL)
.filter(2, VALUE_TO_ARRAY)
.insert(2, block.getBody().getStaticScope())
.insert(3, IRubyObject.class, null)
.append(Block.class, Block.NULL_BLOCK)
.append(block.getBinding().getMethod())
.append(block.type)
.invoke(handle);

test = Binder.from(boolean.class, ThreadContext.class, Block.class, IRubyObject.class).permute(1).append(handle).invoke(TEST_BLOCK);

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);

setTarget(guard);

return (IRubyObject)target.invokeExact(context, block, arg);
}

context.setCurrentBlockType(Block.Type.NORMAL);

return IRRuntimeHelpers.yield(context, block, arg, unwrap);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block) throws Throwable {
MethodHandle handle = getHandleForBlock(block);
if (handle != null) {
MethodHandle test, target, fallback;

fallback = getTarget();

target = Binder.from(IRubyObject.class, ThreadContext.class, Block.class)
.foldVoid(SET_NORMAL)
.append(block.getBody().getStaticScope())
.append(IRubyObject.class, null)
.append(IRubyObject[].class, null)
.append(Block.class, Block.NULL_BLOCK)
.append(block.getBinding().getMethod())
.append(block.type)
.invoke(handle);

test = Binder.from(boolean.class, ThreadContext.class, Block.class).drop(0).append(handle).invoke(TEST_BLOCK);

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);

setTarget(guard);

return (IRubyObject)target.invokeExact(context, block);
}

context.setCurrentBlockType(Block.Type.NORMAL);

return IRRuntimeHelpers.yieldSpecific(context, block);
}

public static final MethodHandles.Lookup LOOKUP = MethodHandles.publicLookup();
private static final MethodHandle SET_NORMAL = Binder.from(void.class, ThreadContext.class, Block.class).drop(1).append(Block.Type.NORMAL).invokeVirtualQuiet(LOOKUP, "setCurrentBlockType");
// private static final MethodHandle YIELD_SPECIFIC = Binder.from(IRubyObject.class, ThreadContext.class, Block.class).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "yieldSpecific");
// private static final MethodHandle YIELD = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class, boolean.class).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "yield");
private static final MethodHandle YIELD_SPECIFIC_FALLBACK = Binder.from(IRubyObject.class, YieldSite.class, ThreadContext.class, Block.class).invokeVirtualQuiet(LOOKUP, "yieldSpecific");
private static final MethodHandle YIELD_FALLBACK = Binder.from(IRubyObject.class, YieldSite.class, ThreadContext.class, Block.class, IRubyObject.class).invokeVirtualQuiet(LOOKUP, "yield");
private static final MethodHandle TEST_BLOCK = Binder.from(boolean.class, Block.class, MethodHandle.class).invokeStaticQuiet(LOOKUP, YieldSite.class, "testBlock");
private static final MethodHandle VALUE_TO_ARRAY = Binder.from(IRubyObject[].class, IRubyObject.class).invokeStaticQuiet(LOOKUP, IRRuntimeHelpers.class, "singleBlockArgToArray");

public static boolean testBlock(Block block, MethodHandle handle) {
return getHandleForBlock(block) == handle;
}

private static MethodHandle getHandleForBlock(Block block) {
BlockBody body = block.getBody();
if (block.getBody() instanceof CompiledIRBlockBody) {
CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) body;
return compiledBody.getHandle();
}
return null;
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/runtime/Binding.java
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ public class Binding {
public static final Binding DUMMY =
new Binding(
RubyBasicObject.NEVER,
new Frame(),
Frame.DUMMY,
Visibility.PUBLIC,
new NoVarsDynamicScope(StaticScopeFactory.newStaticScope(null, StaticScope.Type.BLOCK, null)),
"<dummy>",
8 changes: 2 additions & 6 deletions core/src/main/java/org/jruby/runtime/Block.java
Original file line number Diff line number Diff line change
@@ -42,8 +42,8 @@
package org.jruby.runtime;

import org.jruby.EvalType;
import org.jruby.RubyArray;
import org.jruby.RubyProc;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.builtin.IRubyObject;

/**
@@ -175,11 +175,7 @@ public IRubyObject yieldArray(ThreadContext context, IRubyObject value, IRubyObj
// introduce a specialized entry-point when we know that this block has
// explicit call protocol IR instructions.
IRubyObject[] args;
if (value instanceof RubyArray) {
args = value.convertToArray().toJavaArray();
} else {
args = new IRubyObject[] { value };
}
args = IRRuntimeHelpers.singleBlockArgToArray(value);
return body.yield(context, this, args, self);
}

52 changes: 4 additions & 48 deletions core/src/main/java/org/jruby/runtime/CompiledIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -10,17 +10,10 @@

public class CompiledIRBlockBody extends IRBlockBody {
protected final MethodHandle handle;
protected boolean pushScope;
protected boolean reuseParentScope;
protected boolean usesKwargs;

public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSignature) {
super(closure, Signature.decode(encodedSignature));
this.handle = handle;
// FIXME: duplicated from InterpreterContext
this.reuseParentScope = closure.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
this.pushScope = !closure.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !this.reuseParentScope;
this.usesKwargs = closure.receivesKeywordArgs();

// Done in the interpreter (WrappedIRClosure) but we do it here
closure.getStaticScope().determineModule();
@@ -36,6 +29,10 @@ public boolean canCallDirect() {
return true;
}

public MethodHandle getHandle() {
return handle;
}

@Override
protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
context.setCurrentBlockType(Block.Type.PROC);
@@ -57,45 +54,4 @@ protected IRubyObject yieldDirect(ThreadContext context, Block block, IRubyObjec
return null; // not reached
}
}

// @Override
// protected IRubyObject commonYieldPath(ThreadContext context, Block block, Block.Type type, IRubyObject[] args, IRubyObject self, Block blockArg) {
// Binding binding = block.getBinding();
// Visibility oldVis = binding.getFrame().getVisibility();
// Frame prevFrame = context.preYieldNoScope(binding);
//
// // SSS FIXME: Maybe, we should allocate a NoVarsScope/DummyScope for for-loop bodies because the static-scope here
// // probably points to the parent scope? To be verified and fixed if necessary. There is no harm as it is now. It
// // is just wasteful allocation since the scope is not used at all.
// DynamicScope prevScope = binding.getDynamicScope();
// if (this.pushScope) {
// // SSS FIXME: for lambdas, this behavior is different
// // compared to what InterpretedIRBlockBody and MixedModeIRBlockBody do
// context.pushScope(DynamicScope.newDynamicScope(getStaticScope(), prevScope, this.evalType.get()));
// } else if (this.reuseParentScope) {
// // Reuse! We can avoid the push only if surrounding vars aren't referenced!
// context.pushScope(prevScope);
// }
//
// self = IRRuntimeHelpers.updateBlockState(block, self);
//
// if (usesKwargs) IRRuntimeHelpers.frobnicateKwargsArgument(context, getSignature().required(), args);
//
// try {
// return (IRubyObject) handle.invokeExact(context, block, getStaticScope(), self, args, blockArg, binding.getMethod(), block.type);
// } catch (Throwable t) {
// Helpers.throwException(t);
// return null; // not reached
// } finally {
// // IMPORTANT: Do not clear eval-type in case this is reused in bindings!
// // Ex: eval("...", foo.instance_eval { binding })
// // The dyn-scope used for binding needs to have its eval-type set to INSTANCE_EVAL
// binding.getFrame().setVisibility(oldVis);
// if (this.pushScope || this.reuseParentScope) {
// context.postYield(binding, prevFrame);
// } else {
// context.postYieldNoScope(prevFrame);
// }
// }
// }
}
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/runtime/Frame.java
Original file line number Diff line number Diff line change
@@ -89,6 +89,9 @@ public final class Frame {

/** whether this frame has been captured into a binding **/
private boolean captured;

/** A dummy frame **/
public static final Frame DUMMY = new Frame();

/**
* Empty constructor, since Frame objects are pre-allocated and updated