mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
90a6dce041
This method is similar to VM_METHOD_TYPE_CFUNC methods, but called cfunc without building new frame (does not push new control frame). If error is occured in cfunc, the backtrace only shows caller frame and upper. This kind of methods can be added by rb_define_method_fast(). This feature is similar to specialized instructions (opt_plus, etc), but more flexible (but a bit slower). * class.c (rb_define_method_fast): added. Maybe it will be renamed soon. * vm_insnhelper.c (vm_call_method): support method type VM_METHOD_TYPE_CFUNC_FAST. * proc.c (rb_method_entry_arity): catch up new method type. * vm_method.c (rb_add_method_cfunc_fast): added. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37198 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2342 lines
57 KiB
C
2342 lines
57 KiB
C
/**********************************************************************
|
|
|
|
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;
|
|
VALUE defined_class;
|
|
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_with_cfp(rb_thread_t *th, rb_control_frame_t *src_cfp)
|
|
{
|
|
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, src_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;
|
|
}
|
|
|
|
VALUE
|
|
rb_binding_new(void)
|
|
{
|
|
rb_thread_t *th = GET_THREAD();
|
|
return rb_binding_new_with_cfp(th, th->cfp);
|
|
}
|
|
|
|
/*
|
|
* 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, 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,
|
|
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, 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->defined_class);
|
|
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, defined_class;
|
|
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, &defined_class);
|
|
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(defined_class);
|
|
id = def->original_id;
|
|
goto again;
|
|
}
|
|
|
|
klass = defined_class;
|
|
|
|
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->defined_class = defined_class;
|
|
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->defined_class = orig->defined_class;
|
|
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;
|
|
proc->block.klass = mod;
|
|
}
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* method.clone -> new_method
|
|
*
|
|
* Returns a clone of this method.
|
|
*
|
|
* class A
|
|
* def foo
|
|
* return "bar"
|
|
* end
|
|
* end
|
|
*
|
|
* m = A.new.method(:foo)
|
|
* m.call # => "bar"
|
|
* n = m.clone.call # => "bar"
|
|
*/
|
|
|
|
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, data->defined_class);
|
|
}
|
|
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_FAST:
|
|
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, 0);
|
|
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_alias(rb_cProc, "inspect", "to_s");
|
|
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__");
|
|
}
|
|
|