class Method

Method objects are created by Object#method, 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 UnboundMethod) 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]

[ 1, 2, 3 ].each(&method(:puts)) #=> prints 1, 2, 3

require 'date'
%w[2017-03-01 2017-03-02].collect(&Date.method(:parse))
#=> [#<Date: 2017-03-01 ((2457814j,0s,0n),+0s,2299161j)>, #<Date: 2017-03-02 ((2457815j,0s,0n),+0s,2299161j)>]

Public Instance Methods

Returns a proc that is the composition of the given g and this method.

The returned proc takes a variable number of arguments. It first calls g with the arguments, then calls self with the return value of g.

def f(ary) = ary << 'in f'

f = self.method(:f)
g = proc { |ary| ary << 'in proc' }
(f << g).call([]) # => ["in proc", "in f"]
static VALUE
rb_method_compose_to_left(VALUE self, VALUE g)
{
    g = to_callable(g);
    self = method_to_proc(self);
    return proc_compose_to_left(self, g);
}

Returns whether self and other are bound to the same object and refer to the same method definition and the classes defining the methods are the same class or module.

static VALUE
method_eq(VALUE method, VALUE other)
{
    struct METHOD *m1, *m2;
    VALUE klass1, klass2;

    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 *)RTYPEDDATA_GET_DATA(method);
    m2 = (struct METHOD *)RTYPEDDATA_GET_DATA(other);

    klass1 = method_entry_defined_class(m1->me);
    klass2 = method_entry_defined_class(m2->me);
    if (RB_TYPE_P(klass1, T_ICLASS)) klass1 = RBASIC_CLASS(klass1);
    if (RB_TYPE_P(klass2, T_ICLASS)) klass2 = RBASIC_CLASS(klass2);

    if (!rb_method_entry_eq(m1->me, m2->me) ||
        klass1 != klass2 ||
        m1->klass != m2->klass ||
        m1->recv != m2->recv) {
        return Qfalse;
    }

    return Qtrue;
}
Also aliased as: eql?

Invokes self with the specified arguments, returning the method’s return value.

m = 12.method("+")
m.call(3)    #=> 15
m.call(20)   #=> 32

Using Method#=== allows a method object to be the target of a when clause in a case statement.

require 'prime'

case 1373
when Prime.method(:prime?)
  # ...
end
Alias for: call

Returns a proc that is the composition of this method and the given g.

The returned proc takes a variable number of arguments. It first calls self with the arguments, then calls g with the return value of self.

def f(ary) = ary << 'in f'

f = self.method(:f)
g = proc { |ary| ary << 'in proc' }
(f >> g).call([]) # => ["in f", "in proc"]
static VALUE
rb_method_compose_to_right(VALUE self, VALUE g)
{
    g = to_callable(g);
    self = method_to_proc(self);
    return proc_compose_to_right(self, g);
}
Alias for: call

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. Keyword arguments will be considered as a single additional argument, that argument being mandatory if any keyword argument is mandatory. 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
  def seven(a, b, x:0); end
  def eight(x:, y:); end
  def nine(x:, y:, **z); end
  def ten(*a, x:, y:); 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
c.method(:seven).arity   #=> -3
c.method(:eight).arity   #=> 1
c.method(:nine).arity    #=> 1
c.method(:ten).arity     #=> -2

"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);
}

Returns the Ruby::Box where meth is defined in.

static VALUE
method_box(VALUE obj)
{
    struct METHOD *data;
    const rb_box_t *box;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    box = data->me->def->box;
    if (!box) return Qnil;
    if (box->box_object) return box->box_object;
    rb_bug("Unexpected box on the method definition: %p", (void*) box);
    UNREACHABLE_RETURN(Qnil);
}

Invokes self with the specified arguments, returning the method’s return value.

m = 12.method("+")
m.call(3)    #=> 15
m.call(20)   #=> 32

Using Method#=== allows a method object to be the target of a when clause in a case statement.

require 'prime'

case 1373
when Prime.method(:prime?)
  # ...
end
static VALUE
rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method)
{
    return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS);
}
Also aliased as: ===, []

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(rb_obj_class(self), struct METHOD, &method_data_type, data);
    rb_obj_clone_setup(self, clone, Qnil);
    RB_OBJ_WRITE(clone, &data->recv, orig->recv);
    RB_OBJ_WRITE(clone, &data->klass, orig->klass);
    RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
    RB_OBJ_WRITE(clone, &data->owner, orig->owner);
    RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
    return clone;
}

Returns a curried proc based on the method. When the proc is called with a number of arguments that is lower than the method’s arity, then another curried proc is returned. Only when enough arguments have been supplied to satisfy the method signature, will the method actually be called.

The optional arity argument should be supplied when currying methods with variable arguments to determine how many arguments are needed before the method is called.

def foo(a,b,c)
  [a, b, c]
end

proc  = self.method(:foo).curry
proc2 = proc.call(1, 2)          #=> #<Proc>
proc2.call(3)                    #=> [1,2,3]

def vararg(*args)
  args
end

proc = self.method(:vararg).curry(4)
proc2 = proc.call(:x)      #=> #<Proc>
proc3 = proc2.call(:y, :z) #=> #<Proc>
proc3.call(:a)             #=> [:x, :y, :z, :a]
static VALUE
rb_method_curry(int argc, const VALUE *argv, VALUE self)
{
    VALUE proc = method_to_proc(self);
    return proc_curry(argc, argv, proc);
}
Alias for: ==

Returns a hash value corresponding to the method object.

See also Object#hash.

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->recv);
    hash = rb_hash_method_entry(hash, m->me);
    hash = rb_hash_end(hash);

    return ST2FIX(hash);
}

Returns a human-readable description of the underlying method.

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect    #=> "#<Method: Range(Enumerable)#map()>"

In the latter case, the method description includes the “owner” of the original method (Enumerable module, which is included into Range).

inspect also provides, when possible, method argument names (call sequence) and source location.

require 'net/http'
Net::HTTP.method(:get).inspect
#=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"

... in argument definition means argument is optional (has some default value).

For methods defined in C (language core and extensions), location and argument names can’t be extracted, and only generic information is provided in form of * (any number of arguments) or _ (some positional argument).

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect       #=> "#<Method: String#+(_)>""
static VALUE
method_inspect(VALUE method)
{
    struct METHOD *data;
    VALUE str;
    const char *sharp = "#";
    VALUE mklass;
    VALUE defined_class;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));

    mklass = data->iclass;
    if (!mklass) mklass = data->klass;

    if (RB_TYPE_P(mklass, T_ICLASS)) {
        /* TODO: I'm not sure why mklass is T_ICLASS.
         * UnboundMethod#bind() can set it as T_ICLASS at convert_umethod_to_method_components()
         * but not sure it is needed.
         */
        mklass = RBASIC_CLASS(mklass);
    }

    if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
        defined_class = data->me->def->body.alias.original_me->owner;
    }
    else {
        defined_class = method_entry_defined_class(data->me);
    }

    if (RB_TYPE_P(defined_class, T_ICLASS)) {
        defined_class = RBASIC_CLASS(defined_class);
    }

    if (UNDEF_P(data->recv)) {
        // UnboundMethod
        rb_str_buf_append(str, rb_inspect(defined_class));
    }
    else if (RCLASS_SINGLETON_P(mklass)) {
        VALUE v = RCLASS_ATTACHED_OBJECT(mklass);

        if (UNDEF_P(data->recv)) {
            rb_str_buf_append(str, rb_inspect(mklass));
        }
        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 {
        mklass = data->klass;
        if (RB_TYPE_P(mklass, T_ICLASS)) {
            mklass = RBASIC_CLASS(mklass);
        }
        if (RCLASS_SINGLETON_P(mklass)) {
            VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
            if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) {
                do {
                   mklass = RCLASS_SUPER(mklass);
                } while (RB_TYPE_P(mklass, T_ICLASS));
            }
        }
        rb_str_buf_append(str, rb_inspect(mklass));
        if (defined_class != mklass) {
            rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
        }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_append(str, rb_id2str(data->me->called_id));
    if (data->me->called_id != data->me->def->original_id) {
        rb_str_catf(str, "(%"PRIsVALUE")",
                    rb_id2str(data->me->def->original_id));
    }
    if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
        rb_str_buf_cat2(str, " (not-implemented)");
    }

    // parameter information
    {
        VALUE params = rb_method_parameters(method);
        VALUE pair, name, kind;
        const VALUE req = ID2SYM(rb_intern("req"));
        const VALUE opt = ID2SYM(rb_intern("opt"));
        const VALUE keyreq = ID2SYM(rb_intern("keyreq"));
        const VALUE key = ID2SYM(rb_intern("key"));
        const VALUE rest = ID2SYM(rb_intern("rest"));
        const VALUE keyrest = ID2SYM(rb_intern("keyrest"));
        const VALUE block = ID2SYM(rb_intern("block"));
        const VALUE nokey = ID2SYM(rb_intern("nokey"));
        const VALUE noblock = ID2SYM(rb_intern("noblock"));
        int forwarding = 0;

        rb_str_buf_cat2(str, "(");

        if (RARRAY_LEN(params) == 3 &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) {
            forwarding = 1;
        }

        for (int i = 0; i < RARRAY_LEN(params); i++) {
            pair = RARRAY_AREF(params, i);
            kind = RARRAY_AREF(pair, 0);
            if (RARRAY_LEN(pair) > 1) {
                name = RARRAY_AREF(pair, 1);
            }
            else {
                name = Qnil;
            }

            if (kind == req) {
                append_param_name(str, name, "_");
            }
            else if (kind == opt) {
                rb_str_cat_cstr(append_param_name(str, name, "_"), "=...");
            }
            else if (kind == keyreq) {
                rb_str_cat_cstr(append_param_name(str, name, NULL), ":");
            }
            else if (kind == key) {
                rb_str_cat_cstr(append_param_name(str, name, NULL), ": ...");
            }
            else if (kind == rest) {
                rb_str_cat_cstr(str, forwarding ? "..." : "*");
                if (name != ID2SYM('*')) {
                    append_param_name(str, name, NULL);
                }
            }
            else if (kind == keyrest) {
                if (name != ID2SYM(idPow)) {
                    append_param_name(rb_str_cat_cstr(str, "**"), name, NULL);
                }
                else if (i > 0) {
                    rb_str_set_len(str, RSTRING_LEN(str) - 2);
                }
                else {
                    rb_str_cat_cstr(str, "**");
                }
            }
            else if (kind == block) {
                if (name == ID2SYM('&')) {
                    if (forwarding) {
                        rb_str_set_len(str, RSTRING_LEN(str) - 2);
                    }
                    else {
                        rb_str_cat_cstr(str, "...");
                    }
                }
                else {
                    append_param_name(rb_str_cat_cstr(str, "&"), name, NULL);
                }
            }
            else if (kind == nokey) {
                rb_str_buf_cat2(str, "**nil");
            }
            else if (kind == noblock) {
                rb_str_buf_cat2(str, "&nil");
            }

            if (i < RARRAY_LEN(params) - 1) {
                rb_str_buf_cat2(str, ", ");
            }
        }
        rb_str_buf_cat2(str, ")");
    }

    { // source location
        VALUE loc = rb_method_location(method);
        if (!NIL_P(loc)) {
            rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
                        RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
        }
    }

    rb_str_buf_cat2(str, ">");

    return str;
}
Also aliased as: to_s

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->me->called_id);
}

Returns the original name of the method.

class C
  def foo; end
  alias bar foo
end
C.instance_method(:bar).original_name # => :foo
static VALUE
method_original_name(VALUE obj)
{
    struct METHOD *data;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return ID2SYM(data->me->def->original_id);
}

Returns the class or module on which this method is defined. In other words,

meth.owner.instance_methods(false).include?(meth.name) # => true

holds as long as the method is not removed/undefined/replaced, (with private_instance_methods instead of instance_methods if the method is private).

See also Method#receiver.

(1..3).method(:map).owner #=> Enumerable
static VALUE
method_owner(VALUE obj)
{
    struct METHOD *data;
    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return data->owner;
}

Returns the parameter information of this method.

def foo(bar); end
method(:foo).parameters #=> [[:req, :bar]]

def foo(bar, baz, bat, &blk); end
method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:req, :bat], [:block, :blk]]

def foo(bar, *args); end
method(:foo).parameters #=> [[:req, :bar], [:rest, :args]]

def foo(bar, baz, *args, &blk); end
method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:rest, :args], [:block, :blk]]
static VALUE
rb_method_parameters(VALUE method)
{
    return method_def_parameters(rb_method_def(method));
}

Returns the bound receiver of the method object.

(1..3).method(:map).receiver # => 1..3
static VALUE
method_receiver(VALUE obj)
{
    struct METHOD *data;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return data->recv;
}

Returns a two-element array containing the Ruby source filename as a string and the line number integer where self is defined:

def greeting = "hello"
method(:greeting).source_location # => ["test.rb", 1]

Returns nil if self is not a method defined in Ruby (i.e. defined using native code):

Kernel.method(:puts).source_location # => nil
VALUE
rb_method_location(VALUE method)
{
    return method_def_location(rb_method_def(method));
}

Returns a Ruby::SourceRange for this method, or nil if this method was not defined in Ruby (i.e. native) or has no source path.

The returned Ruby::SourceRange includes the source path, absolute path when available, and the start and end line and byte-column coordinates.

See github.com/ruby/spec/blob/master/core/method/shared/source_range.rb for the location of start/end line/column in various cases.

static VALUE
rb_method_source_range(VALUE method)
{
    return method_def_source_range(rb_method_def(method));
}

Returns a Method of superclass which would be called when super is used or nil if there is no method on superclass.

static VALUE
method_super_method(VALUE method)
{
    const struct METHOD *data;
    VALUE super_class, iclass;
    ID mid;
    const rb_method_entry_t *me;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    iclass = data->iclass;
    if (!iclass) return Qnil;
    bool is_refinement_method = refinement_module_p(data->me->owner);
    if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) {
        super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
            data->me->def->body.alias.original_me->owner));
        mid = data->me->def->body.alias.original_me->def->original_id;
    }
    else if (is_refinement_method) {
        if (RICLASS_FOR_REFINEMENT_P(iclass)) {
            do {
                super_class = RCLASS_SUPER(iclass);
                iclass = super_class;
            } while (RICLASS_FOR_REFINEMENT_P(super_class));
        }
        else {
            super_class = find_refined_target_ancestor(iclass, rb_refinement_module_get_refined_class(data->me->owner));
            if (!super_class) {
                super_class = RCLASS_SUPER(data->me->owner);
            }
        }
        mid = data->me->def->original_id;
    }
    else {
        VALUE klass = iclass;
        if (RICLASS_FOR_REFINEMENT_P(klass)) {
            // Refined methods need this check before superclass determination
            klass = RBASIC(klass)->klass;
        }
        super_class = RCLASS_SUPER(RCLASS_ORIGIN(klass));
        mid = data->me->def->original_id;
    }
    if (!super_class) return Qnil;

    // For refined methods, skip refinements for the same definition, but consider
    // refinements for superclass methods
    const rb_method_definition_t *skip_def = is_refinement_method ? data->me->def : NULL;

    // Use the CREF of the Method/UnboundMethod, not the CREF of the caller of super_method.
    // We must avoid the use of rb_callable_method_entry_with_refinements, as that will
    // implicitly use the refinements activated in of the caller of super_method.
    const rb_cref_t *cref = NULL;
    switch (data->me->def->type) {
      case VM_METHOD_TYPE_ISEQ:
        cref = data->me->def->body.iseq.cref;
        break;
      case VM_METHOD_TYPE_BMETHOD: {
        const rb_proc_t *proc;
        GetProcPtr(data->me->def->body.bmethod.proc, proc);
        const struct rb_block *block = &proc->block;
        if (vm_block_type(block) == block_type_iseq)
            cref = rb_vm_get_cref(block->as.captured.ep);
        break;
      }
      default:
        break;
    }
    VALUE klass = super_class;
    me = NULL;
    while (klass) {
        const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
        if (!cme) break;
        if (cme->def->type != VM_METHOD_TYPE_REFINED) {
            me = (rb_method_entry_t *)cme;
            iclass = cme->defined_class;
            break;
        }
        // Look through all CREF scopes for a refinement for cme->owner, mirroring
        // the loop in search_refined_method.
        const rb_cref_t *c;
        for (c = cref; c; c = CREF_NEXT(c)) {
            VALUE refs = CREF_REFINEMENTS(c);
            if (NIL_P(refs)) continue;
            VALUE r = rb_hash_lookup(refs, cme->owner);
            if (NIL_P(r)) continue;
            r = rb_vm_refinement_iclass_for_cme(r, cme);
            const rb_callable_method_entry_t *ref_cme = rb_callable_method_entry(r, mid);
            if (!ref_cme) break;
            if (ref_cme->def->type == VM_METHOD_TYPE_REFINED) continue;
            if (skip_def && rb_method_definition_eq(ref_cme->def, skip_def)) continue;
            me = (rb_method_entry_t *)ref_cme;
            iclass = ref_cme->defined_class;
            break;
        }
        if (me) break;
        // No refined method found. Use orig_me if available, or normal method lookup
        // in superclass otherwise.
        const rb_method_entry_t *orig_me = cme->def->body.refined.orig_me;
        if (orig_me) {
            me = (rb_method_entry_t *)orig_me;
            iclass = cme->defined_class;
            break;
        }
        klass = RCLASS_SUPER(cme->defined_class);
    }
    if (!me) return Qnil;
    return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
}

Returns the AST node that this method was compiled from. Returns nil for methods not written in Ruby. See RubyVM::InstructionSequence#syntax_tree for other cases where nil is returned.

This method is experimental and might change without notice.

# File ast.rb, line 443
def syntax_tree
  RubyVM::InstructionSequence.of(self)&.syntax_tree
end

Returns a Proc object corresponding to this method.

static VALUE
method_to_proc(VALUE method)
{
    VALUE procval;
    rb_proc_t *proc;

    /*
     * class Method
     *   def to_proc
     *     lambda{|*args|
     *       self.call(*args)
     *     }
     *   end
     * end
     */
    procval = rb_block_call(rb_mRubyVMFrozenCore, idLambda, 0, 0, bmcall, method);
    GetProcPtr(procval, proc);
    proc->header.is_from_method = 1;
    return procval;
}

Returns a human-readable description of the underlying method.

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect    #=> "#<Method: Range(Enumerable)#map()>"

In the latter case, the method description includes the “owner” of the original method (Enumerable module, which is included into Range).

inspect also provides, when possible, method argument names (call sequence) and source location.

require 'net/http'
Net::HTTP.method(:get).inspect
#=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"

... in argument definition means argument is optional (has some default value).

For methods defined in C (language core and extensions), location and argument names can’t be extracted, and only generic information is provided in form of * (any number of arguments) or _ (some positional argument).

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect       #=> "#<Method: String#+(_)>""
Alias for: inspect

Dissociates meth from its current receiver. The resulting UnboundMethod can subsequently be bound to a new object of the same class (see UnboundMethod).

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);
    RB_OBJ_WRITE(method, &data->recv, Qundef);
    RB_OBJ_WRITE(method, &data->klass, Qundef);
    RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
    RB_OBJ_WRITE(method, &data->owner, orig->me->owner);
    RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));

    return method;
}