Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions core/src/main/java/org/jruby/RubyMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,7 @@ public IRubyObject to_proc(ThreadContext context) {
receiver, originModule, originName, getFilename(), line == -1 ? -1 : line - 1);
Block b = MethodBlockBody.createMethodBlock(body);

RubyProc proc = RubyProc.newProc(context.runtime, b, Block.Type.LAMBDA);
proc.setFromMethod();
RubyProc proc = RubyProc.newMethodProc(context.runtime, b);
return proc;
}

Expand Down
4 changes: 1 addition & 3 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -3030,9 +3030,7 @@ private DynamicMethod createProcMethod(Ruby runtime, String name, Visibility vis
block.getBinding().getFrame().setName(name);

// a normal block passed to define_method changes to do arity checking; make it a lambda
RubyProc proc = runtime.newProc(Block.Type.LAMBDA, block);

proc.setFromMethod();
RubyProc proc = RubyProc.newMethodProc(runtime, block);

// various instructions can tell this scope is not an ordinary block but a block representing
// a method definition.
Expand Down
150 changes: 94 additions & 56 deletions core/src/main/java/org/jruby/RubyProc.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@
import org.jruby.anno.JRubyMethod;

import org.jruby.ast.util.ArgsUtil;
import org.jruby.common.IRubyWarnings;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
Expand All @@ -62,40 +60,90 @@
import static org.jruby.api.Define.defineClass;
import static org.jruby.api.Error.argumentError;
import static org.jruby.api.Warn.warn;
import static org.jruby.runtime.Block.Type.LAMBDA;
import static org.jruby.runtime.Block.Type.PROC;
import static org.jruby.runtime.ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR;
import static org.jruby.runtime.ThreadContext.resetCallInfo;
import static org.jruby.util.RubyStringBuilder.types;

@JRubyClass(name="Proc")
public class RubyProc extends RubyObject implements DataType {
private Block block = Block.NULL_BLOCK;
private final Block block;
private final Block.Type type;
private String file = null;
private int line = -1;
private boolean fromMethod;
private final String file;
private final int line;
private final boolean fromMethod;

protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type) {
/**
* Construct a new RubyProc with the given parameters. The Block's proc object will be set to this RubyProc.
*
* @param runtime
* @param rubyClass
* @param block
* @param file
* @param line
* @param fromMethod
*/
private RubyProc(Ruby runtime, RubyClass rubyClass, Block block, Block.Type type, String file, int line, boolean fromMethod) {
super(runtime, rubyClass);

this.block = block;
this.type = type;
}
this.file = file;
this.line = line;
this.fromMethod = fromMethod;

@Deprecated(since = "9.0.0.0")
protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type, ISourcePosition sourcePosition) {
this(runtime, rubyClass, type, sourcePosition.getFile(), sourcePosition.getLine());
block.setProcObject(this);
}

protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type, String file, int line) {
this(runtime, rubyClass, type);
/**
* Construct a new RubyProc with the given parameters. The Block's proc object will be set to this RubyProc.
*
* @param runtime
* @param rubyClass
* @param block
* @param file
* @param line
*/
private RubyProc(Ruby runtime, RubyClass rubyClass, Block block, Block.Type type, String file, int line) {
this(runtime, rubyClass, block, type, file, line, false);
}

this.file = file;
this.line = line;
/**
* Construct a new RubyProc with the given parameters. The Block's proc object will be set to this RubyProc.
*
* @param runtime
* @param rubyClass
* @param block
* @param file
* @param line
*/
public RubyProc(Ruby runtime, RubyClass rubyClass, Block block, String file, int line) {
this(runtime, rubyClass, block, block.type, file, line, false);
}

/**
* Construct a new RubyProc with the given parameters. The Block's proc object will be set to this RubyProc.
*
* @param runtime
* @param rubyClass
* @param block
* @param fromMethod
*/
public RubyProc(Ruby runtime, RubyClass rubyClass, Block block, Block.Type type, boolean fromMethod) {
this(runtime, rubyClass, block, type, null, -1, fromMethod);
}

public RubyProc(Ruby runtime, RubyClass rubyClass, Block block, String file, int line) {
this(runtime, rubyClass, block.type, file, line);
this.block = block;
/**
* Construct a new RubyProc with the given parameters. The Block's proc object will be set to this RubyProc.
*
* @param runtime
* @param rubyClass
* @param block
* @param type
*/
private RubyProc(Ruby runtime, RubyClass rubyClass, Block block, Block.Type type) {
this(runtime, rubyClass, block, type, null, -1, false);
}

public static RubyClass createProcClass(ThreadContext context, RubyClass Object) {
Expand All @@ -119,20 +167,13 @@ public static RubyProc newProc(Ruby runtime, Block.Type type) {
public static RubyProc newProc(Ruby runtime, Block block, Block.Type type) {
// The three valid types of execution here are PROC/LAMBDA/THREAD. NORMAL should not normally
// be passed but when it is we just assume it will be a PROC.
if (type == Block.Type.NORMAL) type = Block.Type.PROC;
if (type == Block.Type.NORMAL) type = PROC;

RubyProc proc = new RubyProc(runtime, runtime.getProc(), type);
proc.setup(runtime, block);

return proc;
return new RubyProc(runtime, runtime.getProc(), prepareBlock(runtime, block, type), type);
}

@Deprecated(since = "9.0.0.0")
public static RubyProc newProc(Ruby runtime, Block block, Block.Type type, ISourcePosition sourcePosition) {
RubyProc proc = new RubyProc(runtime, runtime.getProc(), type, sourcePosition);
proc.setup(runtime, block);

return proc;
public static RubyProc newMethodProc(Ruby runtime, Block block) {
return new RubyProc(runtime, runtime.getProc(), prepareBlock(runtime, block, LAMBDA), LAMBDA, true);
}

public static RubyProc newProc(Ruby runtime, Block block, Block.Type type, String file, int line) {
Expand All @@ -141,10 +182,8 @@ public static RubyProc newProc(Ruby runtime, Block block, Block.Type type, Strin
}

public static RubyProc newProc(Ruby runtime, RubyClass clazz, Block block, Block.Type type, String file, int line) {
RubyProc proc = new RubyProc(runtime, clazz, type, file, line);
proc.setup(runtime, block);

return proc;
return new RubyProc(runtime, clazz, prepareBlock(runtime, block, type), type, file, line);
}

/**
Expand All @@ -163,23 +202,19 @@ public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, I
return block.getProcObject();
}

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

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

private void setup(Ruby runtime, Block procBlock) {
if (!procBlock.isGiven()) throw argumentError(runtime.getCurrentContext(), "tried to create Proc object without a block");
private static Block prepareBlock(Ruby runtime, Block block, Block.Type type) {
if (!block.isGiven()) throw argumentError(runtime.getCurrentContext(), "tried to create Proc object without a block");

if (isLambda()) {
// TODO: warn "tried to create Proc object without a block"
}

if (isThread()) {
Block procBlock;
if (type == Block.Type.THREAD) {
// binding for incoming proc must not share frame
Binding oldBinding = procBlock.getBinding();
Binding oldBinding = block.getBinding();
Binding newBinding = new Binding(
oldBinding.getSelf(),
oldBinding.getFrame().duplicate(),
Expand All @@ -188,32 +223,32 @@ private void setup(Ruby runtime, Block procBlock) {
oldBinding.getMethod(),
oldBinding.getFile(),
oldBinding.getLine());
block = new Block(procBlock.getBody(), newBinding, type);
procBlock = new Block(block.getBody(), newBinding, type);

// Mark as escaped, so non-local flow errors immediately
block.escape();
procBlock.escape();

// modify the block with a new backref/lastline-grabbing scope
StaticScope oldScope = block.getBody().getStaticScope();
StaticScope oldScope = procBlock.getBody().getStaticScope();
StaticScope newScope = oldScope.duplicate();
block.getBody().setStaticScope(newScope);
procBlock.getBody().setStaticScope(newScope);
} else {
// just use as is unless type differs
if (type != procBlock.type) {
block = procBlock.cloneBlockAsType(type);
if (type != block.type) {
procBlock = block.cloneBlockAsType(type);
} else {
block = procBlock;
procBlock = block;
}
}

// force file/line info into the new block's binding
block.getBinding().setFile(block.getBody().getFile());
block.getBinding().setLine(block.getBody().getLine());

block.setProcObject(this);
procBlock.getBinding().setFile(procBlock.getBody().getFile());
procBlock.getBinding().setLine(procBlock.getBody().getLine());

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

return procBlock;
}

@JRubyMethod(name = "clone")
Expand Down Expand Up @@ -242,7 +277,7 @@ public IRubyObject to_s(ThreadContext context) {
boolean isSymbolProc = block.getBody() instanceof RubySymbol.SymbolProcBody;
if (isSymbolProc) {
string.catStringUnsafe("(&:" + ((RubySymbol.SymbolProcBody) block.getBody()).getId() + ")");
} else if ((file = block.getBody().getFile()) != null) {
} else if (block.getBody().getFile() instanceof String file) {
string.catStringUnsafe(" " + file + ":" + (block.getBody().getLine() + 1));
}

Expand Down Expand Up @@ -487,7 +522,10 @@ public IRubyObject dup() {
return dup(getRuntime().getCurrentContext());
}

/**
* @deprecated fromMethod is now final
*/
@Deprecated(since = "10.0.3.0")
public void setFromMethod() {
fromMethod = true;
}
}