Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2320 lines (2090 sloc) 57.972 kb
/**********************************************************************
proc.c - Proc, Binding, Env
$Author$
created at: Wed Jan 17 12:13:14 2007
Copyright (C) 2004-2007 Koichi Sasada
**********************************************************************/
#include "eval_intern.h"
#include "internal.h"
#include "gc.h"
#include "iseq.h"
struct METHOD {
VALUE recv;
VALUE rclass;
ID id;
rb_method_entry_t *me;
struct unlinked_method_entry_list_entry *ume;
};
VALUE rb_cUnboundMethod;
VALUE rb_cMethod;
VALUE rb_cBinding;
VALUE rb_cProc;
static VALUE bmcall(VALUE, VALUE);
static int method_arity(VALUE);
static ID attached;
/* Proc */
#define IS_METHOD_PROC_NODE(node) (nd_type(node) == NODE_IFUNC && (node)->nd_cfnc == bmcall)
static void
proc_free(void *ptr)
{
RUBY_FREE_ENTER("proc");
if (ptr) {
ruby_xfree(ptr);
}
RUBY_FREE_LEAVE("proc");
}
static void
proc_mark(void *ptr)
{
rb_proc_t *proc;
RUBY_MARK_ENTER("proc");
if (ptr) {
proc = ptr;
RUBY_MARK_UNLESS_NULL(proc->envval);
RUBY_MARK_UNLESS_NULL(proc->blockprocval);
RUBY_MARK_UNLESS_NULL(proc->block.proc);
RUBY_MARK_UNLESS_NULL(proc->block.self);
if (proc->block.iseq && RUBY_VM_IFUNC_P(proc->block.iseq)) {
RUBY_MARK_UNLESS_NULL((VALUE)(proc->block.iseq));
}
}
RUBY_MARK_LEAVE("proc");
}
static size_t
proc_memsize(const void *ptr)
{
return ptr ? sizeof(rb_proc_t) : 0;
}
static const rb_data_type_t proc_data_type = {
"proc",
{
proc_mark,
proc_free,
proc_memsize,
},
};
VALUE
rb_proc_alloc(VALUE klass)
{
rb_proc_t *proc;
return TypedData_Make_Struct(klass, rb_proc_t, &proc_data_type, proc);
}
VALUE
rb_obj_is_proc(VALUE proc)
{
if (rb_typeddata_is_kind_of(proc, &proc_data_type)) {
return Qtrue;
}
else {
return Qfalse;
}
}
/* :nodoc: */
static VALUE
proc_dup(VALUE self)
{
VALUE procval = rb_proc_alloc(rb_cProc);
rb_proc_t *src, *dst;
GetProcPtr(self, src);
GetProcPtr(procval, dst);
dst->block = src->block;
dst->block.proc = procval;
dst->blockprocval = src->blockprocval;
dst->envval = src->envval;
dst->safe_level = src->safe_level;
dst->is_lambda = src->is_lambda;
return procval;
}
/* :nodoc: */
static VALUE
proc_clone(VALUE self)
{
VALUE procval = proc_dup(self);
CLONESETUP(procval, self);
return procval;
}
/*
* call-seq:
* prc.lambda? -> true or false
*
* Returns +true+ for a Proc object for which argument handling is rigid.
* Such procs are typically generated by +lambda+.
*
* A Proc object generated by +proc+ ignores extra arguments.
*
* proc {|a,b| [a,b] }.call(1,2,3) #=> [1,2]
*
* It provides +nil+ for missing arguments.
*
* proc {|a,b| [a,b] }.call(1) #=> [1,nil]
*
* It expands a single array argument.
*
* proc {|a,b| [a,b] }.call([1,2]) #=> [1,2]
*
* A Proc object generated by +lambda+ doesn't have such tricks.
*
* lambda {|a,b| [a,b] }.call(1,2,3) #=> ArgumentError
* lambda {|a,b| [a,b] }.call(1) #=> ArgumentError
* lambda {|a,b| [a,b] }.call([1,2]) #=> ArgumentError
*
* Proc#lambda? is a predicate for the tricks.
* It returns +true+ if no tricks apply.
*
* lambda {}.lambda? #=> true
* proc {}.lambda? #=> false
*
* Proc.new is the same as +proc+.
*
* Proc.new {}.lambda? #=> false
*
* +lambda+, +proc+ and Proc.new preserve the tricks of
* a Proc object given by <code>&</code> argument.
*
* lambda(&lambda {}).lambda? #=> true
* proc(&lambda {}).lambda? #=> true
* Proc.new(&lambda {}).lambda? #=> true
*
* lambda(&proc {}).lambda? #=> false
* proc(&proc {}).lambda? #=> false
* Proc.new(&proc {}).lambda? #=> false
*
* A Proc object generated by <code>&</code> argument has the tricks
*
* def n(&b) b.lambda? end
* n {} #=> false
*
* The <code>&</code> argument preserves the tricks if a Proc object
* is given by <code>&</code> argument.
*
* n(&lambda {}) #=> true
* n(&proc {}) #=> false
* n(&Proc.new {}) #=> false
*
* A Proc object converted from a method has no tricks.
*
* def m() end
* method(:m).to_proc.lambda? #=> true
*
* n(&method(:m)) #=> true
* n(&method(:m).to_proc) #=> true
*
* +define_method+ is treated the same as method definition.
* The defined method has no tricks.
*
* class C
* define_method(:d) {}
* end
* C.new.d(1,2) #=> ArgumentError
* C.new.method(:d).to_proc.lambda? #=> true
*
* +define_method+ always defines a method without the tricks,
* even if a non-lambda Proc object is given.
* This is the only exception for which the tricks are not preserved.
*
* class C
* define_method(:e, &proc {})
* end
* C.new.e(1,2) #=> ArgumentError
* C.new.method(:e).to_proc.lambda? #=> true
*
* This exception insures that methods never have tricks
* and makes it easy to have wrappers to define methods that behave as usual.
*
* class C
* def self.def2(name, &body)
* define_method(name, &body)
* end
*
* def2(:f) {}
* end
* C.new.f(1,2) #=> ArgumentError
*
* The wrapper <i>def2</i> defines a method which has no tricks.
*
*/
VALUE
rb_proc_lambda_p(VALUE procval)
{
rb_proc_t *proc;
GetProcPtr(procval, proc);
return proc->is_lambda ? Qtrue : Qfalse;
}
/* Binding */
static void
binding_free(void *ptr)
{
rb_binding_t *bind;
RUBY_FREE_ENTER("binding");
if (ptr) {
bind = ptr;
ruby_xfree(bind);
}
RUBY_FREE_LEAVE("binding");
}
static void
binding_mark(void *ptr)
{
rb_binding_t *bind;
RUBY_MARK_ENTER("binding");
if (ptr) {
bind = ptr;
RUBY_MARK_UNLESS_NULL(bind->env);
RUBY_MARK_UNLESS_NULL(bind->path);
}
RUBY_MARK_LEAVE("binding");
}
static size_t
binding_memsize(const void *ptr)
{
return ptr ? sizeof(rb_binding_t) : 0;
}
static const rb_data_type_t binding_data_type = {
"binding",
{
binding_mark,
binding_free,
binding_memsize,
},
};
static VALUE
binding_alloc(VALUE klass)
{
VALUE obj;
rb_binding_t *bind;
obj = TypedData_Make_Struct(klass, rb_binding_t, &binding_data_type, bind);
return obj;
}
/* :nodoc: */
static VALUE
binding_dup(VALUE self)
{
VALUE bindval = binding_alloc(rb_cBinding);
rb_binding_t *src, *dst;
GetBindingPtr(self, src);
GetBindingPtr(bindval, dst);
dst->env = src->env;
dst->path = src->path;
dst->first_lineno = src->first_lineno;
return bindval;
}
/* :nodoc: */
static VALUE
binding_clone(VALUE self)
{
VALUE bindval = binding_dup(self);
CLONESETUP(bindval, self);
return bindval;
}
VALUE
rb_binding_new(void)
{
rb_thread_t *th = GET_THREAD();
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
VALUE bindval = binding_alloc(rb_cBinding);
rb_binding_t *bind;
if (cfp == 0) {
rb_raise(rb_eRuntimeError, "Can't create Binding Object on top of Fiber.");
}
GetBindingPtr(bindval, bind);
bind->env = rb_vm_make_env_object(th, cfp);
bind->path = cfp->iseq->location.path;
bind->first_lineno = rb_vm_get_sourceline(cfp);
return bindval;
}
/*
* call-seq:
* binding -> a_binding
*
* Returns a +Binding+ object, describing the variable and
* method bindings at the point of call. This object can be used when
* calling +eval+ to execute the evaluated command in this
* environment. See also the description of class +Binding+.
*
* def get_binding(param)
* return binding
* end
* b = get_binding("hello")
* eval("param", b) #=> "hello"
*/
static VALUE
rb_f_binding(VALUE self)
{
return rb_binding_new();
}
/*
* call-seq:
* binding.eval(string [, filename [,lineno]]) -> obj
*
* Evaluates the Ruby expression(s) in <em>string</em>, in the
* <em>binding</em>'s context. If the optional <em>filename</em> and
* <em>lineno</em> parameters are present, they will be used when
* reporting syntax errors.
*
* def get_binding(param)
* return binding
* end
* b = get_binding("hello")
* b.eval("param") #=> "hello"
*/
static VALUE
bind_eval(int argc, VALUE *argv, VALUE bindval)
{
VALUE args[4];
rb_scan_args(argc, argv, "12", &args[0], &args[2], &args[3]);
args[1] = bindval;
return rb_f_eval(argc+1, args, Qnil /* self will be searched in eval */);
}
static VALUE
proc_new(VALUE klass, int is_lambda)
{
VALUE procval = Qnil;
rb_thread_t *th = GET_THREAD();
rb_control_frame_t *cfp = th->cfp;
rb_block_t *block;
if ((block = rb_vm_control_frame_block_ptr(cfp)) != 0) {
/* block found */
}
else {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
if ((block = rb_vm_control_frame_block_ptr(cfp)) != 0) {
if (is_lambda) {
rb_warn("tried to create Proc object without a block");
}
}
else {
rb_raise(rb_eArgError,
"tried to create Proc object without a block");
}
}
procval = block->proc;
if (procval) {
if (RBASIC(procval)->klass == klass) {
return procval;
}
else {
VALUE newprocval = proc_dup(procval);
RBASIC(newprocval)->klass = klass;
return newprocval;
}
}
procval = rb_vm_make_proc(th, block, klass);
rb_vm_rewrite_ep_in_errinfo(th, cfp);
if (is_lambda) {
rb_proc_t *proc;
GetProcPtr(procval, proc);
proc->is_lambda = TRUE;
}
return procval;
}
/*
* call-seq:
* Proc.new {|...| block } -> a_proc
* Proc.new -> a_proc
*
* Creates a new <code>Proc</code> object, bound to the current
* context. <code>Proc::new</code> may be called without a block only
* within a method with an attached block, in which case that block is
* converted to the <code>Proc</code> object.
*
* def proc_from
* Proc.new
* end
* proc = proc_from { "hello" }
* proc.call #=> "hello"
*/
static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
VALUE block = proc_new(klass, FALSE);
rb_obj_call_init(block, argc, argv);
return block;
}
/*
* call-seq:
* proc { |...| block } -> a_proc
*
* Equivalent to <code>Proc.new</code>.
*/
VALUE
rb_block_proc(void)
{
return proc_new(rb_cProc, FALSE);
}
VALUE
rb_block_lambda(void)
{
return proc_new(rb_cProc, TRUE);
}
VALUE
rb_f_lambda(void)
{
rb_warn("rb_f_lambda() is deprecated; use rb_block_proc() instead");
return rb_block_lambda();
}
/*
* call-seq:
* lambda { |...| block } -> a_proc
*
* Equivalent to <code>Proc.new</code>, except the resulting Proc objects
* check the number of parameters passed when called.
*/
static VALUE
proc_lambda(void)
{
return rb_block_lambda();
}
/* Document-method: ===
*
* call-seq:
* proc === obj -> result_of_proc
*
* Invokes the block with +obj+ as the proc's parameter like Proc#call. It
* is to allow a proc object to be a target of +when+ clause in a case
* statement.
*/
/* CHECKME: are the argument checking semantics correct? */
/*
* call-seq:
* prc.call(params,...) -> obj
* prc[params,...] -> obj
* prc.(params,...) -> obj
*
* Invokes the block, setting the block's parameters to the values in
* <i>params</i> using something close to method calling semantics.
* Generates a warning if multiple values are passed to a proc that
* expects just one (previously this silently converted the parameters
* to an array). Note that prc.() invokes prc.call() with the parameters
* given. It's a syntax sugar to hide "call".
*
* For procs created using <code>lambda</code> or <code>->()</code> an error
* is generated if the wrong number of parameters are passed to a Proc with
* multiple parameters. For procs created using <code>Proc.new</code> or
* <code>Kernel.proc</code>, extra parameters are silently discarded.
*
* Returns the value of the last expression evaluated in the block. See
* also <code>Proc#yield</code>.
*
* a_proc = Proc.new {|a, *b| b.collect {|i| i*a }}
* a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
* a_proc[9, 1, 2, 3] #=> [9, 18, 27]
* a_proc = lambda {|a,b| a}
* a_proc.call(1,2,3)
*
* <em>produces:</em>
*
* prog.rb:4:in `block in <main>': wrong number of arguments (3 for 2) (ArgumentError)
* from prog.rb:5:in `call'
* from prog.rb:5:in `<main>'
*
*/
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
VALUE vret;
rb_proc_t *proc;
rb_block_t *blockptr = 0;
rb_iseq_t *iseq;
VALUE passed_procval;
GetProcPtr(procval, proc);
iseq = proc->block.iseq;
if (BUILTIN_TYPE(iseq) == T_NODE || iseq->arg_block != -1) {
if (rb_block_given_p()) {
rb_proc_t *passed_proc;
RB_GC_GUARD(passed_procval) = rb_block_proc();
GetProcPtr(passed_procval, passed_proc);
blockptr = &passed_proc->block;
}
}
vret = rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
argc, argv, blockptr);
RB_GC_GUARD(procval);
return vret;
}
#if SIZEOF_LONG > SIZEOF_INT
static inline int
check_argc(long argc)
{
if (argc > INT_MAX || argc < 0) {
rb_raise(rb_eArgError, "too many arguments (%lu)",
(unsigned long)argc);
}
return (int)argc;
}
#else
#define check_argc(argc) (argc)
#endif
VALUE
rb_proc_call(VALUE self, VALUE args)
{
VALUE vret;
rb_proc_t *proc;
GetProcPtr(self, proc);
vret = rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
check_argc(RARRAY_LEN(args)), RARRAY_PTR(args), 0);
RB_GC_GUARD(self);
RB_GC_GUARD(args);
return vret;
}
VALUE
rb_proc_call_with_block(VALUE self, int argc, VALUE *argv, VALUE pass_procval)
{
VALUE vret;
rb_proc_t *proc;
rb_block_t *block = 0;
GetProcPtr(self, proc);
if (!NIL_P(pass_procval)) {
rb_proc_t *pass_proc;
GetProcPtr(pass_procval, pass_proc);
block = &pass_proc->block;
}
vret = rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
argc, argv, block);
RB_GC_GUARD(self);
RB_GC_GUARD(pass_procval);
return vret;
}
/*
* call-seq:
* prc.arity -> fixnum
*
* Returns the number of arguments that would not be ignored. If the block
* is declared to take no arguments, returns 0. If the block is known
* to take exactly n arguments, returns n. If the block has optional
* arguments, return -n-1, where n is the number of mandatory
* arguments. A <code>proc</code> with no argument declarations
* is the same a block declaring <code>||</code> as its arguments.
*
* proc {}.arity #=> 0
* proc {||}.arity #=> 0
* proc {|a|}.arity #=> 1
* proc {|a,b|}.arity #=> 2
* proc {|a,b,c|}.arity #=> 3
* proc {|*a|}.arity #=> -1
* proc {|a,*b|}.arity #=> -2
* proc {|a,*b, c|}.arity #=> -3
*
* proc { |x = 0| }.arity #=> 0
* lambda { |a = 0| }.arity #=> -1
* proc { |x=0, y| }.arity #=> 0
* lambda { |x=0, y| }.arity #=> -2
* proc { |x=0, y=0| }.arity #=> 0
* lambda { |x=0, y=0| }.arity #=> -1
* proc { |x, y=0| }.arity #=> 1
* lambda { |x, y=0| }.arity #=> -2
* proc { |(x, y), z=0| }.arity #=> 1
* lambda { |(x, y), z=0| }.arity #=> -2
*/
static VALUE
proc_arity(VALUE self)
{
int arity = rb_proc_arity(self);
return INT2FIX(arity);
}
int
rb_proc_arity(VALUE self)
{
rb_proc_t *proc;
rb_iseq_t *iseq;
GetProcPtr(self, proc);
iseq = proc->block.iseq;
if (iseq) {
if (BUILTIN_TYPE(iseq) != T_NODE) {
if (iseq->arg_rest < 0 && (!proc->is_lambda || iseq->arg_opts == 0)) {
return iseq->argc;
}
else {
return -(iseq->argc + 1 + iseq->arg_post_len);
}
}
else {
NODE *node = (NODE *)iseq;
if (IS_METHOD_PROC_NODE(node)) {
/* method(:foo).to_proc.arity */
return method_arity(node->nd_tval);
}
}
}
return -1;
}
#define get_proc_iseq rb_proc_get_iseq
rb_iseq_t *
rb_proc_get_iseq(VALUE self, int *is_proc)
{
rb_proc_t *proc;
rb_iseq_t *iseq;
GetProcPtr(self, proc);
iseq = proc->block.iseq;
if (is_proc) *is_proc = !proc->is_lambda;
if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) {
NODE *node = (NODE *)iseq;
iseq = 0;
if (IS_METHOD_PROC_NODE(node)) {
/* method(:foo).to_proc */
iseq = rb_method_get_iseq(node->nd_tval);
if (is_proc) *is_proc = 0;
}
}
return iseq;
}
static VALUE
iseq_location(rb_iseq_t *iseq)
{
VALUE loc[2];
if (!iseq) return Qnil;
loc[0] = iseq->location.path;
if (iseq->line_info_table) {
loc[1] = INT2FIX(rb_iseq_first_lineno(iseq));
}
else {
loc[1] = Qnil;
}
return rb_ary_new4(2, loc);
}
/*
* call-seq:
* prc.source_location -> [String, Fixnum]
*
* Returns the Ruby source filename and line number containing this proc
* or +nil+ if this proc was not defined in Ruby (i.e. native)
*/
VALUE
rb_proc_location(VALUE self)
{
return iseq_location(get_proc_iseq(self, 0));
}
static VALUE
unnamed_parameters(int arity)
{
VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity);
int n = (arity < 0) ? ~arity : arity;
ID req, rest;
CONST_ID(req, "req");
a = rb_ary_new3(1, ID2SYM(req));
OBJ_FREEZE(a);
for (; n; --n) {
rb_ary_push(param, a);
}
if (arity < 0) {
CONST_ID(rest, "rest");
rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest)));
}
return param;
}
/*
* call-seq:
* prc.parameters -> array
*
* Returns the parameter information of this proc.
*
* prc = lambda{|x, y=42, *other|}
* prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]]
*/
static VALUE
rb_proc_parameters(VALUE self)
{
int is_proc;
rb_iseq_t *iseq = get_proc_iseq(self, &is_proc);
if (!iseq) {
return unnamed_parameters(rb_proc_arity(self));
}
return rb_iseq_parameters(iseq, is_proc);
}
/*
* call-seq:
* prc == other_proc -> true or false
*
* Returns <code>true</code> if <i>prc</i> is the same object as
* <i>other_proc</i>, or if they are both procs with the same body.
*/
static VALUE
proc_eq(VALUE self, VALUE other)
{
if (self == other) {
return Qtrue;
}
else {
if (rb_obj_is_proc(other)) {
rb_proc_t *p1, *p2;
GetProcPtr(self, p1);
GetProcPtr(other, p2);
if (p1->envval == p2->envval &&
p1->block.iseq->iseq_size == p2->block.iseq->iseq_size &&
p1->block.iseq->local_size == p2->block.iseq->local_size &&
MEMCMP(p1->block.iseq->iseq, p2->block.iseq->iseq, VALUE,
p1->block.iseq->iseq_size) == 0) {
return Qtrue;
}
}
}
return Qfalse;
}
st_index_t
rb_hash_proc(st_index_t hash, VALUE prc)
{
rb_proc_t *proc;
GetProcPtr(prc, proc);
hash = rb_hash_uint(hash, (st_index_t)proc->block.iseq);
hash = rb_hash_uint(hash, (st_index_t)proc->envval);
return rb_hash_uint(hash, (st_index_t)proc->block.ep >> 16);
}
/*
* call-seq:
* prc.hash -> integer
*
* Returns a hash value corresponding to proc body.
*/
static VALUE
proc_hash(VALUE self)
{
st_index_t hash;
hash = rb_hash_start(0);
hash = rb_hash_proc(hash, self);
hash = rb_hash_end(hash);
return LONG2FIX(hash);
}
/*
* call-seq:
* prc.to_s -> string
*
* Returns the unique identifier for this proc, along with
* an indication of where the proc was defined.
*/
static VALUE
proc_to_s(VALUE self)
{
VALUE str = 0;
rb_proc_t *proc;
const char *cname = rb_obj_classname(self);
rb_iseq_t *iseq;
const char *is_lambda;
GetProcPtr(self, proc);
iseq = proc->block.iseq;
is_lambda = proc->is_lambda ? " (lambda)" : "";
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
int first_lineno = 0;
if (iseq->line_info_table) {
first_lineno = rb_iseq_first_lineno(iseq);
}
str = rb_sprintf("#<%s:%p@%s:%d%s>", cname, (void *)self,
RSTRING_PTR(iseq->location.path),
first_lineno, is_lambda);
}
else {
str = rb_sprintf("#<%s:%p%s>", cname, (void *)proc->block.iseq,
is_lambda);
}
if (OBJ_TAINTED(self)) {
OBJ_TAINT(str);
}
return str;
}
/*
* call-seq:
* prc.to_proc -> prc
*
* Part of the protocol for converting objects to <code>Proc</code>
* objects. Instances of class <code>Proc</code> simply return
* themselves.
*/
static VALUE
proc_to_proc(VALUE self)
{
return self;
}
static void
bm_mark(void *ptr)
{
struct METHOD *data = ptr;
rb_gc_mark(data->rclass);
rb_gc_mark(data->recv);
if (data->me) rb_mark_method_entry(data->me);
}
static void
bm_free(void *ptr)
{
struct METHOD *data = ptr;
struct unlinked_method_entry_list_entry *ume = data->ume;
ume->me = data->me;
ume->next = GET_VM()->unlinked_method_entry_list;
GET_VM()->unlinked_method_entry_list = ume;
xfree(ptr);
}
static size_t
bm_memsize(const void *ptr)
{
return ptr ? sizeof(struct METHOD) : 0;
}
static const rb_data_type_t method_data_type = {
"method",
{
bm_mark,
bm_free,
bm_memsize,
},
};
VALUE
rb_obj_is_method(VALUE m)
{
if (rb_typeddata_is_kind_of(m, &method_data_type)) {
return Qtrue;
}
else {
return Qfalse;
}
}
static VALUE
mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
{
VALUE method;
VALUE rclass = klass;
ID rid = id;
struct METHOD *data;
rb_method_entry_t *me, meb;
rb_method_definition_t *def = 0;
rb_method_flag_t flag = NOEX_UNDEF;
again:
me = rb_method_entry(klass, id);
if (UNDEFINED_METHOD_ENTRY_P(me)) {
ID rmiss = rb_intern("respond_to_missing?");
VALUE sym = ID2SYM(id);
if (obj != Qundef && !rb_method_basic_definition_p(klass, rmiss)) {
if (RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue))) {
def = ALLOC(rb_method_definition_t);
def->type = VM_METHOD_TYPE_MISSING;
def->original_id = id;
def->alias_count = 0;
meb.flag = 0;
meb.mark = 0;
meb.called_id = id;
meb.klass = klass;
meb.def = def;
me = &meb;
def = 0;
goto gen_method;
}
}
rb_print_undef(klass, id, 0);
}
def = me->def;
if (flag == NOEX_UNDEF) {
flag = me->flag;
if (scope && (flag & NOEX_MASK) != NOEX_PUBLIC) {
const char *v = "";
switch (flag & NOEX_MASK) {
case NOEX_PRIVATE: v = "private"; break;
case NOEX_PROTECTED: v = "protected"; break;
}
rb_name_error(id, "method `%s' for %s `%s' is %s",
rb_id2name(id),
(RB_TYPE_P(klass, T_MODULE)) ? "module" : "class",
rb_class2name(klass),
v);
}
}
if (def && def->type == VM_METHOD_TYPE_ZSUPER) {
klass = RCLASS_SUPER(me->klass);
id = def->original_id;
goto again;
}
klass = me->klass;
while (rclass != klass &&
(FL_TEST(rclass, FL_SINGLETON) || RB_TYPE_P(rclass, T_ICLASS))) {
rclass = RCLASS_SUPER(rclass);
}
if (RB_TYPE_P(klass, T_ICLASS)) {
klass = RBASIC(klass)->klass;
}
gen_method:
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
data->recv = obj;
data->rclass = rclass;
data->id = rid;
data->me = ALLOC(rb_method_entry_t);
*data->me = *me;
data->me->def->alias_count++;
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
OBJ_INFECT(method, klass);
return method;
}
/**********************************************************************
*
* Document-class : Method
*
* Method objects are created by <code>Object#method</code>, and are
* associated with a particular object (not just with a class). They
* may be used to invoke the method within the object, and as a block
* associated with an iterator. They may also be unbound from one
* object (creating an <code>UnboundMethod</code>) and bound to
* another.
*
* class Thing
* def square(n)
* n*n
* end
* end
* thing = Thing.new
* meth = thing.method(:square)
*
* meth.call(9) #=> 81
* [ 1, 2, 3 ].collect(&meth) #=> [1, 4, 9]
*
*/
/*
* call-seq:
* meth == other_meth -> true or false
*
* Two method objects are equal if they are bound to the same
* object and refer to the same method definition.
*/
static VALUE
method_eq(VALUE method, VALUE other)
{
struct METHOD *m1, *m2;
if (!rb_obj_is_method(other))
return Qfalse;
if (CLASS_OF(method) != CLASS_OF(other))
return Qfalse;
Check_TypedStruct(method, &method_data_type);
m1 = (struct METHOD *)DATA_PTR(method);
m2 = (struct METHOD *)DATA_PTR(other);
if (!rb_method_entry_eq(m1->me, m2->me) ||
m1->rclass != m2->rclass ||
m1->recv != m2->recv) {
return Qfalse;
}
return Qtrue;
}
/*
* call-seq:
* meth.hash -> integer
*
* Returns a hash value corresponding to the method object.
*/
static VALUE
method_hash(VALUE method)
{
struct METHOD *m;
st_index_t hash;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, m);
hash = rb_hash_start((st_index_t)m->rclass);
hash = rb_hash_uint(hash, (st_index_t)m->recv);
hash = rb_hash_method_entry(hash, m->me);
hash = rb_hash_end(hash);
return INT2FIX(hash);
}
/*
* call-seq:
* meth.unbind -> unbound_method
*
* Dissociates <i>meth</i> from its current receiver. The resulting
* <code>UnboundMethod</code> can subsequently be bound to a new object
* of the same class (see <code>UnboundMethod</code>).
*/
static VALUE
method_unbind(VALUE obj)
{
VALUE method;
struct METHOD *orig, *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig);
method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
&method_data_type, data);
data->recv = Qundef;
data->id = orig->id;
data->me = ALLOC(rb_method_entry_t);
*data->me = *orig->me;
if (orig->me->def) orig->me->def->alias_count++;
data->rclass = orig->rclass;
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
OBJ_INFECT(method, obj);
return method;
}
/*
* call-seq:
* meth.receiver -> object
*
* Returns the bound receiver of the method object.
*/
static VALUE
method_receiver(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return data->recv;
}
/*
* call-seq:
* meth.name -> symbol
*
* Returns the name of the method.
*/
static VALUE
method_name(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return ID2SYM(data->id);
}
/*
* call-seq:
* meth.owner -> class_or_module
*
* Returns the class or module that defines the method.
*/
static VALUE
method_owner(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return data->me->klass;
}
void
rb_method_name_error(VALUE klass, VALUE str)
{
const char *s0 = " class";
VALUE c = klass;
if (FL_TEST(c, FL_SINGLETON)) {
VALUE obj = rb_ivar_get(klass, attached);
switch (TYPE(obj)) {
case T_MODULE:
case T_CLASS:
c = obj;
s0 = "";
}
}
else if (RB_TYPE_P(c, T_MODULE)) {
s0 = " module";
}
rb_name_error_str(str, "undefined method `%s' for%s `%s'",
RSTRING_PTR(str), s0, rb_class2name(c));
}
/*
* call-seq:
* obj.method(sym) -> method
*
* Looks up the named method as a receiver in <i>obj</i>, returning a
* <code>Method</code> object (or raising <code>NameError</code>). The
* <code>Method</code> object acts as a closure in <i>obj</i>'s object
* instance, so instance variables and the value of <code>self</code>
* remain available.
*
* class Demo
* def initialize(n)
* @iv = n
* end
* def hello()
* "Hello, @iv = #{@iv}"
* end
* end
*
* k = Demo.new(99)
* m = k.method(:hello)
* m.call #=> "Hello, @iv = 99"
*
* l = Demo.new('Fred')
* m = l.method("hello")
* m.call #=> "Hello, @iv = Fred"
*/
VALUE
rb_obj_method(VALUE obj, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
rb_method_name_error(CLASS_OF(obj), vid);
}
return mnew(CLASS_OF(obj), obj, id, rb_cMethod, FALSE);
}
/*
* call-seq:
* obj.public_method(sym) -> method
*
* Similar to _method_, searches public method only.
*/
VALUE
rb_obj_public_method(VALUE obj, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
rb_method_name_error(CLASS_OF(obj), vid);
}
return mnew(CLASS_OF(obj), obj, id, rb_cMethod, TRUE);
}
/*
* call-seq:
* mod.instance_method(symbol) -> unbound_method
*
* Returns an +UnboundMethod+ representing the given
* instance method in _mod_.
*
* class Interpreter
* def do_a() print "there, "; end
* def do_d() print "Hello "; end
* def do_e() print "!\n"; end
* def do_v() print "Dave"; end
* Dispatcher = {
* "a" => instance_method(:do_a),
* "d" => instance_method(:do_d),
* "e" => instance_method(:do_e),
* "v" => instance_method(:do_v)
* }
* def interpret(string)
* string.each_char {|b| Dispatcher[b].bind(self).call }
* end
* end
*
* interpreter = Interpreter.new
* interpreter.interpret('dave')
*
* <em>produces:</em>
*
* Hello there, Dave!
*/
static VALUE
rb_mod_instance_method(VALUE mod, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
rb_method_name_error(mod, vid);
}
return mnew(mod, Qundef, id, rb_cUnboundMethod, FALSE);
}
/*
* call-seq:
* mod.public_instance_method(symbol) -> unbound_method
*
* Similar to _instance_method_, searches public method only.
*/
static VALUE
rb_mod_public_instance_method(VALUE mod, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
rb_method_name_error(mod, vid);
}
return mnew(mod, Qundef, id, rb_cUnboundMethod, TRUE);
}
/*
* call-seq:
* define_method(symbol, method) -> new_method
* define_method(symbol) { block } -> proc
*
* Defines an instance method in the receiver. The _method_
* parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object.
* If a block is specified, it is used as the method body. This block
* is evaluated using <code>instance_eval</code>, a point that is
* tricky to demonstrate because <code>define_method</code> is private.
* (This is why we resort to the +send+ hack in this example.)
*
* class A
* def fred
* puts "In Fred"
* end
* def create_method(name, &block)
* self.class.send(:define_method, name, &block)
* end
* define_method(:wilma) { puts "Charge it!" }
* end
* class B < A
* define_method(:barney, instance_method(:fred))
* end
* a = B.new
* a.barney
* a.wilma
* a.create_method(:betty) { p self }
* a.betty
*
* <em>produces:</em>
*
* In Fred
* Charge it!
* #<B:0x401b39e8>
*/
static VALUE
rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
{
ID id;
VALUE body;
int noex = NOEX_PUBLIC;
if (argc == 1) {
id = rb_to_id(argv[0]);
body = rb_block_lambda();
}
else {
rb_check_arity(argc, 1, 2);
id = rb_to_id(argv[0]);
body = argv[1];
if (!rb_obj_is_method(body) && !rb_obj_is_proc(body)) {
rb_raise(rb_eTypeError,
"wrong argument type %s (expected Proc/Method)",
rb_obj_classname(body));
}
}
if (rb_obj_is_method(body)) {
struct METHOD *method = (struct METHOD *)DATA_PTR(body);
VALUE rclass = method->rclass;
if (rclass != mod && !RB_TYPE_P(rclass, T_MODULE) &&
!RTEST(rb_class_inherited_p(mod, rclass))) {
if (FL_TEST(rclass, FL_SINGLETON)) {
rb_raise(rb_eTypeError,
"can't bind singleton method to a different class");
}
else {
rb_raise(rb_eTypeError,
"bind argument must be a subclass of %s",
rb_class2name(rclass));
}
}
rb_method_entry_set(mod, id, method->me, noex);
}
else if (rb_obj_is_proc(body)) {
rb_proc_t *proc;
body = proc_dup(body);
GetProcPtr(body, proc);
if (BUILTIN_TYPE(proc->block.iseq) != T_NODE) {
proc->block.iseq->defined_method_id = id;
proc->block.iseq->klass = mod;
proc->is_lambda = TRUE;
proc->is_from_method = TRUE;
}
rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)body, noex);
}
else {
/* type error */
rb_raise(rb_eTypeError, "wrong argument type (expected Proc/Method)");
}
return body;
}
/*
* call-seq:
* define_singleton_method(symbol, method) -> new_method
* define_singleton_method(symbol) { block } -> proc
*
* Defines a singleton method in the receiver. The _method_
* parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object.
* If a block is specified, it is used as the method body.
*
* class A
* class << self
* def class_name
* to_s
* end
* end
* end
* A.define_singleton_method(:who_am_i) do
* "I am: #{class_name}"
* end
* A.who_am_i # ==> "I am: A"
*
* guy = "Bob"
* guy.define_singleton_method(:hello) { "#{self}: Hello there!" }
* guy.hello #=> "Bob: Hello there!"
*/
static VALUE
rb_obj_define_method(int argc, VALUE *argv, VALUE obj)
{
VALUE klass = rb_singleton_class(obj);
return rb_mod_define_method(argc, argv, klass);
}
/*
* MISSING: documentation
*/
static VALUE
method_clone(VALUE self)
{
VALUE clone;
struct METHOD *orig, *data;
TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
CLONESETUP(clone, self);
*data = *orig;
data->me = ALLOC(rb_method_entry_t);
*data->me = *orig->me;
if (data->me->def) data->me->def->alias_count++;
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
return clone;
}
/*
* call-seq:
* meth.call(args, ...) -> obj
* meth[args, ...] -> obj
*
* Invokes the <i>meth</i> with the specified arguments, returning the
* method's return value.
*
* m = 12.method("+")
* m.call(3) #=> 15
* m.call(20) #=> 32
*/
VALUE
rb_method_call(int argc, VALUE *argv, VALUE method)
{
VALUE result = Qnil; /* OK */
struct METHOD *data;
int state;
volatile int safe = -1;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
if (data->recv == Qundef) {
rb_raise(rb_eTypeError, "can't call unbound method; bind first");
}
PUSH_TAG();
if (OBJ_TAINTED(method)) {
safe = rb_safe_level();
if (rb_safe_level() < 4) {
rb_set_safe_level_force(4);
}
}
if ((state = EXEC_TAG()) == 0) {
rb_thread_t *th = GET_THREAD();
PASS_PASSED_BLOCK_TH(th);
result = rb_vm_call(th, data->recv, data->id, argc, argv, data->me);
}
POP_TAG();
if (safe >= 0)
rb_set_safe_level_force(safe);
if (state)
JUMP_TAG(state);
return result;
}
/**********************************************************************
*
* Document-class: UnboundMethod
*
* Ruby supports two forms of objectified methods. Class
* <code>Method</code> is used to represent methods that are associated
* with a particular object: these method objects are bound to that
* object. Bound method objects for an object can be created using
* <code>Object#method</code>.
*
* Ruby also supports unbound methods; methods objects that are not
* associated with a particular object. These can be created either by
* calling <code>Module#instance_method</code> or by calling
* <code>unbind</code> on a bound method object. The result of both of
* these is an <code>UnboundMethod</code> object.
*
* Unbound methods can only be called after they are bound to an
* object. That object must be be a kind_of? the method's original
* class.
*
* class Square
* def area
* @side * @side
* end
* def initialize(side)
* @side = side
* end
* end
*
* area_un = Square.instance_method(:area)
*
* s = Square.new(12)
* area = area_un.bind(s)
* area.call #=> 144
*
* Unbound methods are a reference to the method at the time it was
* objectified: subsequent changes to the underlying class will not
* affect the unbound method.
*
* class Test
* def test
* :original
* end
* end
* um = Test.instance_method(:test)
* class Test
* def test
* :modified
* end
* end
* t = Test.new
* t.test #=> :modified
* um.bind(t).call #=> :original
*
*/
/*
* call-seq:
* umeth.bind(obj) -> method
*
* Bind <i>umeth</i> to <i>obj</i>. If <code>Klass</code> was the class
* from which <i>umeth</i> was obtained,
* <code>obj.kind_of?(Klass)</code> must be true.
*
* class A
* def test
* puts "In test, class = #{self.class}"
* end
* end
* class B < A
* end
* class C < B
* end
*
*
* um = B.instance_method(:test)
* bm = um.bind(C.new)
* bm.call
* bm = um.bind(B.new)
* bm.call
* bm = um.bind(A.new)
* bm.call
*
* <em>produces:</em>
*
* In test, class = C
* In test, class = B
* prog.rb:16:in `bind': bind argument must be an instance of B (TypeError)
* from prog.rb:16
*/
static VALUE
umethod_bind(VALUE method, VALUE recv)
{
struct METHOD *data, *bound;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
if (data->rclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, data->rclass)) {
if (FL_TEST(data->rclass, FL_SINGLETON)) {
rb_raise(rb_eTypeError,
"singleton method called for a different object");
}
else {
rb_raise(rb_eTypeError, "bind argument must be an instance of %s",
rb_class2name(data->rclass));
}
}
method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
*bound = *data;
bound->me = ALLOC(rb_method_entry_t);
*bound->me = *data->me;
if (bound->me->def) bound->me->def->alias_count++;
bound->recv = recv;
bound->rclass = CLASS_OF(recv);
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
return method;
}
int
rb_method_entry_arity(const rb_method_entry_t *me)
{
const rb_method_definition_t *def = me->def;
if (!def) return 0;
switch (def->type) {
case VM_METHOD_TYPE_CFUNC:
if (def->body.cfunc.argc < 0)
return -1;
return check_argc(def->body.cfunc.argc);
case VM_METHOD_TYPE_ZSUPER:
return -1;
case VM_METHOD_TYPE_ATTRSET:
return 1;
case VM_METHOD_TYPE_IVAR:
return 0;
case VM_METHOD_TYPE_BMETHOD:
return rb_proc_arity(def->body.proc);
case VM_METHOD_TYPE_ISEQ: {
rb_iseq_t *iseq = def->body.iseq;
if (iseq->arg_rest == -1 && iseq->arg_opts == 0) {
return iseq->argc;
}
else {
return -(iseq->argc + 1 + iseq->arg_post_len);
}
}
case VM_METHOD_TYPE_UNDEF:
case VM_METHOD_TYPE_NOTIMPLEMENTED:
return 0;
case VM_METHOD_TYPE_MISSING:
return -1;
case VM_METHOD_TYPE_OPTIMIZED: {
switch (def->body.optimize_type) {
case OPTIMIZED_METHOD_TYPE_SEND:
return -1;
default:
break;
}
}
}
rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type);
UNREACHABLE;
}
/*
* call-seq:
* meth.arity -> fixnum
*
* Returns an indication of the number of arguments accepted by a
* method. Returns a nonnegative integer for methods that take a fixed
* number of arguments. For Ruby methods that take a variable number of
* arguments, returns -n-1, where n is the number of required
* arguments. For methods written in C, returns -1 if the call takes a
* variable number of arguments.
*
* class C
* def one; end
* def two(a); end
* def three(*a); end
* def four(a, b); end
* def five(a, b, *c); end
* def six(a, b, *c, &d); end
* end
* c = C.new
* c.method(:one).arity #=> 0
* c.method(:two).arity #=> 1
* c.method(:three).arity #=> -1
* c.method(:four).arity #=> 2
* c.method(:five).arity #=> -3
* c.method(:six).arity #=> -3
*
* "cat".method(:size).arity #=> 0
* "cat".method(:replace).arity #=> 1
* "cat".method(:squeeze).arity #=> -1
* "cat".method(:count).arity #=> -1
*/
static VALUE
method_arity_m(VALUE method)
{
int n = method_arity(method);
return INT2FIX(n);
}
static int
method_arity(VALUE method)
{
struct METHOD *data;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
return rb_method_entry_arity(data->me);
}
int
rb_mod_method_arity(VALUE mod, ID id)
{
rb_method_entry_t *me = rb_method_entry(mod, id);
return rb_method_entry_arity(me);
}
int
rb_obj_method_arity(VALUE obj, ID id)
{
return rb_mod_method_arity(CLASS_OF(obj), id);
}
static inline rb_method_definition_t *
method_get_def(VALUE method)
{
struct METHOD *data;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
return data->me->def;
}
static rb_iseq_t *
method_get_iseq(rb_method_definition_t *def)
{
switch (def->type) {
case VM_METHOD_TYPE_BMETHOD:
return get_proc_iseq(def->body.proc, 0);
case VM_METHOD_TYPE_ISEQ:
return def->body.iseq;
default:
return 0;
}
}
rb_iseq_t *
rb_method_get_iseq(VALUE method)
{
return method_get_iseq(method_get_def(method));
}
/*
* call-seq:
* meth.source_location -> [String, Fixnum]
*
* Returns the Ruby source filename and line number containing this method
* or nil if this method was not defined in Ruby (i.e. native)
*/
VALUE
rb_method_location(VALUE method)
{
rb_method_definition_t *def = method_get_def(method);
if (def->type == VM_METHOD_TYPE_ATTRSET || def->type == VM_METHOD_TYPE_IVAR) {
if (!def->body.attr.location)
return Qnil;
return rb_ary_dup(def->body.attr.location);
}
return iseq_location(method_get_iseq(def));
}
/*
* call-seq:
* meth.parameters -> array
*
* Returns the parameter information of this method.
*/
static VALUE
rb_method_parameters(VALUE method)
{
rb_iseq_t *iseq = rb_method_get_iseq(method);
if (!iseq) {
return unnamed_parameters(method_arity(method));
}
return rb_iseq_parameters(iseq, 0);
}
/*
* call-seq:
* meth.to_s -> string
* meth.inspect -> string
*
* Returns the name of the underlying method.
*
* "cat".method(:count).inspect #=> "#<Method: String#count>"
*/
static VALUE
method_inspect(VALUE method)
{
struct METHOD *data;
VALUE str;
const char *s;
const char *sharp = "#";
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
str = rb_str_buf_new2("#<");
s = rb_obj_classname(method);
rb_str_buf_cat2(str, s);
rb_str_buf_cat2(str, ": ");
if (FL_TEST(data->me->klass, FL_SINGLETON)) {
VALUE v = rb_ivar_get(data->me->klass, attached);
if (data->recv == Qundef) {
rb_str_buf_append(str, rb_inspect(data->me->klass));
}
else if (data->recv == v) {
rb_str_buf_append(str, rb_inspect(v));
sharp = ".";
}
else {
rb_str_buf_append(str, rb_inspect(data->recv));
rb_str_buf_cat2(str, "(");
rb_str_buf_append(str, rb_inspect(v));
rb_str_buf_cat2(str, ")");
sharp = ".";
}
}
else {
rb_str_buf_cat2(str, rb_class2name(data->rclass));
if (data->rclass != data->me->klass) {
rb_str_buf_cat2(str, "(");
rb_str_buf_cat2(str, rb_class2name(data->me->klass));
rb_str_buf_cat2(str, ")");
}
}
rb_str_buf_cat2(str, sharp);
rb_str_append(str, rb_id2str(data->me->def->original_id));
if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
rb_str_buf_cat2(str, " (not-implemented)");
}
rb_str_buf_cat2(str, ">");
return str;
}
static VALUE
mproc(VALUE method)
{
return rb_funcall(Qnil, rb_intern("proc"), 0);
}
static VALUE
mlambda(VALUE method)
{
return rb_funcall(Qnil, rb_intern("lambda"), 0);
}
static VALUE
bmcall(VALUE args, VALUE method)
{
volatile VALUE a;
VALUE ret;
int argc;
if (CLASS_OF(args) != rb_cArray) {
args = rb_ary_new3(1, args);
argc = 1;
}
else {
argc = check_argc(RARRAY_LEN(args));
}
ret = rb_method_call(argc, RARRAY_PTR(args), method);
RB_GC_GUARD(a) = args;
return ret;
}
VALUE
rb_proc_new(
VALUE (*func)(ANYARGS), /* VALUE yieldarg[, VALUE procarg] */
VALUE val)
{
VALUE procval = rb_iterate(mproc, 0, func, val);
return procval;
}
/*
* call-seq:
* meth.to_proc -> prc
*
* Returns a <code>Proc</code> object corresponding to this method.
*/
static VALUE
method_proc(VALUE method)
{
VALUE procval;
rb_proc_t *proc;
/*
* class Method
* def to_proc
* proc{|*args|
* self.call(*args)
* }
* end
* end
*/
procval = rb_iterate(mlambda, 0, bmcall, method);
GetProcPtr(procval, proc);
proc->is_from_method = 1;
return procval;
}
/*
* call_seq:
* local_jump_error.exit_value -> obj
*
* Returns the exit value associated with this +LocalJumpError+.
*/
static VALUE
localjump_xvalue(VALUE exc)
{
return rb_iv_get(exc, "@exit_value");
}
/*
* call-seq:
* local_jump_error.reason -> symbol
*
* The reason this block was terminated:
* :break, :redo, :retry, :next, :return, or :noreason.
*/
static VALUE
localjump_reason(VALUE exc)
{
return rb_iv_get(exc, "@reason");
}
/*
* call-seq:
* prc.binding -> binding
*
* Returns the binding associated with <i>prc</i>. Note that
* <code>Kernel#eval</code> accepts either a <code>Proc</code> or a
* <code>Binding</code> object as its second parameter.
*
* def fred(param)
* proc {}
* end
*
* b = fred(99)
* eval("param", b.binding) #=> 99
*/
static VALUE
proc_binding(VALUE self)
{
rb_proc_t *proc;
VALUE bindval;
rb_binding_t *bind;
GetProcPtr(self, proc);
if (RB_TYPE_P((VALUE)proc->block.iseq, T_NODE)) {
if (!IS_METHOD_PROC_NODE((NODE *)proc->block.iseq)) {
rb_raise(rb_eArgError, "Can't create Binding from C level Proc");
}
}
bindval = binding_alloc(rb_cBinding);
GetBindingPtr(bindval, bind);
bind->env = proc->envval;
if (RUBY_VM_NORMAL_ISEQ_P(proc->block.iseq)) {
bind->path = proc->block.iseq->location.path;
bind->first_lineno = rb_iseq_first_lineno(proc->block.iseq);
}
else {
bind->path = Qnil;
bind->first_lineno = 0;
}
return bindval;
}
static VALUE curry(VALUE dummy, VALUE args, int argc, VALUE *argv, VALUE passed_proc);
static VALUE
make_curry_proc(VALUE proc, VALUE passed, VALUE arity)
{
VALUE args = rb_ary_new3(3, proc, passed, arity);
rb_proc_t *procp;
int is_lambda;
GetProcPtr(proc, procp);
is_lambda = procp->is_lambda;
rb_ary_freeze(passed);
rb_ary_freeze(args);
proc = rb_proc_new(curry, args);
GetProcPtr(proc, procp);
procp->is_lambda = is_lambda;
return proc;
}
static VALUE
curry(VALUE dummy, VALUE args, int argc, VALUE *argv, VALUE passed_proc)
{
VALUE proc, passed, arity;
proc = RARRAY_PTR(args)[0];
passed = RARRAY_PTR(args)[1];
arity = RARRAY_PTR(args)[2];
passed = rb_ary_plus(passed, rb_ary_new4(argc, argv));
rb_ary_freeze(passed);
if (RARRAY_LEN(passed) < FIX2INT(arity)) {
if (!NIL_P(passed_proc)) {
rb_warn("given block not used");
}
arity = make_curry_proc(proc, passed, arity);
return arity;
}
else {
return rb_proc_call_with_block(proc, check_argc(RARRAY_LEN(passed)),
RARRAY_PTR(passed), passed_proc);
}
}
/*
* call-seq:
* prc.curry -> a_proc
* prc.curry(arity) -> a_proc
*
* Returns a curried proc. If the optional <i>arity</i> argument is given,
* it determines the number of arguments.
* A curried proc receives some arguments. If a sufficient number of
* arguments are supplied, it passes the supplied arguments to the original
* proc and returns the result. Otherwise, returns another curried proc that
* takes the rest of arguments.
*
* b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
* p b.curry[1][2][3] #=> 6
* p b.curry[1, 2][3, 4] #=> 6
* p b.curry(5)[1][2][3][4][5] #=> 6
* p b.curry(5)[1, 2][3, 4][5] #=> 6
* p b.curry(1)[1] #=> 1
*
* b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
* p b.curry[1][2][3] #=> 6
* p b.curry[1, 2][3, 4] #=> 10
* p b.curry(5)[1][2][3][4][5] #=> 15
* p b.curry(5)[1, 2][3, 4][5] #=> 15
* p b.curry(1)[1] #=> 1
*
* b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
* p b.curry[1][2][3] #=> 6
* p b.curry[1, 2][3, 4] #=> wrong number of arguments (4 for 3)
* p b.curry(5) #=> wrong number of arguments (5 for 3)
* p b.curry(1) #=> wrong number of arguments (1 for 3)
*
* b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
* p b.curry[1][2][3] #=> 6
* p b.curry[1, 2][3, 4] #=> 10
* p b.curry(5)[1][2][3][4][5] #=> 15
* p b.curry(5)[1, 2][3, 4][5] #=> 15
* p b.curry(1) #=> wrong number of arguments (1 for 3)
*
* b = proc { :foo }
* p b.curry[] #=> :foo
*/
static VALUE
proc_curry(int argc, VALUE *argv, VALUE self)
{
int sarity, marity = rb_proc_arity(self);
VALUE arity, opt = Qfalse;
if (marity < 0) {
marity = -marity - 1;
opt = Qtrue;
}
rb_scan_args(argc, argv, "01", &arity);
if (NIL_P(arity)) {
arity = INT2FIX(marity);
}
else {
sarity = FIX2INT(arity);
if (rb_proc_lambda_p(self) && (sarity < marity || (sarity > marity && !opt))) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", sarity, marity);
}
}
return make_curry_proc(self, rb_ary_new(), arity);
}
/*
* Document-class: LocalJumpError
*
* Raised when Ruby can't yield as requested.
*
* A typical scenario is attempting to yield when no block is given:
*
* def call_block
* yield 42
* end
* call_block
*
* <em>raises the exception:</em>
*
* LocalJumpError: no block given (yield)
*
* A more subtle example:
*
* def get_me_a_return
* Proc.new { return 42 }
* end
* get_me_a_return.call
*
* <em>raises the exception:</em>
*
* LocalJumpError: unexpected return
*/
/*
* Document-class: SystemStackError
*
* Raised in case of a stack overflow.
*
* def me_myself_and_i
* me_myself_and_i
* end
* me_myself_and_i
*
* <em>raises the exception:</em>
*
* SystemStackError: stack level too deep
*/
/*
* <code>Proc</code> objects are blocks of code that have been bound to
* a set of local variables. Once bound, the code may be called in
* different contexts and still access those variables.
*
* def gen_times(factor)
* return Proc.new {|n| n*factor }
* end
*
* times3 = gen_times(3)
* times5 = gen_times(5)
*
* times3.call(12) #=> 36
* times5.call(5) #=> 25
* times3.call(times5.call(4)) #=> 60
*
*/
void
Init_Proc(void)
{
/* Proc */
rb_cProc = rb_define_class("Proc", rb_cObject);
rb_undef_alloc_func(rb_cProc);
rb_define_singleton_method(rb_cProc, "new", rb_proc_s_new, -1);
#if 0 /* incomplete. */
rb_add_method(rb_cProc, rb_intern("call"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
rb_add_method(rb_cProc, rb_intern("[]"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
rb_add_method(rb_cProc, rb_intern("==="), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
rb_add_method(rb_cProc, rb_intern("yield"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
#else
rb_define_method(rb_cProc, "call", proc_call, -1);
rb_define_method(rb_cProc, "[]", proc_call, -1);
rb_define_method(rb_cProc, "===", proc_call, -1);
rb_define_method(rb_cProc, "yield", proc_call, -1);
#endif
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "clone", proc_clone, 0);
rb_define_method(rb_cProc, "dup", proc_dup, 0);
rb_define_method(rb_cProc, "==", proc_eq, 1);
rb_define_method(rb_cProc, "eql?", proc_eq, 1);
rb_define_method(rb_cProc, "hash", proc_hash, 0);
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
rb_define_method(rb_cProc, "lambda?", rb_proc_lambda_p, 0);
rb_define_method(rb_cProc, "binding", proc_binding, 0);
rb_define_method(rb_cProc, "curry", proc_curry, -1);
rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);
/* Exceptions */
rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
rb_define_method(rb_eLocalJumpError, "exit_value", localjump_xvalue, 0);
rb_define_method(rb_eLocalJumpError, "reason", localjump_reason, 0);
rb_eSysStackError = rb_define_class("SystemStackError", rb_eException);
sysstack_error = rb_exc_new3(rb_eSysStackError,
rb_obj_freeze(rb_str_new2("stack level too deep")));
OBJ_TAINT(sysstack_error);
/* utility functions */
rb_define_global_function("proc", rb_block_proc, 0);
rb_define_global_function("lambda", proc_lambda, 0);
/* Method */
rb_cMethod = rb_define_class("Method", rb_cObject);
rb_undef_alloc_func(rb_cMethod);
rb_undef_method(CLASS_OF(rb_cMethod), "new");
rb_define_method(rb_cMethod, "==", method_eq, 1);
rb_define_method(rb_cMethod, "eql?", method_eq, 1);
rb_define_method(rb_cMethod, "hash", method_hash, 0);
rb_define_method(rb_cMethod, "clone", method_clone, 0);
rb_define_method(rb_cMethod, "call", rb_method_call, -1);
rb_define_method(rb_cMethod, "[]", rb_method_call, -1);
rb_define_method(rb_cMethod, "arity", method_arity_m, 0);
rb_define_method(rb_cMethod, "inspect", method_inspect, 0);
rb_define_method(rb_cMethod, "to_s", method_inspect, 0);
rb_define_method(rb_cMethod, "to_proc", method_proc, 0);
rb_define_method(rb_cMethod, "receiver", method_receiver, 0);
rb_define_method(rb_cMethod, "name", method_name, 0);
rb_define_method(rb_cMethod, "owner", method_owner, 0);
rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
rb_define_method(rb_cMethod, "source_location", rb_method_location, 0);
rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0);
rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1);
/* UnboundMethod */
rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
rb_undef_alloc_func(rb_cUnboundMethod);
rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
rb_define_method(rb_cUnboundMethod, "eql?", method_eq, 1);
rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "name", method_name, 0);
rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0);
rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0);
rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0);
/* Module#*_method */
rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1);
rb_define_method(rb_cModule, "public_instance_method", rb_mod_public_instance_method, 1);
rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1);
/* Kernel */
rb_define_method(rb_mKernel, "define_singleton_method", rb_obj_define_method, -1);
}
/*
* Objects of class <code>Binding</code> encapsulate the execution
* context at some particular place in the code and retain this context
* for future use. The variables, methods, value of <code>self</code>,
* and possibly an iterator block that can be accessed in this context
* are all retained. Binding objects can be created using
* <code>Kernel#binding</code>, and are made available to the callback
* of <code>Kernel#set_trace_func</code>.
*
* These binding objects can be passed as the second argument of the
* <code>Kernel#eval</code> method, establishing an environment for the
* evaluation.
*
* class Demo
* def initialize(n)
* @secret = n
* end
* def get_binding
* return binding()
* end
* end
*
* k1 = Demo.new(99)
* b1 = k1.get_binding
* k2 = Demo.new(-3)
* b2 = k2.get_binding
*
* eval("@secret", b1) #=> 99
* eval("@secret", b2) #=> -3
* eval("@secret") #=> nil
*
* Binding objects have no class-specific methods.
*
*/
void
Init_Binding(void)
{
rb_cBinding = rb_define_class("Binding", rb_cObject);
rb_undef_alloc_func(rb_cBinding);
rb_undef_method(CLASS_OF(rb_cBinding), "new");
rb_define_method(rb_cBinding, "clone", binding_clone, 0);
rb_define_method(rb_cBinding, "dup", binding_dup, 0);
rb_define_method(rb_cBinding, "eval", bind_eval, -1);
rb_define_global_function("binding", rb_f_binding, 0);
attached = rb_intern("__attached__");
}
Jump to Line
Something went wrong with that request. Please try again.