/*
 *  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(method, recv)
    VALUE method, recv;
{
    struct METHOD *data, *bound;
    VALUE rklass = CLASS_OF(recv);

    Data_Get_Struct(method, struct METHOD, data);
    if (data->rklass != rklass) {
        if (FL_TEST(data->rklass, FL_SINGLETON)) {
            rb_raise(rb_eTypeError, "singleton method bound for a different object");
        }
        if (TYPE(data->rklass) == T_MODULE) {
            st_table *m_tbl = RCLASS(data->rklass)->m_tbl;
            while (RCLASS(rklass)->m_tbl != m_tbl) {
                rklass = RCLASS(rklass)->super;
                if (!rklass) goto not_instace;
            }
        }
        else if (!rb_obj_is_kind_of(recv, data->rklass)) {
          not_instace:
            rb_raise(rb_eTypeError, "bind argument must be an instance of %s",
                     rb_class2name(data->rklass));
        }
    }

    method = Data_Make_Struct(rb_cMethod,struct METHOD,bm_mark,free,bound);
    *bound = *data;
    bound->recv = recv;
    bound->rklass = rklass;

    return method;
}