package org.jruby;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
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;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.Signature;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.marshal.DataType;
import static org.jruby.util.RubyStringBuilder.types;
@JRubyClass(name="Proc")
public class RubyProc extends RubyObject implements DataType {
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;
}
@Deprecated
protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type, ISourcePosition sourcePosition) {
this(runtime, rubyClass, type, sourcePosition.getFile(), sourcePosition.getLine());
}
protected RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type, String file, int line) {
this(runtime, rubyClass, type);
this.file = file;
this.line = line;
}
public RubyProc(Ruby runtime, RubyClass rubyClass, Block block, String file, int line) {
this(runtime, rubyClass, block.type, file, line);
this.block = block;
}
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;
}
public Block getBlock() {
return block;
}
@Deprecated
public static RubyProc newProc(Ruby runtime, Block.Type type) {
throw runtime.newRuntimeError("deprecated RubyProc.newProc with no block; do not use");
}
public static RubyProc newProc(Ruby runtime, Block block, Block.Type type) {
RubyProc proc = new RubyProc(runtime, runtime.getProc(), type);
proc.setup(block);
return proc;
}
@Deprecated
public static RubyProc newProc(Ruby runtime, Block block, Block.Type type, ISourcePosition sourcePosition) {
RubyProc proc = new RubyProc(runtime, runtime.getProc(), type, sourcePosition);
proc.setup(block);
return proc;
}
public static RubyProc newProc(Ruby runtime, Block block, Block.Type type, String file, int line) {
RubyProc proc = new RubyProc(runtime, runtime.getProc(), type, file, line);
proc.setup(block);
return proc;
}
@JRubyMethod(name = "new", rest = true, meta = true)
public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
if (!block.isGiven()) block = context.getCurrentFrame().getBlock();
if (block.isGiven() && block.getProcObject() != null && block.getProcObject().metaClass == 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()) {
}
if (isThread()) {
Binding oldBinding = procBlock.getBinding();
Binding newBinding = new Binding(
oldBinding.getSelf(),
oldBinding.getFrame().duplicate(),
oldBinding.getVisibility(),
oldBinding.getDynamicScope(),
oldBinding.getMethod(),
oldBinding.getFile(),
oldBinding.getLine());
block = new Block(procBlock.getBody(), newBinding);
block.escape();
StaticScope oldScope = block.getBody().getStaticScope();
StaticScope newScope = oldScope.duplicate();
block.getBody().setStaticScope(newScope);
} else {
block = procBlock;
}
block.getBinding().setFile(block.getBody().getFile());
block.getBinding().setLine(block.getBody().getLine());
block.type = type;
block.setProcObject(this);
block.getBinding().getDummyScope(block.getBody().getStaticScope());
}
@JRubyMethod(name = "clone")
@Override
public IRubyObject rbClone() {
RubyProc newProc = newProc(getRuntime(), block, type, file, line);
return newProc;
}
@JRubyMethod(name = "dup")
@Override
public IRubyObject dup() {
return newProc(getRuntime(), block, type, file, line);
}
@Override
@JRubyMethod(name = "to_s", alias = "inspect")
public IRubyObject to_s() {
Ruby runtime = getRuntime();
RubyString string = runtime.newString("#<");
string.append(types(runtime, type()));
string.catString(":0x" + Integer.toString(System.identityHashCode(block), 16));
String file = block.getBody().getFile();
if (file != null) string.catString("@" + file + ":" + (block.getBody().getLine() + 1));
if (isLambda()) string.catString(" (lambda)");
string.catString(">");
if (isTaint()) string.setTaint(true);
return string;
}
@JRubyMethod(name = "binding")
public IRubyObject binding() {
return getRuntime().newBinding(block.getBinding());
}
public static IRubyObject[] prepareArgs(ThreadContext context, Block.Type type, BlockBody blockBody, IRubyObject[] args) {
if (type == Block.Type.LAMBDA) {
blockBody.getSignature().checkArity(context.runtime, args);
return args;
}
int arityValue = blockBody.getSignature().arityValue();
if (args.length == 1 && (arityValue < -1 || arityValue > 1)) args = IRRuntimeHelpers.toAry(context, args);
return args;
}
@JRubyMethod(name = {"call", "[]", "yield", "==="}, rest = true, omit = true)
public final IRubyObject call(ThreadContext context, IRubyObject[] args, Block blockCallArg) {
IRubyObject[] preppedArgs = prepareArgs(context, type, block.getBody(), args);
return call(context, preppedArgs, null, blockCallArg);
}
public final IRubyObject call(ThreadContext context, IRubyObject arg) {
return block.call(context, arg);
}
public final IRubyObject call(ThreadContext context, IRubyObject... args) {
return block.call(context, args);
}
public final IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject self, Block passedBlock) {
return call(context, args, self, null, passedBlock);
}
public final IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject self, RubyModule sourceModule, Block passedBlock) {
assert args != null;
Block newBlock;
if (self == null || sourceModule == null) {
newBlock = block;
} else {
newBlock = block.cloneBlockAndFrame();
if (self != null) {
newBlock.getBinding().setSelf(self);
}
if (sourceModule != null) {
newBlock.getFrame().setKlazz(sourceModule);
}
}
return newBlock.call(context, args, passedBlock);
}
@JRubyMethod(name = "arity")
public RubyFixnum arity() {
Signature signature = block.getSignature();
if (block.type == Block.Type.LAMBDA) return getRuntime().newFixnum(signature.arityValue());
return getRuntime().newFixnum(signature.hasRest() ? signature.arityValue() : signature.required() + signature.getRequiredKeywordForArityCount());
}
@JRubyMethod(name = "to_proc")
public RubyProc to_proc() {
return this;
}
@JRubyMethod
public IRubyObject source_location(ThreadContext context) {
Ruby runtime = context.runtime;
if (file != null) {
return runtime.newArray(runtime.newString(file), runtime.newFixnum(line + 1 ));
}
if (block != null) {
Binding binding = block.getBinding();
if (binding.getFile() != null) {
return runtime.newArray(
runtime.newString(binding.getFile()),
runtime.newFixnum(binding.getLine() + 1 ));
}
}
return context.nil;
}
@JRubyMethod
public IRubyObject parameters(ThreadContext context) {
BlockBody body = this.getBlock().getBody();
return Helpers.argumentDescriptorsToParameters(context.runtime,
body.getArgumentDescriptors(), isLambda());
}
@JRubyMethod(name = "lambda?")
public IRubyObject lambda_p(ThreadContext context) {
return context.runtime.newBoolean(isLambda());
}
private boolean isLambda() {
return type.equals(Block.Type.LAMBDA);
}
private boolean isThread() {
return type.equals(Block.Type.THREAD);
}
@Deprecated
public final IRubyObject call19(ThreadContext context, IRubyObject[] args, Block block) {
return call(context, args, block);
}
@Deprecated
public IRubyObject to_s19() {
return to_s();
}
}