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

1678 lines (1502 sloc) 43.15 kb
/*
* MacRuby implementation of Ruby 1.9's proc.c.
*
* This file is covered by the Ruby license. See COPYING for more details.
*
* Copyright (C) 2012, The MacRuby Team. All rights reserved.
* Copyright (C) 2007-2011, Apple Inc. All rights reserved.
* Copyright (C) 2004-2007 Koichi Sasada
*/
#include "macruby_internal.h"
#include "ruby/node.h"
#include "vm.h"
#include "class.h"
#define GetCoreDataFromValue(obj, type, ptr) do { \
ptr = (type*)DATA_PTR(obj); \
} while (0)
#define GetProcPtr(obj, ptr) GetCoreDataFromValue(obj, rb_vm_block_t, ptr)
#define GetBindingPtr(obj, ptr) \
GetCoreDataFromValue((obj), rb_vm_binding_t, (ptr))
VALUE rb_cUnboundMethod;
VALUE rb_cMethod;
VALUE rb_cBinding;
VALUE rb_cProc;
/* Proc */
VALUE
rb_proc_alloc(VALUE klass)
{
VALUE obj;
rb_vm_block_t *proc;
obj = Data_Make_Struct(klass, rb_vm_block_t, NULL, NULL, proc);
MEMZERO(proc, rb_vm_block_t, 1);
return obj;
}
VALUE
rb_proc_alloc_with_block(VALUE klass, rb_vm_block_t *proc)
{
if (proc->proc != Qnil) {
return proc->proc;
}
VALUE obj;
obj = Data_Wrap_Struct(klass, NULL, NULL, proc);
proc->proc = obj; // weak
rb_vm_block_make_detachable_proc(proc);
return obj;
}
VALUE
rb_obj_is_proc(VALUE obj)
{
if (CLASS_OF(obj) == rb_cProc) {
return Qtrue;
}
else {
return Qfalse;
}
}
static inline bool
rb_obj_is_method(VALUE obj)
{
VALUE klass = CLASS_OF(obj);
return (klass == rb_cMethod) || (klass == rb_cUnboundMethod);
}
static VALUE
proc_dup(VALUE self, SEL sel)
{
rb_vm_block_t *src;
GetProcPtr(self, src);
return Data_Wrap_Struct(CLASS_OF(self), NULL, NULL, src);
}
static VALUE
proc_clone(VALUE self, SEL sel)
{
VALUE procval = proc_dup(self, 0);
CLONESETUP(procval, self);
return procval;
}
/*
* call-seq:
* prc.lambda? => true or false
*
* Returns true for a Proc object which argument handling is rigid.
* Such procs are typically generated by lambda.
*
* A Proc object generated by proc ignore extra arguments.
*
* proc {|a,b| [a,b] }.call(1,2,3) => [1,2]
*
* It provides nil for lacked arguments.
*
* proc {|a,b| [a,b] }.call(1) => [1,nil]
*
* It expand 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.
*
* lambda {}.lambda? => true
* proc {}.lambda? => false
*
* Proc.new is same as proc.
*
* Proc.new {}.lambda? => false
*
* lambda, proc and Proc.new preserves the tricks of
* a Proc object given by & 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 & argument has the tricks
*
* def n(&b) b.lambda? end
* n {} => false
*
* The & argument preserves the tricks if a Proc object is given
* by & 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 same as method definition.
* The defined method has no tricks.
*
* class C
* define_method(:d) {}
* end
* C.new.e(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 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 is for a wrapper of define_method.
* It eases defining a method defining method which defines a usual method which has no tricks.
*
* class << C
* def def2(name, &body)
* define_method(name, &body)
* end
* end
* class C
* def2(:f) {}
* end
* C.new.f(1,2) => ArgumentError
*
* The wrapper, def2, defines a method which has no tricks.
*
*/
static VALUE
proc_lambda_p(VALUE procval, SEL sel)
{
rb_vm_block_t *proc;
GetProcPtr(procval, proc);
return (proc->flags & VM_BLOCK_LAMBDA) == VM_BLOCK_LAMBDA
? Qtrue : Qfalse;
}
VALUE
rb_proc_lambda_p(VALUE procval)
{
return proc_lambda_p(procval, 0);
}
/* Binding */
static VALUE
binding_alloc(VALUE klass)
{
VALUE obj;
rb_vm_binding_t *bind;
obj = Data_Make_Struct(klass, rb_vm_binding_t,
NULL, NULL, bind);
return obj;
}
static VALUE
binding_dup(VALUE self, SEL sel)
{
VALUE bindval = binding_alloc(rb_cBinding);
rb_vm_binding_t *src, *dst;
GetBindingPtr(self, src);
GetBindingPtr(bindval, dst);
GC_WB(&dst->self, src->self);
GC_WB(&dst->next, src->next);
GC_WB(&dst->locals, src->locals);
GC_WB(&dst->outer_stack, src->outer_stack);
GC_WB(&dst->block, src->block);
return bindval;
}
static VALUE
binding_clone(VALUE self, SEL sel)
{
VALUE bindval = binding_dup(self, 0);
CLONESETUP(bindval, self);
return bindval;
}
VALUE
rb_binding_new(void)
{
rb_vm_binding_t *bind = rb_vm_current_binding();
if (bind == NULL) {
// Should very rarely happen (when the compiler does not generate a
// binding).
rb_raise(rb_eRuntimeError, "current binding not defined");
}
return Data_Wrap_Struct(rb_cBinding, NULL, NULL, bind);
}
VALUE
rb_binding_new_from_binding(rb_vm_binding_t *bind)
{
return Data_Wrap_Struct(rb_cBinding, NULL, NULL, bind);
}
/*
* 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. Also see the description of class +Binding+.
*
* def getBinding(param)
* return binding
* end
* b = getBinding("hello")
* eval("param", b) #=> "hello"
*/
static VALUE
rb_f_binding(VALUE self, SEL sel)
{
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 getBinding(param)
* return binding
* end
* b = getBinding("hello")
* b.eval("param") #=> "hello"
*/
VALUE rb_f_eval(VALUE self, SEL sel, int argc, VALUE *argv);
static VALUE
bind_eval(VALUE bindval, SEL sel, int argc, VALUE *argv)
{
VALUE args[4];
rb_scan_args(argc, argv, "12", &args[0], &args[2], &args[3]);
args[1] = bindval;
return rb_f_eval(Qnil, 0, argc+1, args /* self will be searched in eval */);
}
static VALUE
proc_new(VALUE klass, bool is_lambda)
{
rb_vm_block_t *block = rb_vm_first_block();
if (block == NULL) {
rb_raise(rb_eArgError,
"tried to create Proc object without a block");
}
if (is_lambda) {
block->flags |= VM_BLOCK_LAMBDA;
}
return rb_proc_alloc_with_block(klass, block);
}
/*
* 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(VALUE klass, SEL sel, int argc, VALUE *argv)
{
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_proc_imp(void)
{
return rb_block_proc();
}
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(VALUE klass, SEL sel)
{
return rb_block_lambda();
}
/* CHECKME: are the argument checking semantics correct? */
/*
* call-seq:
* prc.call(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).
*
* For procs created using <code>Kernel.proc</code>, generates an
* error if the wrong number of parameters
* are passed to a proc with multiple parameters. For procs created using
* <code>Proc.new</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 = Proc.new {|a,b| a}
* a_proc.call(1,2,3)
*
* <em>produces:</em>
*
* prog.rb:5: wrong number of arguments (3 for 2) (ArgumentError)
* from prog.rb:4:in `call'
* from prog.rb:5
*/
static VALUE
proc_call(VALUE procval, SEL sel, int argc, const VALUE *argv)
{
#if 0
rb_proc_t *proc;
rb_block_t *blockptr = 0;
GetProcPtr(procval, proc);
if (BUILTIN_TYPE(proc->block.iseq) != T_NODE &&
proc->block.iseq->arg_block != -1) {
if (rb_block_given_p()) {
rb_proc_t *proc;
VALUE procval;
procval = rb_block_proc();
GetProcPtr(procval, proc);
blockptr = &proc->block;
}
}
return vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
argc, argv, blockptr);
#endif
rb_vm_block_t *proc;
GetProcPtr(procval, proc);
return rb_vm_block_eval(proc, argc, argv);
}
VALUE
rb_proc_call(VALUE self, VALUE args)
{
#if 0
rb_proc_t *proc;
GetProcPtr(self, proc);
return vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
RARRAY_LEN(args), RARRAY_PTR(args), 0);
#endif
return proc_call(self, 0, RARRAY_LEN(args), RARRAY_PTR(args));
}
VALUE
rb_proc_call2(VALUE self, int argc, VALUE *argv)
{
return proc_call(self, 0, argc, argv);
}
VALUE
rb_proc_check_and_call(VALUE proc, int argc, VALUE *argv)
{
VALUE tmp = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
if (NIL_P(tmp)) {
rb_raise(rb_eTypeError,
"wrong type %s (expected Proc)",
rb_obj_classname(proc));
}
proc = tmp;
const int arity = rb_proc_arity(proc);
if (arity != argc) {
rb_raise(rb_eArgError, "expected Proc with %d arguments (got %d)",
argc, arity);
}
return proc_call(proc, 0, argc, argv);
}
/*
* 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.new {}.arity #=> 0
* Proc.new {||}.arity #=> 0
* Proc.new {|a|}.arity #=> 1
* Proc.new {|a,b|}.arity #=> 2
* Proc.new {|a,b,c|}.arity #=> 3
* Proc.new {|*a|}.arity #=> -1
* Proc.new {|a,*b|}.arity #=> -2
* Proc.new {|a,*b, c|}.arity #=> -3
*/
static inline int
method_arity(VALUE method)
{
rb_vm_method_t *data;
Data_Get_Struct(method, rb_vm_method_t, data);
return data->arity;
}
static VALUE
proc_arity(VALUE self, SEL sel)
{
return INT2FIX(rb_proc_arity(self));
}
int
rb_proc_arity(VALUE proc)
{
rb_vm_block_t *b;
GetProcPtr(proc, b);
return rb_vm_arity_n(b->arity);
}
#if 0
static rb_iseq_t *
get_proc_iseq(VALUE self)
{
rb_proc_t *proc;
rb_iseq_t *iseq;
GetProcPtr(self, proc);
iseq = proc->block.iseq;
if (!RUBY_VM_NORMAL_ISEQ_P(iseq))
return 0;
return iseq;
}
VALUE
rb_proc_location(VALUE self)
{
rb_iseq_t *iseq = get_proc_iseq(self);
VALUE loc[2];
if (!iseq) return Qnil;
loc[0] = iseq->filename;
if (iseq->insn_info_table) {
loc[1] = INT2FIX(iseq->insn_info_table[0].line_no);
}
else {
loc[1] = Qnil;
}
return rb_ary_new4(2, loc);
}
#endif
/*
* call-seq:
* prc == other_proc => true or false
*
* Return <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, SEL sel, VALUE other)
{
if (self == other) {
return Qtrue;
}
else if (rb_obj_is_kind_of(other, rb_cProc)) {
rb_vm_block_t *self_b, *other_b;
GetProcPtr(self, self_b);
GetProcPtr(other, other_b);
return self_b == other_b ? Qtrue : Qfalse;
}
return Qfalse;
}
/*
* call-seq:
* prc.hash => integer
*
* Return hash value corresponding to proc body.
*/
static VALUE
proc_hash(VALUE self, SEL sel)
{
rb_vm_block_t *b;
GetProcPtr(self, b);
return LONG2FIX(b);
}
/*
* call-seq:
* prc.to_s => string
*
* Shows the unique identifier for this proc, along with
* an indication of where the proc was defined.
*/
static VALUE
proc_to_s(VALUE self, SEL sel)
{
const char *cname = rb_obj_classname(self);
rb_vm_block_t *proc;
GetProcPtr(self, proc);
const char *is_lambda = (proc->flags & VM_BLOCK_LAMBDA) ? " (lambda)" : "";
VALUE str = rb_sprintf("#<%s:%p%s>", cname, (void *)self, is_lambda);
if (OBJ_TAINTED(self)) {
OBJ_TAINT(str);
}
return str;
}
#if 0 // TODO
static VALUE
proc_to_s(VALUE self, SEL sel)
{
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 line_no = 0;
if (iseq->insn_info_table) {
line_no = iseq->insn_info_table[0].line_no;
}
str = rb_sprintf("#<%s:%p@%s:%d%s>", cname, (void *)self,
RSTRING_PTR(iseq->filename),
line_no, is_lambda);
}
else {
str = rb_sprintf("#<%s:%p%s>", cname, proc->block.iseq,
is_lambda);
}
if (OBJ_TAINTED(self)) {
OBJ_TAINT(str);
}
return str;
}
#endif
/*
* 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, SEL sel)
{
return self;
}
NODE *rb_get_method_body(VALUE klass, ID id, ID *idp);
void rb_print_undef(VALUE klass, ID id, int scope);
static inline VALUE
mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
{
rb_vm_method_t *m = rb_vm_get_method(klass, obj, id, scope);
assert(m != NULL);
if (m->node) {
const int flag = m->node->flags & NOEX_MASK;
if (scope && flag != NOEX_PUBLIC) {
const char *v = "";
switch (flag) {
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),
(TYPE(klass) == T_MODULE) ? "module" : "class",
rb_class2name(klass),
v);
}
}
return Data_Wrap_Struct(mclass, NULL, NULL, m);
}
/**********************************************************************
*
* 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 that are bound to the same
* object and contain the same body.
*/
static VALUE
method_eq(VALUE method, SEL sel, VALUE other)
{
rb_vm_method_t *m1, *m2;
if (CLASS_OF(method) != CLASS_OF(other)) {
return Qfalse;
}
Data_Get_Struct(method, rb_vm_method_t, m1);
Data_Get_Struct(other, rb_vm_method_t, m2);
if (m1->oclass != m2->oclass
|| m1->rclass != m2->rclass
|| m1->recv != m2->recv) {
return Qfalse;
}
IMP m1_imp = m1->node == NULL
? class_getMethodImplementation((Class)m1->oclass, m1->sel)
: m1->node->objc_imp;
IMP m2_imp = m2->node == NULL
? class_getMethodImplementation((Class)m2->oclass, m2->sel)
: m2->node->objc_imp;
if (m1_imp != m2_imp) {
return Qfalse;
}
return Qtrue;
}
/*
* call-seq:
* meth.hash => integer
*
* Return a hash value corresponding to the method object.
*/
static VALUE
method_hash(VALUE method, SEL sel)
{
rb_vm_method_t *m;
long hash;
Data_Get_Struct(method, rb_vm_method_t, m);
hash = (long)m->oclass;
hash ^= (long)m->rclass;
hash ^= (long)m->recv;
hash ^= (long)m->node;
return INT2FIX(hash);
}
/*
* call-seq:
* meth.unbind => unbound_method
*
* Dissociates <i>meth</i> from it's 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, SEL sel)
{
VALUE method;
rb_vm_method_t *orig, *data;
Data_Get_Struct(obj, rb_vm_method_t, orig);
method =
Data_Make_Struct(rb_cUnboundMethod, rb_vm_method_t, NULL, NULL, data);
data->oclass = orig->oclass;
data->recv = Qundef;
data->node = orig->node;
data->rclass = orig->rclass;
data->sel = orig->sel;
data->cache = orig->cache;
data->arity = orig->arity;
return method;
}
/*
* call-seq:
* meth.receiver => object
*
* Returns the bound receiver of the method object.
*/
static VALUE
method_receiver(VALUE obj, SEL sel)
{
rb_vm_method_t *data;
Data_Get_Struct(obj, rb_vm_method_t, data);
return data->recv;
}
/*
* call-seq:
* meth.name => symbol
*
* Returns the name of the method.
*/
static VALUE
method_name(VALUE obj, SEL sel)
{
rb_vm_method_t *data;
Data_Get_Struct(obj, rb_vm_method_t, data);
ID mid = rb_intern(sel_getName(data->sel));
return ID2SYM(mid);
}
/*
* call-seq:
* meth.owner => class_or_module
*
* Returns the class or module that defines the method.
*/
static VALUE
method_owner(VALUE obj, SEL sel)
{
rb_vm_method_t *data;
Data_Get_Struct(obj, rb_vm_method_t, data);
return data->oclass;
}
/*
* 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"
*/
static VALUE
rb_obj_method(VALUE obj, SEL sel, VALUE vid)
{
return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod, Qfalse);
}
static VALUE
rb_obj_public_method(VALUE obj, SEL sel, VALUE vid)
{
return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod, Qtrue);
}
/*
* 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_byte {|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, SEL sel, VALUE vid)
{
return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod, Qfalse);
}
static VALUE
rb_mod_public_instance_method(VALUE mod, SEL sel, VALUE vid)
{
return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod, Qtrue);
}
/*
* 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+ or +Method+ 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(VALUE mod, SEL sel, int argc, VALUE *argv)
{
#if MACRUBY_STATIC
not_implemented_in_static(sel);
#else
ID id;
VALUE body;
if (argc == 1) {
id = rb_to_id(argv[0]);
body = rb_block_lambda();
}
else if (argc == 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));
}
}
else {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
}
if (rb_obj_is_method(body)) {
rb_vm_method_t *data;
Data_Get_Struct(body, rb_vm_method_t, data);
if (data->node == NULL) {
rb_raise(rb_eArgError, "cannot use Method object of pure Objective-C method");
}
VALUE klass = data->rclass;
if (RBASIC(mod)->klass != klass && !RTEST(rb_class_inherited_p(mod, klass))) {
rb_raise(rb_eTypeError,
"bind argument must be a subclass of %s",
rb_class2name(klass));
}
SEL msel = rb_vm_id_to_sel(id, data->arity);
rb_vm_define_method2((Class)mod, msel, data->node, data->node->flags, false);
}
else {
rb_vm_block_t *proc;
GetProcPtr(body, proc);
rb_vm_define_method3((Class)mod, id, proc);
}
return body;
#endif
}
static VALUE
rb_obj_define_method(VALUE obj, SEL sel, int argc, VALUE *argv)
{
VALUE klass = rb_singleton_class(obj);
return rb_mod_define_method(klass, 0, argc, argv);
}
/*
* MISSING: documentation
*/
static VALUE
method_clone(VALUE self, SEL sel)
{
VALUE clone;
rb_vm_method_t *orig, *data;
Data_Get_Struct(self, rb_vm_method_t, orig);
clone =
Data_Make_Struct(CLASS_OF(self), rb_vm_method_t, NULL, NULL, data);
CLONESETUP(clone, self);
*data = *orig;
GC_WB(&data->recv, orig->recv);
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(VALUE method, SEL sel, int argc, VALUE *argv)
{
rb_vm_method_t *data;
Data_Get_Struct(method, rb_vm_method_t, data);
if (data->recv == Qundef) {
rb_raise(rb_eTypeError, "can't call unbound method; bind first");
}
int safe = -1;
if (OBJ_TAINTED(method)) {
safe = rb_safe_level();
if (rb_safe_level() < 4) {
rb_set_safe_level_force(4);
}
}
VALUE result = rb_vm_method_call(data, rb_vm_current_block(), argc, argv);
if (safe >= 0) {
rb_set_safe_level_force(safe);
}
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, SEL sel, VALUE recv)
{
rb_vm_method_t *data, *bound;
Data_Get_Struct(method, rb_vm_method_t, data);
if (data->rclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, data->rclass)) {
if (RCLASS_SINGLETON(data->rclass)) {
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 = Data_Make_Struct(rb_cMethod, rb_vm_method_t, NULL, NULL, bound);
*bound = *data;
GC_WB(&bound->recv, recv);
bound->rclass = CLASS_OF(recv);
return method;
}
int
rb_node_arity(NODE* body)
{
// TODO should be replaced by the roxor.cpp's stuff
switch (nd_type(body)) {
case NODE_CFUNC:
if (body->nd_argc < 0) {
return -1;
}
return body->nd_argc;
case NODE_ZSUPER:
return -1;
case NODE_ATTRSET:
return 1;
case NODE_IVAR:
return 0;
case NODE_BMETHOD:
return rb_proc_arity(body->nd_cval);
default:
rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body));
}
}
/*
* 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, SEL sel)
{
int n = method_arity(method);
return INT2FIX(n);
}
/*
* call-seq:
* meth.to_s => string
* meth.inspect => string
*
* Show the name of the underlying method.
*
* "cat".method(:count).inspect #=> "#<Method: String#count>"
*/
static VALUE
method_inspect(VALUE method, SEL sel)
{
rb_vm_method_t *data;
VALUE str;
const char *s;
const char *sharp = "#";
Data_Get_Struct(method, rb_vm_method_t, data);
str = rb_str_buf_new2("#<");
s = rb_obj_classname(method);
rb_str_buf_cat2(str, s);
rb_str_buf_cat2(str, ": ");
rb_str_buf_cat2(str, rb_class2name(data->rclass));
if (data->rclass != data->oclass) {
rb_str_buf_cat2(str, "(");
rb_str_buf_cat2(str, rb_class2name(data->oclass));
rb_str_buf_cat2(str, ")");
}
rb_str_buf_cat2(str, sharp);
rb_str_buf_cat2(str, sel_getName(data->sel));
rb_str_buf_cat2(str, ">");
return str;
}
static VALUE
mproc(VALUE method)
{
return rb_funcall(Qnil, rb_intern("proc"), 0);
}
#if 0
static VALUE
mlambda(VALUE method)
{
return rb_funcall(Qnil, rb_intern("lambda"), 0);
}
#endif
#if 0
static VALUE
bmcall(VALUE args, VALUE method)
{
volatile VALUE a;
#if WITH_OBJC
if (TYPE(args) != T_ARRAY) {
return rb_method_call(1, &args, method);
}
#else
if (CLASS_OF(args) != rb_cArray) {
args = rb_ary_new3(1, args);
}
#endif
a = args;
return rb_method_call(RARRAY_LEN(a), (VALUE *)RARRAY_PTR(a), method);
}
#endif
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, SEL sel)
{
rb_vm_method_t *data;
Data_Get_Struct(method, rb_vm_method_t, data);
rb_vm_block_t *block = rb_vm_create_block_from_method(data);
return rb_proc_alloc_with_block(rb_cProc, block);
}
/*
* call_seq:
* local_jump_error.exit_value => obj
*
* Returns the exit value associated with this +LocalJumpError+.
*/
static VALUE
localjump_xvalue(VALUE exc, SEL sel)
{
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, SEL sel)
{
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, SEL sel)
{
rb_vm_block_t *block;
GetProcPtr(self, block);
rb_vm_binding_t *binding = (rb_vm_binding_t *)xmalloc(
sizeof(rb_vm_binding_t));
binding->block = NULL;
GC_WB(&binding->self, block->self);
GC_WB(&binding->locals, block->locals);
binding->outer_stack = NULL;
return Data_Wrap_Struct(rb_cBinding, NULL, NULL, binding);
}
/*
* 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 or 3)
* p b.curry(5) #=> wrong number of arguments (5 or 3)
* p b.curry(1) #=> wrong number of arguments (1 or 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 or 3)
*
* b = proc { :foo }
* p b.curry[] #=> :foo
*/
static VALUE
proc_curry(VALUE self, SEL sel, int argc, VALUE *argv)
{
int sarity, marity = FIX2INT(proc_arity(self, 0));
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 (proc_lambda_p(self, 0) && (sarity < marity || (sarity > marity && !opt))) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", sarity, marity);
}
}
return rb_vm_make_curry_proc(self, rb_ary_new(), arity);
}
/*
* <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
*
*/
extern VALUE sysstack_error; // defined in eval.c for WTF reason
void
Init_Proc(void)
{
/* Proc */
rb_cProc = rb_define_class("Proc", rb_cObject);
rb_undef_alloc_func(rb_cProc);
rb_objc_define_method(*(VALUE *)rb_cProc, "new", rb_proc_s_new, -1);
rb_objc_define_method(rb_cProc, "call", proc_call, -1);
rb_objc_define_method(rb_cProc, "[]", proc_call, -1);
rb_objc_define_method(rb_cProc, "===", proc_call, -1);
rb_objc_define_method(rb_cProc, "yield", proc_call, -1);
rb_objc_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
rb_objc_define_method(rb_cProc, "arity", proc_arity, 0);
rb_objc_define_method(rb_cProc, "clone", proc_clone, 0);
rb_objc_define_method(rb_cProc, "dup", proc_dup, 0);
rb_objc_define_method(rb_cProc, "==", proc_eq, 1);
rb_objc_define_method(rb_cProc, "eql?", proc_eq, 1);
rb_objc_define_method(rb_cProc, "hash", proc_hash, 0);
rb_objc_define_method(rb_cProc, "to_s", proc_to_s, 0);
rb_objc_define_method(rb_cProc, "lambda?", proc_lambda_p, 0);
rb_objc_define_method(rb_cProc, "binding", proc_binding, 0);
rb_objc_define_method(rb_cProc, "curry", proc_curry, -1);
/* Exceptions */
rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
rb_objc_define_method(rb_eLocalJumpError, "exit_value", localjump_xvalue, 0);
rb_objc_define_method(rb_eLocalJumpError, "reason", localjump_reason, 0);
rb_eSysStackError = rb_define_class("SystemStackError", rb_eException);
sysstack_error = rb_exc_new2(rb_eSysStackError, "stack level too deep");
OBJ_TAINT(sysstack_error);
GC_RETAIN(sysstack_error);
/* utility functions */
rb_objc_define_module_function(rb_mKernel, "proc", rb_block_proc_imp, 0);
rb_objc_define_module_function(rb_mKernel, "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_objc_define_method(rb_cMethod, "==", method_eq, 1);
rb_objc_define_method(rb_cMethod, "eql?", method_eq, 1);
rb_objc_define_method(rb_cMethod, "hash", method_hash, 0);
rb_objc_define_method(rb_cMethod, "clone", method_clone, 0);
rb_objc_define_method(rb_cMethod, "call", rb_method_call, -1);
rb_objc_define_method(rb_cMethod, "[]", rb_method_call, -1);
rb_objc_define_method(rb_cMethod, "arity", method_arity_m, 0);
rb_objc_define_method(rb_cMethod, "inspect", method_inspect, 0);
rb_objc_define_method(rb_cMethod, "to_s", method_inspect, 0);
rb_objc_define_method(rb_cMethod, "to_proc", method_proc, 0);
rb_objc_define_method(rb_cMethod, "receiver", method_receiver, 0);
rb_objc_define_method(rb_cMethod, "name", method_name, 0);
rb_objc_define_method(rb_cMethod, "owner", method_owner, 0);
rb_objc_define_method(rb_cMethod, "unbind", method_unbind, 0);
rb_objc_define_method(rb_mKernel, "method", rb_obj_method, 1);
rb_objc_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_objc_define_method(rb_cUnboundMethod, "==", method_eq, 1);
rb_objc_define_method(rb_cUnboundMethod, "eql?", method_eq, 1);
rb_objc_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
rb_objc_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
rb_objc_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
rb_objc_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
rb_objc_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
rb_objc_define_method(rb_cUnboundMethod, "name", method_name, 0);
rb_objc_define_method(rb_cUnboundMethod, "owner", method_owner, 0);
rb_objc_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
/* Module#*_method */
rb_objc_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1);
rb_objc_define_method(rb_cModule, "public_instance_method", rb_mod_public_instance_method, 1);
rb_objc_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1);
/* Kernel */
rb_objc_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 getBinding
* return binding()
* end
* end
*
* k1 = Demo.new(99)
* b1 = k1.getBinding
* k2 = Demo.new(-3)
* b2 = k2.getBinding
*
* 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_objc_define_method(rb_cBinding, "clone", binding_clone, 0);
rb_objc_define_method(rb_cBinding, "dup", binding_dup, 0);
rb_objc_define_method(rb_cBinding, "eval", bind_eval, -1);
rb_objc_define_module_function(rb_mKernel, "binding", rb_f_binding, 0);
rb_vm_binding_t *binding = (rb_vm_binding_t *)xmalloc(
sizeof(rb_vm_binding_t));
GC_WB(&binding->self, rb_vm_top_self());
binding->outer_stack = NULL;
rb_define_global_const("TOPLEVEL_BINDING",
rb_binding_new_from_binding(binding));
}
Jump to Line
Something went wrong with that request. Please try again.