package org.jruby.runtime;
import java.util.Objects;
import org.jruby.EvalType;
import org.jruby.RubyProc;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.builtin.IRubyObject;
public class Block {
public enum Type {
NORMAL(false), PROC(false), LAMBDA(true), THREAD(false);
Type(boolean checkArity) {
this.checkArity = checkArity;
}
public final boolean checkArity;
}
private RubyProc proc = null;
public final Type type;
private final Binding binding;
private final BlockBody body;
private boolean escaped;
private final Block escapeBlock;
private final EvalType evalType;
public static final Block NULL_BLOCK = new Block(BlockBody.NULL_BODY, new Binding(null, new Frame(), Visibility.PUBLIC));
static {
NULL_BLOCK.getBinding().getFrame().updateFrame(null, null, "", NULL_BLOCK);
}
private Block(BlockBody body, Binding binding, Type type, Block escapeBlock, EvalType evalType) {
assert binding != null;
this.body = body;
this.binding = binding;
this.type = type;
this.escapeBlock = escapeBlock;
this.evalType = evalType;
}
public Block(BlockBody body, Binding binding, Type type) {
assert binding != null;
this.body = body;
this.binding = binding;
this.type = type;
this.escapeBlock = this;
this.evalType = EvalType.NONE;
}
public Block(BlockBody body, Binding binding) {
this(body, binding, Type.NORMAL);
}
public Block(BlockBody body) {
this(body, Block.NULL_BLOCK.getBinding(), Type.NORMAL);
}
public DynamicScope allocScope(DynamicScope parentScope) {
DynamicScope newScope = DynamicScope.newDynamicScope(body.getStaticScope(), parentScope, evalType);
if (type == Block.Type.LAMBDA) newScope.setLambda(true);
return newScope;
}
public EvalType getEvalType() {
return evalType;
}
public IRubyObject call(ThreadContext context, IRubyObject[] args) {
return body.call(context, this, args);
}
public IRubyObject call(ThreadContext context, IRubyObject[] args, Block blockArg) {
return body.call(context, this, args, blockArg);
}
public IRubyObject call(ThreadContext context) {
return body.call(context, this);
}
public IRubyObject call(ThreadContext context, Block blockArg) {
return body.call(context, this, blockArg);
}
public IRubyObject yieldSpecific(ThreadContext context) {
return body.yieldSpecific(context, this);
}
public IRubyObject call(ThreadContext context, IRubyObject arg0) {
return body.call(context, this, arg0);
}
public IRubyObject call(ThreadContext context, IRubyObject arg0, Block blockArg) {
return body.call(context, this, arg0, blockArg);
}
public IRubyObject yieldSpecific(ThreadContext context, IRubyObject arg0) {
return body.yieldSpecific(context, this, arg0);
}
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
return body.call(context, this, arg0, arg1);
}
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block blockArg) {
return body.call(context, this, arg0, arg1, blockArg);
}
public IRubyObject yieldSpecific(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
return body.yieldSpecific(context, this, arg0, arg1);
}
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return body.call(context, this, arg0, arg1, arg2);
}
public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg) {
return body.call(context, this, arg0, arg1, arg2, blockArg);
}
public IRubyObject yieldSpecific(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return body.yieldSpecific(context, this, arg0, arg1, arg2);
}
public IRubyObject yield(ThreadContext context, IRubyObject value) {
return body.yield(context, this, value);
}
public IRubyObject yieldNonArray(ThreadContext context, IRubyObject value, IRubyObject self) {
return body.yield(context, this, new IRubyObject[] { value }, self);
}
public IRubyObject yieldArray(ThreadContext context, IRubyObject value, IRubyObject self) {
IRubyObject[] args = IRRuntimeHelpers.singleBlockArgToArray(value);
return body.yield(context, this, args, self);
}
public IRubyObject yieldValues(ThreadContext context, IRubyObject[] args) {
return body.yield(context, this, args, null);
}
public Block cloneBlock() {
return cloneBlockAsType(type);
}
public Block cloneBlockAsType(Type newType) {
Block newBlock = new Block(body, binding, newType, this, evalType);
return newBlock;
}
public Block cloneBlockAndBinding() {
return cloneBlockAndBinding(evalType);
}
public Block cloneBlockAndBinding(EvalType evalType) {
Block newBlock = new Block(body, binding.clone(), type, this, evalType);
return newBlock;
}
public Block cloneBlockAndFrame() {
return cloneBlockAndFrame(EvalType.NONE);
}
public Block cloneBlockAndFrame(EvalType evalType) {
Binding oldBinding = binding;
Binding binding = new Binding(
oldBinding.getSelf(),
oldBinding.getFrame().duplicate(),
oldBinding.getVisibility(),
oldBinding.getDynamicScope(),
oldBinding.getMethod(),
oldBinding.getFile(),
oldBinding.getLine());
Block newBlock = new Block(body, binding, type, this, evalType);
return newBlock;
}
public Block cloneBlockForEval(IRubyObject self, EvalType evalType) {
Block newBlock = new Block(body, binding, type, this, evalType);
Block block = newBlock;
block.getBinding().setSelf(self);
block.getBinding().getFrame().setSelf(self);
return block;
}
public Block deepCloneBlockForEval(IRubyObject self, EvalType evalType) {
Block block = cloneBlockAndBinding(evalType);
block.getBinding().setSelf(self);
block.getBinding().getFrame().setSelf(self);
return block;
}
@Deprecated
public Arity arity() {
return getSignature().arity();
}
public Signature getSignature() {
return body.getSignature();
}
public RubyProc getProcObject() {
return proc;
}
public void setProcObject(RubyProc procObject) {
this.proc = procObject;
}
public final boolean isGiven() {
return this != NULL_BLOCK;
}
public Binding getBinding() {
return binding;
}
public BlockBody getBody() {
return body;
}
public Frame getFrame() {
return binding.getFrame();
}
public boolean isEscaped() {
return escapeBlock.escaped;
}
public void escape() {
escapeBlock.escaped = true;
}
public Visibility getVisibility() {
return binding.getFrame().getVisibility();
}
public void setVisibility(Visibility vis) {
binding.getFrame().setVisibility(vis);
}
@Override
public boolean equals(Object other) {
if ( this == other ) return true;
if ( ! ( other instanceof Block ) ) return false;
final Block that = (Block) other;
return this.binding.equals(that.binding) && this.body == that.body;
}
@Override
public int hashCode() {
int hash = 11;
hash = 13 * hash + Objects.hashCode(this.binding);
hash = 17 * hash + Objects.hashCode(this.body);
return hash;
}
}