mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Add mrbgems/mruby-method
from https://github.com/ksss/mruby-method/commit/0837a0b507a5b4cdf8a1f1039ee371cee4e3b7fb
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
install:
|
||||
- sudo apt-get -qq install rake bison git gperf
|
||||
before_script:
|
||||
- cd ../
|
||||
- git clone https://github.com/mruby/mruby.git
|
||||
- cd mruby
|
||||
- cp -fp ../mruby-method/.travis_build_config.rb build_config.rb
|
||||
script:
|
||||
- ruby minirake all test
|
||||
notifications:
|
||||
email: false
|
||||
@@ -0,0 +1,6 @@
|
||||
MRuby::Build.new do |conf|
|
||||
toolchain :gcc
|
||||
enable_debug
|
||||
conf.gembox 'default'
|
||||
conf.gem '../mruby-method'
|
||||
end
|
||||
@@ -0,0 +1,77 @@
|
||||
mruby-method
|
||||
===
|
||||
|
||||
[](https://travis-ci.org/ksss/mruby-method)
|
||||
|
||||
A implementetion of class **Method** and **UnboundMethod** for mruby
|
||||
|
||||
```ruby
|
||||
p Enumerable.instance_method(:find_all).source_location
|
||||
#=> ["mruby/mruby/mrblib/enum.rb", 148]
|
||||
```
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
|
||||
in build_config.rb
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
enable_debug
|
||||
|
||||
conf.gem :mgem => "mruby-method"
|
||||
# or
|
||||
conf.gem :github => "ksss/mruby-method"
|
||||
end
|
||||
```
|
||||
|
||||
# Note
|
||||
|
||||
`source_location` method need this configuration in build_config.rb
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
enable_debug
|
||||
end
|
||||
```
|
||||
|
||||
# Supported Methods
|
||||
|
||||
## Kernel
|
||||
|
||||
- `Kernel#method`
|
||||
- `Kernel#singleton_method`
|
||||
|
||||
## Module
|
||||
|
||||
- `Module#instance_method`
|
||||
|
||||
## Method class
|
||||
|
||||
- `Method#name`
|
||||
- `Method#call`
|
||||
- `Method#super_method`
|
||||
- `Method#arity`
|
||||
- `Method#unbind`
|
||||
- `Method#[]`
|
||||
- `Method#owner`
|
||||
- `Method#receiver`
|
||||
- `Method#parameters`
|
||||
- `Method#source_location`
|
||||
- `Method#to_proc`
|
||||
|
||||
## UnboundMethod class
|
||||
|
||||
- `UnboundMethod#name`
|
||||
- `UnboundMethod#bind`
|
||||
- `UnboundMethod#super_method`
|
||||
- `UnboundMethod#arity`
|
||||
- `UnboundMethod#owner`
|
||||
- `UnboundMethod#parameters`
|
||||
- `UnboundMethod#source_location`
|
||||
|
||||
# See also
|
||||
|
||||
- https://ruby-doc.org/core-2.3.3/Method.html
|
||||
- https://ruby-doc.org/core-2.3.3/UnboundMethod.html
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,7 @@
|
||||
MRuby::Gem::Specification.new('mruby-method') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.author = 'mruby developers'
|
||||
spec.summary = 'Method and UnboundMethod class'
|
||||
|
||||
spec.add_dependency('mruby-proc-ext', :core => 'mruby-proc-ext')
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Kernel
|
||||
def singleton_method(name)
|
||||
m = method(name)
|
||||
if m.owner != singleton_class
|
||||
raise NameError, "undefined method `#{name}' for class `#{singleton_class}'"
|
||||
end
|
||||
m
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
class Method
|
||||
def to_proc
|
||||
m = self
|
||||
lambda { |*args, &b|
|
||||
m.call(*args, &b)
|
||||
}
|
||||
end
|
||||
|
||||
def owner
|
||||
@owner
|
||||
end
|
||||
|
||||
def receiver
|
||||
@recv
|
||||
end
|
||||
|
||||
def name
|
||||
@name
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class UnboundMethod
|
||||
def owner
|
||||
@owner
|
||||
end
|
||||
|
||||
def name
|
||||
@name
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,423 @@
|
||||
#include "mruby.h"
|
||||
#include "mruby/array.h"
|
||||
#include "mruby/data.h"
|
||||
#include "mruby/class.h"
|
||||
#include "mruby/variable.h"
|
||||
#include "mruby/proc.h"
|
||||
#include "mruby/string.h"
|
||||
#include <alloca.h>
|
||||
|
||||
static struct RObject *
|
||||
method_object_alloc(mrb_state *mrb, struct RClass *mclass)
|
||||
{
|
||||
return (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mclass);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
unbound_method_bind(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RObject *me;
|
||||
mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
|
||||
mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
|
||||
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
|
||||
mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
|
||||
mrb_value recv;
|
||||
|
||||
mrb_get_args(mrb, "o", &recv);
|
||||
|
||||
if (mrb_type(owner) != MRB_TT_MODULE &&
|
||||
mrb_class_ptr(owner) != mrb_obj_class(mrb, recv) &&
|
||||
!mrb_obj_is_kind_of(mrb, recv, mrb_class_ptr(owner))) {
|
||||
if (mrb_type(owner) == MRB_TT_SCLASS) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object");
|
||||
} else {
|
||||
const char *s = mrb_class_name(mrb, mrb_class_ptr(owner));
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %S", mrb_str_new_static(mrb, s, strlen(s)));
|
||||
}
|
||||
}
|
||||
me = method_object_alloc(mrb, mrb_class_get(mrb, "Method"));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), owner);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), recv);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), name);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), proc);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), klass);
|
||||
|
||||
return mrb_obj_value(me);
|
||||
}
|
||||
|
||||
#define IV_GET(value, name) mrb_iv_get(mrb, value, mrb_intern_lit(mrb, name))
|
||||
static mrb_value
|
||||
method_eql(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value other, receiver, orig_proc, other_proc;
|
||||
struct RClass *owner, *klass;
|
||||
struct RProc *orig_rproc, *other_rproc;
|
||||
|
||||
mrb_get_args(mrb, "o", &other);
|
||||
if (!mrb_obj_is_instance_of(mrb, other, mrb_class(mrb, self)))
|
||||
return mrb_false_value();
|
||||
|
||||
if (mrb_class(mrb, self) != mrb_class(mrb, other))
|
||||
return mrb_false_value();
|
||||
|
||||
klass = mrb_class_ptr(IV_GET(self, "@klass"));
|
||||
if (klass != mrb_class_ptr(IV_GET(other, "@klass")))
|
||||
return mrb_false_value();
|
||||
|
||||
owner = mrb_class_ptr(IV_GET(self, "@owner"));
|
||||
if (owner != mrb_class_ptr(IV_GET(other, "@owner")))
|
||||
return mrb_false_value();
|
||||
|
||||
receiver = IV_GET(self, "@recv");
|
||||
if (!mrb_obj_equal(mrb, receiver, IV_GET(other, "@recv")))
|
||||
return mrb_false_value();
|
||||
|
||||
orig_proc = IV_GET(self, "proc");
|
||||
other_proc = IV_GET(other, "proc");
|
||||
if (mrb_nil_p(orig_proc) && mrb_nil_p(other_proc)) {
|
||||
if (mrb_symbol(IV_GET(self, "@name")) == mrb_symbol(IV_GET(other, "@name")))
|
||||
return mrb_true_value();
|
||||
else
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
if (mrb_nil_p(orig_proc))
|
||||
return mrb_false_value();
|
||||
if (mrb_nil_p(other_proc))
|
||||
return mrb_false_value();
|
||||
|
||||
orig_rproc = mrb_proc_ptr(orig_proc);
|
||||
other_rproc = mrb_proc_ptr(other_proc);
|
||||
if (MRB_PROC_CFUNC_P(orig_rproc)) {
|
||||
if (!MRB_PROC_CFUNC_P(other_rproc))
|
||||
return mrb_false_value();
|
||||
if (orig_rproc->body.func != other_rproc->body.func)
|
||||
return mrb_false_value();
|
||||
}
|
||||
else {
|
||||
if (MRB_PROC_CFUNC_P(other_rproc))
|
||||
return mrb_false_value();
|
||||
if (orig_rproc->body.irep != other_rproc->body.irep)
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
return mrb_true_value();
|
||||
}
|
||||
|
||||
#undef IV_GET
|
||||
|
||||
static mrb_value
|
||||
method_call(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
|
||||
mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
|
||||
mrb_value recv = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@recv"));
|
||||
struct RClass *owner = mrb_class_ptr(mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner")));
|
||||
mrb_int argc, i;
|
||||
mrb_value *argv, ret, block;
|
||||
mrb_sym orig_mid;
|
||||
|
||||
mrb_get_args(mrb, "*&", &argv, &argc, &block);
|
||||
orig_mid = mrb->c->ci->mid;
|
||||
mrb->c->ci->mid = mrb_symbol(name);
|
||||
if (mrb_nil_p(proc)) {
|
||||
mrb_value *missing_argv = (mrb_value*)alloca(sizeof(mrb_value) * (argc + 1));
|
||||
missing_argv[0] = name;
|
||||
for(i = 0; i < argc; i++) {
|
||||
missing_argv[i + 1] = argv[i];
|
||||
}
|
||||
ret = mrb_funcall_argv(mrb, recv, mrb_intern_lit(mrb, "method_missing"), argc + 1, missing_argv);
|
||||
}
|
||||
else if (!mrb_nil_p(block)) {
|
||||
/*
|
||||
workaround since `mrb_yield_with_class` does not support passing block as parameter
|
||||
need new API that initializes `mrb->c->stack[argc+1]` with block passed by argument
|
||||
*/
|
||||
ret = mrb_funcall_with_block(mrb, recv, mrb_symbol(name), argc, argv, block);
|
||||
}
|
||||
else {
|
||||
ret = mrb_yield_with_class(mrb, proc, argc, argv, recv, owner);
|
||||
}
|
||||
mrb->c->ci->mid = orig_mid;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
method_unbind(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RObject *ume;
|
||||
mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
|
||||
mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
|
||||
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
|
||||
mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
|
||||
|
||||
ume = method_object_alloc(mrb, mrb_class_get(mrb, "UnboundMethod"));
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@owner"), owner);
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@recv"), mrb_nil_value());
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@name"), name);
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "proc"), proc);
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@klass"), klass);
|
||||
|
||||
return mrb_obj_value(ume);
|
||||
}
|
||||
|
||||
static struct RProc *
|
||||
method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
|
||||
{
|
||||
mrb_method_t m = mrb_method_search_vm(mrb, cp, mid);
|
||||
if (MRB_METHOD_UNDEF_P(m))
|
||||
return NULL;
|
||||
if (MRB_METHOD_PROC_P(m))
|
||||
return MRB_METHOD_PROC(m);
|
||||
return mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
method_super_method(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value recv = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@recv"));
|
||||
mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
|
||||
mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
|
||||
mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
|
||||
struct RClass *super, *rklass;
|
||||
struct RProc *proc;
|
||||
struct RObject *me;
|
||||
|
||||
switch (mrb_type(klass)) {
|
||||
case MRB_TT_SCLASS:
|
||||
super = mrb_class_ptr(klass)->super->super;
|
||||
break;
|
||||
case MRB_TT_ICLASS:
|
||||
super = mrb_class_ptr(klass)->super;
|
||||
break;
|
||||
default:
|
||||
super = mrb_class_ptr(owner)->super;
|
||||
break;
|
||||
}
|
||||
|
||||
proc = method_search_vm(mrb, &super, mrb_symbol(name));
|
||||
if (!proc)
|
||||
return mrb_nil_value();
|
||||
|
||||
rklass = super;
|
||||
while (super->tt == MRB_TT_ICLASS)
|
||||
super = super->c;
|
||||
|
||||
me = method_object_alloc(mrb, mrb_obj_class(mrb, self));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(super));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), recv);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), name);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), mrb_obj_value(proc));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), mrb_obj_value(rklass));
|
||||
|
||||
return mrb_obj_value(me);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
method_arity(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
|
||||
struct RProc *rproc;
|
||||
struct RClass *orig;
|
||||
mrb_value ret;
|
||||
|
||||
if (mrb_nil_p(proc))
|
||||
return mrb_fixnum_value(-1);
|
||||
|
||||
rproc = mrb_proc_ptr(proc);
|
||||
orig = rproc->c;
|
||||
rproc->c = mrb->proc_class;
|
||||
ret = mrb_funcall(mrb, proc, "arity", 0);
|
||||
rproc->c = orig;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
method_source_location(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
|
||||
struct RProc *rproc;
|
||||
struct RClass *orig;
|
||||
mrb_value ret;
|
||||
|
||||
if (mrb_nil_p(proc))
|
||||
return mrb_nil_value();
|
||||
|
||||
rproc = mrb_proc_ptr(proc);
|
||||
orig = rproc->c;
|
||||
rproc->c = mrb->proc_class;
|
||||
ret = mrb_funcall(mrb, proc, "source_location", 0);
|
||||
rproc->c = orig;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
method_parameters(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
|
||||
struct RProc *rproc;
|
||||
struct RClass *orig;
|
||||
mrb_value ret;
|
||||
|
||||
if (mrb_nil_p(proc)) {
|
||||
mrb_value rest = mrb_symbol_value(mrb_intern_lit(mrb, "rest"));
|
||||
mrb_value arest = mrb_ary_new_from_values(mrb, 1, &rest);
|
||||
return mrb_ary_new_from_values(mrb, 1, &arest);
|
||||
}
|
||||
|
||||
rproc = mrb_proc_ptr(proc);
|
||||
orig = rproc->c;
|
||||
rproc->c = mrb->proc_class;
|
||||
ret = mrb_funcall(mrb, proc, "parameters", 0);
|
||||
rproc->c = orig;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
method_to_s(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
|
||||
mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
|
||||
mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
|
||||
mrb_value str = mrb_str_new_lit(mrb, "#<");
|
||||
struct RClass *rklass;
|
||||
|
||||
mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, self));
|
||||
mrb_str_cat_lit(mrb, str, ": ");
|
||||
rklass = mrb_class_ptr(klass);
|
||||
if (mrb_class_ptr(owner) == rklass) {
|
||||
mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
|
||||
mrb_str_cat_lit(mrb, str, "#");
|
||||
mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
|
||||
}
|
||||
else {
|
||||
mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass));
|
||||
mrb_str_cat_lit(mrb, str, "(");
|
||||
mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
|
||||
mrb_str_cat_lit(mrb, str, ")#");
|
||||
mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
|
||||
}
|
||||
mrb_str_cat_lit(mrb, str, ">");
|
||||
return str;
|
||||
}
|
||||
|
||||
static void
|
||||
mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, struct RClass **owner, struct RProc **proc, mrb_bool unbound)
|
||||
{
|
||||
mrb_value ret;
|
||||
const char *s;
|
||||
|
||||
*owner = c;
|
||||
*proc = method_search_vm(mrb, owner, name);
|
||||
if (!*proc) {
|
||||
if (unbound) {
|
||||
goto name_error;
|
||||
}
|
||||
if (!mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "respond_to_missing?"))) {
|
||||
goto name_error;
|
||||
}
|
||||
ret = mrb_funcall(mrb, obj, "respond_to_missing?", 2, mrb_symbol_value(name), mrb_true_value());
|
||||
if (!mrb_test(ret)) {
|
||||
goto name_error;
|
||||
}
|
||||
*owner = c;
|
||||
}
|
||||
|
||||
while ((*owner)->tt == MRB_TT_ICLASS)
|
||||
*owner = (*owner)->c;
|
||||
|
||||
return;
|
||||
|
||||
name_error:
|
||||
s = mrb_class_name(mrb, c);
|
||||
mrb_raisef(
|
||||
mrb, E_NAME_ERROR,
|
||||
"undefined method `%S' for class `%S'",
|
||||
mrb_sym2str(mrb, name),
|
||||
mrb_str_new_static(mrb, s, strlen(s))
|
||||
);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_kernel_method(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RClass *owner;
|
||||
struct RProc *proc;
|
||||
struct RObject *me;
|
||||
mrb_sym name;
|
||||
|
||||
mrb_get_args(mrb, "n", &name);
|
||||
|
||||
mrb_search_method_owner(mrb, mrb_class(mrb, self), self, name, &owner, &proc, FALSE);
|
||||
|
||||
me = method_object_alloc(mrb, mrb_class_get(mrb, "Method"));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(owner));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), self);
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), mrb_symbol_value(name));
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), proc ? mrb_obj_value(proc) : mrb_nil_value());
|
||||
mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), mrb_obj_value(mrb_class(mrb, self)));
|
||||
|
||||
return mrb_obj_value(me);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_module_instance_method(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RClass *owner;
|
||||
struct RProc *proc;
|
||||
struct RObject *ume;
|
||||
mrb_sym name;
|
||||
|
||||
mrb_get_args(mrb, "n", &name);
|
||||
|
||||
mrb_search_method_owner(mrb, mrb_class_ptr(self), self, name, &owner, &proc, TRUE);
|
||||
|
||||
ume = method_object_alloc(mrb, mrb_class_get(mrb, "UnboundMethod"));
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(owner));
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@recv"), mrb_nil_value());
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@name"), mrb_symbol_value(name));
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "proc"), proc ? mrb_obj_value(proc) : mrb_nil_value());
|
||||
mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@klass"), self);
|
||||
|
||||
return mrb_obj_value(ume);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_method_gem_init(mrb_state* mrb)
|
||||
{
|
||||
struct RClass *unbound_method = mrb_define_class(mrb, "UnboundMethod", mrb->object_class);
|
||||
struct RClass *method = mrb_define_class(mrb, "Method", mrb->object_class);
|
||||
|
||||
mrb_undef_class_method(mrb, unbound_method, "new");
|
||||
mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1));
|
||||
mrb_alias_method(mrb, unbound_method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
|
||||
mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, unbound_method, "source_location", method_source_location, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, unbound_method, "parameters", method_parameters, MRB_ARGS_NONE());
|
||||
|
||||
mrb_undef_class_method(mrb, method, "new");
|
||||
mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1));
|
||||
mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
|
||||
mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY());
|
||||
mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "[]"), mrb_intern_lit(mrb, "call"));
|
||||
mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, method, "source_location", method_source_location, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, method, "parameters", method_parameters, MRB_ARGS_NONE());
|
||||
|
||||
mrb_define_method(mrb, mrb->kernel_module, "method", mrb_kernel_method, MRB_ARGS_REQ(1));
|
||||
|
||||
mrb_define_method(mrb, mrb->module_class, "instance_method", mrb_module_instance_method, MRB_ARGS_REQ(1));
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_method_gem_final(mrb_state* mrb)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
class Base
|
||||
def foo() :base end
|
||||
end
|
||||
|
||||
class Derived < Base
|
||||
def foo() :derived end
|
||||
end
|
||||
|
||||
class Interpreter
|
||||
attr_accessor :ret
|
||||
|
||||
def do_a() @ret += "there, "; end
|
||||
def do_d() @ret += "Hello "; end
|
||||
def do_e() @ret += "!\n"; end
|
||||
def do_v() @ret += "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)
|
||||
@ret = ""
|
||||
string.each_char {|b| Dispatcher[b].bind(self).call }
|
||||
end
|
||||
end
|
||||
|
||||
assert 'demo' do
|
||||
interpreter = Interpreter.new
|
||||
interpreter.interpret('dave')
|
||||
assert_equal "Hello there, Dave!\n", interpreter.ret
|
||||
end
|
||||
|
||||
assert 'Method#arity' do
|
||||
Class.new {
|
||||
attr_accessor :done
|
||||
def initialize; @done = false; end
|
||||
def m0() end
|
||||
def m1(a) end
|
||||
def m2(a, b) end
|
||||
def mo1(a = nil, &b) end
|
||||
def mo2(a, b = nil) end
|
||||
def mo3(*a) end
|
||||
def mo4(a, *b, &c) end
|
||||
def mo5(a, *b, c) end
|
||||
def mo6(a, *b, c, &d) end
|
||||
def mo7(a, b = nil, *c, d, &e) end
|
||||
def ma1((a), &b) nil && a end
|
||||
|
||||
def run
|
||||
assert_equal(0, method(:m0).arity)
|
||||
assert_equal(1, method(:m1).arity)
|
||||
assert_equal(2, method(:m2).arity)
|
||||
assert_equal(-1, method(:mo1).arity)
|
||||
assert_equal(-2, method(:mo2).arity)
|
||||
assert_equal(-1, method(:mo3).arity)
|
||||
assert_equal(-2, method(:mo4).arity)
|
||||
assert_equal(-3, method(:mo5).arity)
|
||||
assert_equal(-3, method(:mo6).arity)
|
||||
assert_equal(-3, method(:mo7).arity)
|
||||
assert_equal(1, method(:ma1).arity)
|
||||
|
||||
assert_equal(-1, method(:__send__).arity)
|
||||
assert_equal(-1, method(:nothing).arity)
|
||||
end
|
||||
|
||||
def respond_to_missing?(m, b)
|
||||
m == :nothing
|
||||
end
|
||||
}.new.run
|
||||
end
|
||||
|
||||
assert 'Method and UnboundMethod should not be have a `new` method' do
|
||||
assert_raise(NoMethodError){ Method.new }
|
||||
assert_raise(NoMethodError){ UnboundMethod.new }
|
||||
end
|
||||
|
||||
assert 'instance' do
|
||||
assert_kind_of Method, 1.method(:+)
|
||||
assert_kind_of UnboundMethod, Fixnum.instance_method(:+)
|
||||
end
|
||||
|
||||
assert 'Method#call' do
|
||||
assert_equal 3, 1.method(:+).call(2)
|
||||
assert_equal "ab", "a".method(:+)["b"]
|
||||
klass = Class.new {
|
||||
def foo; 42; end
|
||||
}
|
||||
klass2 = Class.new(klass) {
|
||||
def foo; super; end
|
||||
}
|
||||
assert_equal 42, klass2.new.method(:foo).call
|
||||
|
||||
i = Class.new {
|
||||
def bar
|
||||
yield 3
|
||||
end
|
||||
}.new
|
||||
assert_raise(LocalJumpError) { i.method(:bar).call }
|
||||
assert_equal 3, i.method(:bar).call { |i| i }
|
||||
end
|
||||
|
||||
assert 'Method#call for regression' do
|
||||
obj = BasicObject.new
|
||||
def obj.foo
|
||||
:ok
|
||||
end
|
||||
assert_equal :ok, Kernel.instance_method(:send).bind(obj).call(:foo), "https://github.com/ksss/mruby-method/issues/4"
|
||||
end
|
||||
|
||||
assert 'Method#call with undefined method' do
|
||||
c = Class.new {
|
||||
attr_accessor :m, :argv
|
||||
def respond_to_missing?(m, b)
|
||||
m == :foo
|
||||
end
|
||||
|
||||
def method_missing(m, *argv)
|
||||
@m = m
|
||||
@argv = argv
|
||||
super
|
||||
end
|
||||
}
|
||||
cc = c.new
|
||||
assert_raise(NameError) { cc.method(:nothing) }
|
||||
assert_kind_of Method, cc.method(:foo)
|
||||
assert_raise(NoMethodError) { cc.method(:foo).call(:arg1, :arg2) }
|
||||
assert_equal :foo, cc.m
|
||||
assert_equal [:arg1, :arg2], cc.argv
|
||||
|
||||
cc = c.new
|
||||
m = cc.method(:foo)
|
||||
c.class_eval do
|
||||
def foo
|
||||
:ng
|
||||
end
|
||||
end
|
||||
assert_raise(NoMethodError) { m.call(:arg1, :arg2) }
|
||||
end
|
||||
|
||||
assert 'Method#source_location' do
|
||||
skip if proc{}.source_location.nil?
|
||||
|
||||
filename = __FILE__
|
||||
klass = Class.new
|
||||
|
||||
lineno = __LINE__ + 1
|
||||
klass.define_method(:find_me_if_you_can) {}
|
||||
assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location
|
||||
|
||||
lineno = __LINE__ + 1
|
||||
klass.define_singleton_method(:s_find_me_if_you_can) {}
|
||||
assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location
|
||||
|
||||
klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end }
|
||||
assert_nil klass.new.method(:nothing).source_location
|
||||
end
|
||||
|
||||
assert 'UnboundMethod#source_location' do
|
||||
skip if proc{}.source_location.nil?
|
||||
|
||||
filename = __FILE__
|
||||
klass = Class.new {
|
||||
def respond_to_missing?(m, b)
|
||||
m == :nothing
|
||||
end
|
||||
}
|
||||
|
||||
lineno = __LINE__ + 1
|
||||
klass.define_method(:find_me_if_you_can) {}
|
||||
assert_equal [filename, lineno], klass.instance_method(:find_me_if_you_can).source_location
|
||||
assert_nil klass.new.method(:nothing).unbind.source_location
|
||||
end
|
||||
|
||||
assert 'Method#parameters' do
|
||||
klass = Class.new {
|
||||
def foo(a, b=nil, *c) end
|
||||
def respond_to_missing?(m, b)
|
||||
m == :missing
|
||||
end
|
||||
}
|
||||
assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.new.method(:foo).parameters
|
||||
assert_equal [[:rest]], klass.new.method(:missing).parameters
|
||||
end
|
||||
|
||||
assert 'UnboundMethod#parameters' do
|
||||
klass = Class.new {
|
||||
def foo(a, b=nil, *c) end
|
||||
def respond_to_missing?(m, b)
|
||||
m == :nothing
|
||||
end
|
||||
}
|
||||
assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.instance_method(:foo).parameters
|
||||
assert_equal [[:rest]], klass.new.method(:nothing).unbind.parameters
|
||||
end
|
||||
|
||||
assert 'Method#to_proc' do
|
||||
m = 3.method(:+)
|
||||
assert_kind_of Proc, m.to_proc
|
||||
assert_equal 7, m.call(4)
|
||||
|
||||
o = Object.new
|
||||
def o.foo(a, b=nil, *c)
|
||||
[a, b, c]
|
||||
end
|
||||
assert_equal [:bar, nil, []], o.method(:foo).to_proc.call(:bar)
|
||||
# We can fix this issue but leave until the problem
|
||||
# assert_equal o.method(:foo).arity, o.method(:foo).to_proc.arity
|
||||
|
||||
def o.bar
|
||||
yield 39
|
||||
end
|
||||
assert_equal 42, o.bar(&3.method(:+))
|
||||
end
|
||||
|
||||
assert 'to_s' do
|
||||
o = Object.new
|
||||
def o.foo; end
|
||||
m = o.method(:foo)
|
||||
assert_equal("#<UnboundMethod: #{ class << o; self; end.inspect }#foo>", m.unbind.inspect)
|
||||
|
||||
c = Class.new
|
||||
c.class_eval { def foo; end; }
|
||||
m = c.new.method(:foo)
|
||||
assert_equal("#<Method: #{ c.inspect }#foo>", m.inspect)
|
||||
m = c.instance_method(:foo)
|
||||
assert_equal("#<UnboundMethod: #{ c.inspect }#foo>", m.inspect)
|
||||
end
|
||||
|
||||
assert 'owner' do
|
||||
c = Class.new do
|
||||
def foo; end
|
||||
def self.bar; end
|
||||
end
|
||||
m = Module.new do
|
||||
def baz; end
|
||||
end
|
||||
c.include(m)
|
||||
c2 = Class.new(c)
|
||||
|
||||
assert_equal(c, c.instance_method(:foo).owner)
|
||||
assert_equal(c, c2.instance_method(:foo).owner)
|
||||
|
||||
assert_equal(c, c.new.method(:foo).owner)
|
||||
assert_equal(c, c2.new.method(:foo).owner)
|
||||
assert_equal(c.singleton_class, c2.method(:bar).owner)
|
||||
end
|
||||
|
||||
assert 'owner missing' do
|
||||
c = Class.new do
|
||||
def respond_to_missing?(name, bool)
|
||||
name == :foo
|
||||
end
|
||||
end
|
||||
c2 = Class.new(c)
|
||||
assert_equal(c, c.new.method(:foo).owner)
|
||||
assert_equal(c2, c2.new.method(:foo).owner)
|
||||
end
|
||||
|
||||
assert 'receiver name owner' do
|
||||
o = Object.new
|
||||
def o.foo; end
|
||||
m = o.method(:foo)
|
||||
assert_equal(o, m.receiver)
|
||||
assert_equal(:foo, m.name)
|
||||
assert_equal(class << o; self; end, m.owner)
|
||||
assert_equal(:foo, m.unbind.name)
|
||||
assert_equal(class << o; self; end, m.unbind.owner)
|
||||
end
|
||||
|
||||
assert 'Method#unbind' do
|
||||
assert_equal(:derived, Derived.new.foo)
|
||||
um = Derived.new.method(:foo).unbind
|
||||
assert_kind_of(UnboundMethod, um)
|
||||
Derived.class_eval do
|
||||
def foo() :changed end
|
||||
end
|
||||
assert_equal(:changed, Derived.new.foo)
|
||||
assert_equal(:changed, Derived.new.foo{})
|
||||
assert_equal(:derived, um.bind(Derived.new).call)
|
||||
assert_raise(TypeError) do
|
||||
um.bind(Base.new)
|
||||
end
|
||||
|
||||
# TODO:
|
||||
# Block passed method not handled correctly with workaround.
|
||||
# See comment near `mrb_funcall_with_block` for detail.
|
||||
# assert_equal(:derived, um.bind(Derived.new).call{})
|
||||
end
|
||||
|
||||
assert 'Kernel#method' do
|
||||
c1 = Class.new {
|
||||
def foo; :foo; end
|
||||
}
|
||||
o = c1.new
|
||||
assert_kind_of Method, o.method(:foo)
|
||||
assert_kind_of Method, o.method('foo')
|
||||
assert_raise(TypeError) { o.method(nil) }
|
||||
assert_raise(NameError) { o.method('bar') }
|
||||
assert_raise(NameError) { o.method(:bar) }
|
||||
end
|
||||
|
||||
assert "Module#instance_method" do
|
||||
assert_kind_of UnboundMethod, Object.instance_method(:object_id)
|
||||
assert_raise(NameError) { Object.instance_method(:nothing) }
|
||||
c = Class.new {
|
||||
def respond_to_missing?(m, b)
|
||||
false
|
||||
end
|
||||
}
|
||||
assert_raise(NameError) { c.instance_method(:nothing) }
|
||||
end
|
||||
|
||||
assert 'Kernel#singleton_method' do
|
||||
c1 = Class.new {
|
||||
def foo; :foo; end
|
||||
}
|
||||
o = c1.new
|
||||
def o.bar; :bar; end
|
||||
assert_kind_of Method, o.method(:foo)
|
||||
assert_raise(NameError) { o.singleton_method(:foo) }
|
||||
assert_kind_of Method, o.singleton_method(:bar)
|
||||
assert_raise(TypeError) { o.singleton_method(nil) }
|
||||
m = assert_nothing_raised(NameError) { break o.singleton_method(:bar) }
|
||||
assert_equal(:bar, m.call)
|
||||
end
|
||||
|
||||
assert 'Method#super_method' do
|
||||
o = Derived.new
|
||||
m = o.method(:foo).super_method
|
||||
assert_equal(Base, m.owner)
|
||||
assert_true(o.equal? m.receiver)
|
||||
assert_equal(:foo, m.name)
|
||||
assert_nil(m.super_method)
|
||||
|
||||
c = Class.new {
|
||||
def foo; end
|
||||
}
|
||||
o = c.new
|
||||
o.extend Module.new {
|
||||
def foo; end
|
||||
}
|
||||
assert_equal c, o.method(:foo).super_method.owner
|
||||
assert_equal :foo, o.method(:foo).super_method.name
|
||||
assert_equal o, o.method(:foo).super_method.receiver
|
||||
end
|
||||
|
||||
assert 'Method#==' do
|
||||
o = Object.new
|
||||
class << o
|
||||
def foo; end
|
||||
end
|
||||
assert_not_equal(o.method(:foo), nil)
|
||||
m = o.method(:foo)
|
||||
def m.foo; end
|
||||
# TODO: assert_not_equal(o.method(:foo), m)
|
||||
assert_equal(o.method(:foo), o.method(:foo))
|
||||
# TODO: assert_false(o.method(:foo).eql? m)
|
||||
assert_true(o.method(:foo).eql? o.method(:foo))
|
||||
|
||||
assert_false(0.method(:+) == 1.method(:+))
|
||||
assert_false(0.method(:+) == 0.method(:-))
|
||||
a = 0.method(:+)
|
||||
assert_true(a.method(:==) == a.method(:eql?))
|
||||
end
|
||||
|
||||
assert "Method#initialize_copy" do
|
||||
c = Class.new {
|
||||
def foo
|
||||
end
|
||||
}.new
|
||||
m1 = c.method(:foo)
|
||||
m2 = m1.clone
|
||||
assert_equal(m1, m2)
|
||||
end
|
||||
|
||||
assert 'UnboundMethod#arity' do
|
||||
c = Class.new {
|
||||
def foo(a, b)
|
||||
end
|
||||
|
||||
def respond_to_missing?(m, b)
|
||||
m == :nothing
|
||||
end
|
||||
}
|
||||
assert_equal 2, c.instance_method(:foo).arity
|
||||
assert_equal(-1, c.new.method(:nothing).unbind.arity)
|
||||
end
|
||||
|
||||
assert 'UnboundMethod#==' do
|
||||
assert_false(Fixnum.instance_method(:+) == Fixnum.instance_method(:-))
|
||||
assert_true(Fixnum.instance_method(:+) == Fixnum.instance_method(:+))
|
||||
assert_false(Fixnum.instance_method(:+) == Float.instance_method(:+))
|
||||
assert_true(UnboundMethod.instance_method(:==) == UnboundMethod.instance_method(:eql?))
|
||||
end
|
||||
|
||||
assert 'UnboundMethod#super_method' do
|
||||
m = Derived.instance_method(:foo)
|
||||
m = m.super_method
|
||||
assert_equal(Base.instance_method(:foo), m)
|
||||
assert_nil(m.super_method)
|
||||
|
||||
m = Object.instance_method(:object_id)
|
||||
assert_nil(m.super_method)
|
||||
end
|
||||
|
||||
assert 'UnboundMethod#bind' do
|
||||
m = Module.new{ def meth() :meth end }.instance_method(:meth)
|
||||
assert_raise(ArgumentError) { m.bind }
|
||||
assert_kind_of Method, m.bind(1)
|
||||
assert_kind_of Method, m.bind(:sym)
|
||||
assert_kind_of Method, m.bind(Object.new)
|
||||
assert_equal(:meth, m.bind(1).call)
|
||||
assert_equal(:meth, m.bind(:sym).call)
|
||||
assert_equal(:meth, m.bind(Object.new).call)
|
||||
sc = Class.new {
|
||||
class << self
|
||||
def foo
|
||||
end
|
||||
end
|
||||
}.singleton_class
|
||||
assert_raise(TypeError) { sc.instance_method(:foo).bind([]) }
|
||||
assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
|
||||
assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
|
||||
end
|
||||
Reference in New Issue
Block a user