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: 65630e70c8e0
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a4ab4136fb00
Choose a head ref
  • 7 commits
  • 4 files changed
  • 1 contributor

Commits on Dec 2, 2015

  1. avoid BlockBody in Hash#to_proc impl - instead do all logic in RubyPr…

    …oc sub-class
    
    also revert the protected block field in RubyProc introduced due previous hack
    
    as advised in #2499
    kares committed Dec 2, 2015
    Copy the full SHA
    3c8048b View commit details
  2. Copy the full SHA
    8b7e43a View commit details
  3. Copy the full SHA
    b9af4e2 View commit details
  4. Copy the full SHA
    5d8f753 View commit details
  5. Copy the full SHA
    5a46bfb View commit details
  6. Copy the full SHA
    b2cac04 View commit details
  7. NullBlockBody should return null on getFile - gets Proc's formatted a…

    …ccording to MRI
    
    prev: #<Proc:0x25a664f6@(null):0> after: #<Proc:0x1d549387> on Hash#to_proc
    kares committed Dec 2, 2015
    Copy the full SHA
    a4ab413 View commit details
127 changes: 87 additions & 40 deletions core/src/main/java/org/jruby/RubyHash.java
Original file line number Diff line number Diff line change
@@ -904,57 +904,104 @@ public void visit(IRubyObject key, IRubyObject value) {

@JRubyMethod(name = "to_proc")
public RubyProc to_proc(ThreadContext context) {
final BlockBody body = new BlockBody(Signature.ONE_ARGUMENT) {
@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject key) {
// NOTE: the way currently RubyProc works this version is never dispatched!
return op_aref(context, key);
}
final Ruby runtime = context.runtime;
return new RubyProc(runtime, runtime.getProc(), new HashBlock(), null, -1);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
// NOTE: at this point we get the args normalized into [ one ]
// signature.checkArity(context.runtime, args);
return op_aref(context, args[0]);
}
private class HashBlock extends Block {

@Override
public String getFile() { return null; }
HashBlock() {
super(BlockBody.NULL_BODY);
this.type = Block.Type.PROC;
}

@Override
public int getLine() { return -1; }
private void checkArity(ThreadContext context, IRubyObject... args) {
// acts like a Proc but validate args like a lambda :
Signature.ONE_ARGUMENT.checkArity(context.runtime, args);
}

@Override
public StaticScope getStaticScope() {
return getRuntime().getStaticScopeFactory().getDummyScope();
}
@Override
public Signature getSignature() {
return Signature.ONE_ARGUMENT;
}

@Override
public void setStaticScope(StaticScope newScope) { /* noop */ }
};
@Override
public IRubyObject call(ThreadContext context, IRubyObject[] args) {
checkArity(context, args);
return op_aref(context, args[0]);
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject[] args, Block blockArg) {
return call(context, args);
}

return new StrictProc(context.runtime, new Block(body, context.currentBinding()));
}
@Override
public IRubyObject call(ThreadContext context) {
checkArity(context); // fails
throw new AssertionError();
}
@Override
public IRubyObject call(ThreadContext context, Block blockArg) {
return call(context);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context) {
return call(context);
}

// TODO this is a hack due the impossibility of validating arguments for a non-lambda
private static class StrictProc extends RubyProc {
@Override
public IRubyObject call(ThreadContext context, IRubyObject arg0) {
return op_aref(context, arg0);
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject arg0, Block blockArg) {
return call(context, arg0);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, IRubyObject arg0) {
return call(context, arg0);
}

StrictProc(final Ruby runtime, final Block block) {
super(runtime, runtime.getProc(), Block.Type.PROC);
// setup :
//block.getBinding().setFile(block.getBody().getFile());
//block.getBinding().setLine(block.getBody().getLine());
//
this.block = block;
block.type = Block.Type.PROC;
block.setProcObject(this);
@Override
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
checkArity(context, arg0, arg1); // fails
throw new AssertionError();
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block blockArg) {
return call(context, arg0, arg1);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
return call(context, arg0, arg1); // fails
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
checkArity(context, arg0, arg1, arg2); // fails
throw new AssertionError();
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg) {
return call(context, arg0, arg1, arg2);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return call(context, arg0, arg1, arg2); // fails
}

@Override
public IRubyObject yield(ThreadContext context, IRubyObject value) {
return op_aref(context, value);
}
@Override
public IRubyObject yieldNonArray(ThreadContext context, IRubyObject value, IRubyObject self) {
return yield(context, value);
}

@Override
public IRubyObject call19(ThreadContext context, IRubyObject[] args, Block blockCallArg) {
// validate args like a lambda :
getBlock().getBody().getSignature().checkArity(context.runtime, args);
return call(context, args, null, blockCallArg);
public IRubyObject yieldArray(ThreadContext context, IRubyObject value, IRubyObject self) {
throw new UnsupportedOperationException();
}

}
54 changes: 31 additions & 23 deletions core/src/main/java/org/jruby/RubyProc.java
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
* Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -57,14 +57,14 @@
*/
@JRubyClass(name="Proc")
public class RubyProc extends RubyObject implements DataType {
protected Block block = Block.NULL_BLOCK;
private Block block = Block.NULL_BLOCK;
private Block.Type type;
private String file = null;
private int line = -1;

protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type) {
super(runtime, rubyClass);

this.type = type;
}

@@ -80,13 +80,21 @@ protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type, String fi
this.line = line;
}


RubyProc(Ruby runtime, RubyClass rubyClass, Block block, String file, int line) {
this(runtime, rubyClass, block.type);
this.block = block;
this.file = file;
this.line = line;
}

public static RubyClass createProcClass(Ruby runtime) {
RubyClass procClass = runtime.defineClass("Proc", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
runtime.setProc(procClass);

procClass.setClassIndex(ClassIndex.PROC);
procClass.setReifiedClass(RubyProc.class);

procClass.defineAnnotatedMethods(RubyProc.class);

return procClass;
@@ -124,10 +132,10 @@ public static RubyProc newProc(Ruby runtime, Block block, Block.Type type, Strin

return proc;
}

/**
* Create a new instance of a Proc object. We override this method (from RubyClass)
* since we need to deal with special case of Proc.new with no arguments or block arg. In
* since we need to deal with special case of Proc.new with no arguments or block arg. In
* this case, we need to check previous frame for a block to consume.
*/
@JRubyMethod(name = "new", rest = true, meta = true)
@@ -140,19 +148,19 @@ public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, I
if (block.isGiven() && block.getProcObject() != null && block.getProcObject().getMetaClass() == recv) {
return block.getProcObject();
}

RubyProc obj = new RubyProc(context.runtime, (RubyClass)recv, Block.Type.PROC);
obj.setup(block);

obj.callMethod(context, "initialize", args, block);
return obj;
}

private void setup(Block procBlock) {
if (!procBlock.isGiven()) {
throw getRuntime().newArgumentError("tried to create Proc object without a block");
}

if (isLambda()) {
// TODO: warn "tried to create Proc object without a block"
}
@@ -185,11 +193,11 @@ private void setup(Block procBlock) {

block.type = type;
block.setProcObject(this);

// pre-request dummy scope to avoid clone overhead in lightweight blocks
block.getBinding().getDummyScope(block.getBody().getStaticScope());
}

@JRubyMethod(name = "clone")
@Override
public IRubyObject rbClone() {
@@ -203,7 +211,7 @@ public IRubyObject rbClone() {
public IRubyObject dup() {
return newProc(getRuntime(), block, type, file, line);
}

@Override
public IRubyObject to_s() {
return to_s19();
@@ -212,7 +220,7 @@ public IRubyObject to_s() {
@JRubyMethod(name = "to_s", alias = "inspect")
public IRubyObject to_s19() {
StringBuilder sb = new StringBuilder(32);
sb.append("#<Proc:0x").append(Integer.toString(block.hashCode(), 16));
sb.append("#<Proc:0x").append(Integer.toString(System.identityHashCode(block), 16));

String file = block.getBody().getFile();
if (file != null) sb.append('@').append(file).append(':').append(block.getBody().getLine() + 1);
@@ -223,7 +231,7 @@ public IRubyObject to_s19() {
IRubyObject string = RubyString.newString(getRuntime(), sb.toString());

if (isTaint()) string.setTaint(true);

return string;
}

@@ -320,17 +328,17 @@ public IRubyObject call19(ThreadContext context, IRubyObject[] args, Block block

public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject self, Block passedBlock) {
assert args != null;

Block newBlock;

// bind to new self, if given
if (self == null) {
newBlock = block;
} else {
newBlock = block.cloneBlockAndFrame();
newBlock.getBinding().setSelf(self);
}

return newBlock.call(context, args, passedBlock);
}

@@ -343,7 +351,7 @@ public RubyFixnum arity() {
// FIXME: Consider min/max like MRI here instead of required + kwarg count.
return getRuntime().newFixnum(signature.hasRest() ? signature.arityValue() : signature.required() + signature.getRequiredKeywordForArityCount());
}

@JRubyMethod(name = "to_proc")
public RubyProc to_proc() {
return this;
@@ -379,10 +387,10 @@ public IRubyObject lambda_p(ThreadContext context) {
private boolean isLambda() {
return type.equals(Block.Type.LAMBDA);
}
private boolean isProc() {
return type.equals(Block.Type.PROC);
}

//private boolean isProc() {
// return type.equals(Block.Type.PROC);
//}

private boolean isThread() {
return type.equals(Block.Type.THREAD);
78 changes: 41 additions & 37 deletions core/src/main/java/org/jruby/runtime/Block.java
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
* Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
* Copyright (C) 2004-2007 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -41,6 +41,7 @@

package org.jruby.runtime;

import java.util.Objects;
import org.jruby.EvalType;
import org.jruby.RubyArray;
import org.jruby.RubyProc;
@@ -49,36 +50,36 @@
/**
* Internal live representation of a block ({...} or do ... end).
*/
public final class Block {
public class Block {
public enum Type {
NORMAL(false), PROC(false), LAMBDA(true), THREAD(false);

Type(boolean checkArity) {
this.checkArity = checkArity;
}

public final boolean checkArity;
}

/**
* The Proc that this block is associated with. When we reference blocks via variable
* reference they are converted to Proc objects. We store a reference of the associated
* Proc object for easy conversion.
* Proc object for easy conversion.
*/
private RubyProc proc = null;

public Type type = Type.NORMAL;

private final Binding binding;

private final BlockBody body;

/** Whether this block and any clones of it should be considered "escaped" */
private boolean escaped;

/** What block to use for determining escape; defaults to this */
private Block escapeBlock = this;

/**
* All Block variables should either refer to a real block or this NULL_BLOCK.
*/
@@ -163,7 +164,7 @@ public IRubyObject yieldArray(ThreadContext context, IRubyObject value, IRubyObj

public Block cloneBlock() {
Block newBlock = new Block(body, binding);

newBlock.type = type;
newBlock.escapeBlock = this;

@@ -180,9 +181,9 @@ public Block cloneBlockAndFrame() {
oldBinding.getMethod(),
oldBinding.getFile(),
oldBinding.getLine());

Block newBlock = new Block(body, binding);

newBlock.type = type;
newBlock.escapeBlock = this;

@@ -201,12 +202,12 @@ public Block cloneBlockForEval(IRubyObject self, EvalType evalType) {

/**
* What is the arity of this block?
*
*
* @return the arity
*/
@Deprecated
public Arity arity() {
return body.getSignature().arity();
return getSignature().arity();
}

public Signature getSignature() {
@@ -215,69 +216,72 @@ public Signature getSignature() {

/**
* Retrieve the proc object associated with this block
*
*
* @return the proc or null if this has no proc associated with it
*/
public RubyProc getProcObject() {
return proc;
}

/**
* Set the proc object associated with this block
*
*
* @param procObject
*/
public void setProcObject(RubyProc procObject) {
this.proc = procObject;
}

/**
* Is the current block a real yield'able block instead a null one
*
*
* @return true if this is a valid block or false otherwise
*/
final public boolean isGiven() {
public final boolean isGiven() {
return this != NULL_BLOCK;
}

public Binding getBinding() {
return binding;
}

public BlockBody getBody() {
return body;
}

/**
* Gets the frame.
*
*
* @return Returns a RubyFrame
*/
public Frame getFrame() {
return binding.getFrame();
}

public boolean isEscaped() {
return escapeBlock.escaped;
}

public void escape() {
escapeBlock.escaped = true;
}

@Override
public boolean equals(Object other) {
if(this == other) {
return true;
}
if ( this == other ) return true;
if ( ! ( other instanceof Block ) ) return false;

if(!(other instanceof Block)) {
return false;
}
final Block that = (Block) other;

Block bOther = (Block)other;
return this.binding.equals(that.binding) && this.body == that.body;
}

return this.binding.equals(bOther.binding) &&
this.body == bOther.body;
@Override
public int hashCode() {
int hash = 11;
hash = 13 * hash + Objects.hashCode(this.binding);
hash = 17 * hash + Objects.hashCode(this.body);
return hash;
}

}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/runtime/NullBlockBody.java
Original file line number Diff line number Diff line change
@@ -66,7 +66,7 @@ public void setStaticScope(StaticScope newScope) {
}

public String getFile() {
return "(null)";
return null;
}

public int getLine() {