mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Merge branch 'module-prepend' of https://github.com/polyfox/mruby into polyfox-module-prepend
This commit is contained in:
@@ -206,6 +206,7 @@ MRB_API struct RClass *mrb_define_class(mrb_state *, const char*, struct RClass*
|
||||
MRB_API struct RClass *mrb_define_module(mrb_state *, const char*);
|
||||
MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
|
||||
MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
|
||||
MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
|
||||
|
||||
MRB_API void mrb_define_method(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
|
||||
MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
|
||||
|
||||
@@ -16,6 +16,7 @@ struct RClass {
|
||||
struct iv_tbl *iv;
|
||||
struct kh_mt *mt;
|
||||
struct RClass *super;
|
||||
struct RClass *origin;
|
||||
};
|
||||
|
||||
#define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v)))
|
||||
@@ -48,8 +49,11 @@ mrb_class(mrb_state *mrb, mrb_value v)
|
||||
}
|
||||
}
|
||||
|
||||
#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~0xff) | (char)tt)
|
||||
#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & 0xff)
|
||||
// TODO: figure out where to put user flags
|
||||
#define MRB_FLAG_IS_ORIGIN (1 << 20)
|
||||
#define MRB_INSTANCE_TT_MASK (0xFF)
|
||||
#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt)
|
||||
#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK)
|
||||
|
||||
MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*);
|
||||
MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
struct RClass *c;\
|
||||
struct RBasic *gcnext
|
||||
|
||||
#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag)
|
||||
|
||||
/* white: 011, black: 100, gray: 000 */
|
||||
#define MRB_GC_GRAY 0
|
||||
#define MRB_GC_WHITE_A 1
|
||||
|
||||
+138
-43
@@ -76,7 +76,8 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
|
||||
|
||||
if (o->c->tt == MRB_TT_SCLASS) return;
|
||||
sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
|
||||
sc->mt = 0;
|
||||
sc->origin = sc;
|
||||
sc->mt = kh_init(mt, mrb);
|
||||
sc->iv = 0;
|
||||
if (o->tt == MRB_TT_CLASS) {
|
||||
c = (struct RClass*)o;
|
||||
@@ -194,6 +195,7 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *
|
||||
|
||||
if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
|
||||
c = class_from_sym(mrb, outer, name);
|
||||
c = c->origin;
|
||||
if (super && mrb_class_real(c->super) != super) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)",
|
||||
mrb_sym2str(mrb, name),
|
||||
@@ -323,8 +325,9 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s
|
||||
MRB_API void
|
||||
mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p)
|
||||
{
|
||||
khash_t(mt) *h = c->mt;
|
||||
khash_t(mt) *h;
|
||||
khiter_t k;
|
||||
h = c->origin->mt;
|
||||
|
||||
if (!h) h = c->mt = kh_init(mt, mrb);
|
||||
k = kh_put(mt, mrb, h, mid);
|
||||
@@ -806,6 +809,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
|
||||
struct RClass *c;
|
||||
|
||||
c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class);
|
||||
c->origin = c;
|
||||
if (super) {
|
||||
c->super = super;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super);
|
||||
@@ -817,47 +821,133 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
|
||||
return c;
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
|
||||
static void
|
||||
boot_initmod(mrb_state *mrb, struct RClass *mod)
|
||||
{
|
||||
struct RClass *ins_pos;
|
||||
mod->origin = mod;
|
||||
mod->mt = kh_init(mt, mrb);
|
||||
}
|
||||
|
||||
static struct RClass*
|
||||
include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super)
|
||||
{
|
||||
struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class);
|
||||
if (m->tt == MRB_TT_ICLASS) {
|
||||
m = m->c;
|
||||
}
|
||||
ic->origin = ic;
|
||||
ic->iv = m->iv;
|
||||
ic->mt = m->origin->mt;
|
||||
ic->super = super;
|
||||
if (m->tt == MRB_TT_ICLASS) {
|
||||
ic->c = m->c;
|
||||
} else {
|
||||
ic->c = m;
|
||||
}
|
||||
return ic;
|
||||
}
|
||||
|
||||
static int
|
||||
include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super)
|
||||
{
|
||||
struct RClass *p, *ic;
|
||||
void *klass_mt = c->origin->mt;
|
||||
|
||||
ins_pos = c;
|
||||
while (m) {
|
||||
struct RClass *p = c, *ic;
|
||||
int superclass_seen = 0;
|
||||
|
||||
if (c->mt && c->mt == m->mt) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
|
||||
}
|
||||
while (p) {
|
||||
if (c != p && p->tt == MRB_TT_CLASS) {
|
||||
superclass_seen = 1;
|
||||
}
|
||||
else if (p->mt == m->mt) {
|
||||
if (p->tt == MRB_TT_ICLASS && !superclass_seen) {
|
||||
ins_pos = p;
|
||||
if (m->origin != m)
|
||||
goto skip;
|
||||
|
||||
if (klass_mt && klass_mt == m->mt)
|
||||
return -1;
|
||||
|
||||
p = c->super;
|
||||
while(p) {
|
||||
if (p->tt == MRB_TT_ICLASS) {
|
||||
if (p->mt == m->mt) {
|
||||
if (!superclass_seen) {
|
||||
ins_pos = p; // move insert point
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
goto skip;
|
||||
} else if (p->tt == MRB_TT_CLASS) {
|
||||
if (!search_super) break;
|
||||
superclass_seen = 1;
|
||||
}
|
||||
p = p->super;
|
||||
}
|
||||
ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class);
|
||||
if (m->tt == MRB_TT_ICLASS) {
|
||||
ic->c = m->c;
|
||||
}
|
||||
else {
|
||||
ic->c = m;
|
||||
}
|
||||
ic->mt = m->mt;
|
||||
ic->iv = m->iv;
|
||||
ic->super = ins_pos->super;
|
||||
|
||||
ic = include_class_new(mrb, m, ins_pos->super);
|
||||
ins_pos->super = ic;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super);
|
||||
ins_pos = ic;
|
||||
skip:
|
||||
m = m->super;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
|
||||
{
|
||||
int changed = include_module_at(mrb, c, c->origin, m, 1);
|
||||
if (changed < 0) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
|
||||
{
|
||||
struct RClass *origin;
|
||||
int changed = 0;
|
||||
|
||||
origin = c->origin;
|
||||
if (origin == c) {
|
||||
origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
|
||||
origin->flags |= MRB_FLAG_IS_ORIGIN;
|
||||
origin->origin = origin;
|
||||
origin->super = c->super;
|
||||
c->super = origin;
|
||||
c->origin = origin;
|
||||
origin->mt = c->mt;
|
||||
c->mt = kh_init(mt, mrb);
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->origin);
|
||||
}
|
||||
changed = include_module_at(mrb, c, c, m, 0);
|
||||
if (changed < 0) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
|
||||
}
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod)
|
||||
{
|
||||
mrb_value klass;
|
||||
|
||||
mrb_check_type(mrb, mod, MRB_TT_MODULE);
|
||||
mrb_get_args(mrb, "C", &klass);
|
||||
mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod));
|
||||
return mod;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_mod_prepend(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
mrb_value *argv;
|
||||
mrb_int argc, i;
|
||||
|
||||
mrb_get_args(mrb, "*", &argv, &argc);
|
||||
for (i=0; i<argc; i++) {
|
||||
mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
|
||||
}
|
||||
while (argc--) {
|
||||
mrb_funcall(mrb, argv[argc], "prepend_features", 1, klass);
|
||||
mrb_funcall(mrb, argv[argc], "prepended", 1, klass);
|
||||
}
|
||||
|
||||
return klass;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
@@ -931,15 +1021,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value result;
|
||||
struct RClass *c = mrb_class_ptr(self);
|
||||
|
||||
result = mrb_ary_new(mrb);
|
||||
mrb_ary_push(mrb, result, mrb_obj_value(c));
|
||||
c = c->super;
|
||||
while (c) {
|
||||
if (c->tt == MRB_TT_ICLASS) {
|
||||
mrb_ary_push(mrb, result, mrb_obj_value(c->c));
|
||||
}
|
||||
else if (c->tt != MRB_TT_SCLASS) {
|
||||
else if (c->origin == c) {
|
||||
mrb_ary_push(mrb, result, mrb_obj_value(c));
|
||||
}
|
||||
c = c->super;
|
||||
@@ -964,11 +1051,14 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value result;
|
||||
struct RClass *c = mrb_class_ptr(self);
|
||||
struct RClass *origin = c->origin;
|
||||
|
||||
result = mrb_ary_new(mrb);
|
||||
while (c) {
|
||||
if (c->tt == MRB_TT_ICLASS) {
|
||||
mrb_ary_push(mrb, result, mrb_obj_value(c->c));
|
||||
if (c != origin && c->tt == MRB_TT_ICLASS) {
|
||||
if (c->c->tt == MRB_TT_MODULE) {
|
||||
mrb_ary_push(mrb, result, mrb_obj_value(c->c));
|
||||
}
|
||||
}
|
||||
c = c->super;
|
||||
}
|
||||
@@ -980,10 +1070,11 @@ static mrb_value
|
||||
mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
|
||||
{
|
||||
mrb_value b;
|
||||
|
||||
mrb_get_args(mrb, "&", &b);
|
||||
struct RClass *m = mrb_class_ptr(mod);
|
||||
boot_initmod(mrb, m); // bootstrap a newly initialized module
|
||||
mrb_get_args(mrb, "|&", &b);
|
||||
if (!mrb_nil_p(b)) {
|
||||
mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod));
|
||||
mrb_yield_with_class(mrb, b, 1, &mod, mod, m);
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
@@ -1300,9 +1391,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass)
|
||||
struct RClass *c;
|
||||
|
||||
c = mrb_class_ptr(klass);
|
||||
c = c->super;
|
||||
c = c->origin->super;
|
||||
while (c && c->tt == MRB_TT_ICLASS) {
|
||||
c = c->super;
|
||||
c = c->origin->super;
|
||||
}
|
||||
if (!c) return mrb_nil_value();
|
||||
return mrb_obj_value(c);
|
||||
@@ -1540,8 +1631,7 @@ MRB_API struct RClass*
|
||||
mrb_module_new(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class);
|
||||
m->mt = kh_init(mt, mrb);
|
||||
|
||||
boot_initmod(mrb, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -1900,13 +1990,14 @@ static void
|
||||
remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
|
||||
{
|
||||
struct RClass *c = mrb_class_ptr(mod);
|
||||
khash_t(mt) *h = c->mt;
|
||||
khash_t(mt) *h = c->origin->mt;
|
||||
khiter_t k;
|
||||
|
||||
if (h) {
|
||||
k = kh_get(mt, mrb, h, mid);
|
||||
if (k != kh_end(h)) {
|
||||
kh_del(mt, mrb, h, k);
|
||||
mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2140,6 +2231,9 @@ mrb_init_class(mrb_state *mrb)
|
||||
mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */
|
||||
mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */
|
||||
mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */
|
||||
mrb_define_method(mrb, mod, "prepend", mrb_mod_prepend, MRB_ARGS_ANY());
|
||||
mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, mod, "include", mrb_mod_include, MRB_ARGS_ANY()); /* 15.2.2.4.27 */
|
||||
mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */
|
||||
mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */
|
||||
@@ -2156,6 +2250,7 @@ mrb_init_class(mrb_state *mrb)
|
||||
mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */
|
||||
mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */
|
||||
mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */
|
||||
mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */
|
||||
mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */
|
||||
mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE());
|
||||
|
||||
@@ -498,7 +498,12 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
|
||||
mrb_gc_mark(mrb, (struct RBasic*)obj->c);
|
||||
switch (obj->tt) {
|
||||
case MRB_TT_ICLASS:
|
||||
mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
|
||||
{
|
||||
struct RClass *c = (struct RClass*)obj;
|
||||
if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN))
|
||||
mrb_gc_mark_mt(mrb, c);
|
||||
mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
|
||||
}
|
||||
break;
|
||||
|
||||
case MRB_TT_CLASS:
|
||||
@@ -624,7 +629,10 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
|
||||
mrb_gc_free_mt(mrb, (struct RClass*)obj);
|
||||
mrb_gc_free_iv(mrb, (struct RObject*)obj);
|
||||
break;
|
||||
|
||||
case MRB_TT_ICLASS:
|
||||
if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN))
|
||||
mrb_gc_free_mt(mrb, (struct RClass*)obj);
|
||||
break;
|
||||
case MRB_TT_ENV:
|
||||
{
|
||||
struct REnv *e = (struct REnv*)obj;
|
||||
|
||||
+19
-1
@@ -248,6 +248,11 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
|
||||
clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
|
||||
}
|
||||
|
||||
if (klass->origin != klass)
|
||||
clone->origin = klass->origin;
|
||||
else
|
||||
clone->origin = clone;
|
||||
|
||||
clone->super = klass->super;
|
||||
if (klass->iv) {
|
||||
mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
|
||||
@@ -269,6 +274,13 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
|
||||
{
|
||||
struct RClass *dc = mrb_class_ptr(dst);
|
||||
struct RClass *sc = mrb_class_ptr(src);
|
||||
/* if the origin is not the same as the class, then the origin and
|
||||
the current class need to be copied */
|
||||
if (sc->origin != sc) {
|
||||
dc->origin = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(sc->origin)));
|
||||
} else {
|
||||
dc->origin = dc;
|
||||
}
|
||||
dc->mt = kh_copy(mt, mrb, sc->mt);
|
||||
dc->super = sc->super;
|
||||
}
|
||||
@@ -641,13 +653,19 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl
|
||||
{
|
||||
khint_t i;
|
||||
mrb_value ary;
|
||||
mrb_bool prepended;
|
||||
struct RClass* oldklass;
|
||||
khash_t(st)* set = kh_init(st, mrb);
|
||||
|
||||
if (!recur && klass->origin != klass) {
|
||||
klass = klass->origin;
|
||||
prepended = 1;
|
||||
}
|
||||
|
||||
oldklass = 0;
|
||||
while (klass && (klass != oldklass)) {
|
||||
method_entry_loop(mrb, klass, set);
|
||||
if ((klass->tt == MRB_TT_ICLASS) ||
|
||||
if ((klass->tt == MRB_TT_ICLASS && !prepended) ||
|
||||
(klass->tt == MRB_TT_SCLASS)) {
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -487,6 +487,7 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "class or module required");
|
||||
}
|
||||
|
||||
c = c->origin;
|
||||
while (cl) {
|
||||
if (cl == c || cl->mt == c->mt)
|
||||
return TRUE;
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
##
|
||||
# Module ISO Test
|
||||
|
||||
def labeled_module(name, &block)
|
||||
Module.new do
|
||||
singleton_class.class_eval do
|
||||
define_method(:to_s) { name }
|
||||
alias_method :inspect, :to_s
|
||||
end
|
||||
class_eval(&block) if block
|
||||
end
|
||||
end
|
||||
|
||||
def labeled_class(name, supklass = Object, &block)
|
||||
Class.new(supklass) do
|
||||
singleton_class.class_eval do
|
||||
define_method(:to_s) { name }
|
||||
alias_method :inspect, :to_s
|
||||
end
|
||||
class_eval(&block) if block
|
||||
end
|
||||
end
|
||||
|
||||
assert('Module', '15.2.2') do
|
||||
assert_equal Class, Module.class
|
||||
end
|
||||
@@ -474,6 +494,286 @@ end
|
||||
|
||||
# Not ISO specified
|
||||
|
||||
# @!group prepend
|
||||
assert('Module#prepend') do
|
||||
module M0
|
||||
def m1; [:M0] end
|
||||
end
|
||||
module M1
|
||||
def m1; [:M1, super, :M1] end
|
||||
end
|
||||
module M2
|
||||
def m1; [:M2, super, :M2] end
|
||||
end
|
||||
M3 = Module.new do
|
||||
def m1; [:M3, super, :M3] end
|
||||
end
|
||||
module M4
|
||||
def m1; [:M4, super, :M4] end
|
||||
end
|
||||
|
||||
class P0
|
||||
include M0
|
||||
prepend M1
|
||||
def m1; [:C0, super, :C0] end
|
||||
end
|
||||
class P1 < P0
|
||||
prepend M2, M3
|
||||
include M4
|
||||
def m1; [:C1, super, :C1] end
|
||||
end
|
||||
|
||||
obj = P1.new
|
||||
expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2]
|
||||
assert_equal(expected, obj.m1)
|
||||
end
|
||||
|
||||
# mruby shouldn't be affected by this since there is
|
||||
# no visibility control (yet)
|
||||
assert('Module#prepend public') do
|
||||
assert_nothing_raised('ruby/ruby #8846') do
|
||||
Class.new.prepend(Module.new)
|
||||
end
|
||||
end
|
||||
|
||||
assert('Module#prepend inheritance') do
|
||||
bug6654 = '[ruby-core:45914]'
|
||||
a = labeled_module('a')
|
||||
b = labeled_module('b') { include a }
|
||||
c = labeled_module('c') { prepend b }
|
||||
|
||||
#assert bug6654 do
|
||||
# the Module#< operator should be used here instead, but we don't have it
|
||||
assert_include(c.ancestors, a)
|
||||
assert_include(c.ancestors, b)
|
||||
#end
|
||||
|
||||
bug8357 = '[ruby-core:54736] [Bug #8357]'
|
||||
b = labeled_module('b') { prepend a }
|
||||
c = labeled_class('c') { include b }
|
||||
|
||||
#assert bug8357 do
|
||||
# the Module#< operator should be used here instead, but we don't have it
|
||||
assert_include(c.ancestors, a)
|
||||
assert_include(c.ancestors, b)
|
||||
#end
|
||||
|
||||
bug8357 = '[ruby-core:54742] [Bug #8357]'
|
||||
assert_kind_of(b, c.new, bug8357)
|
||||
end
|
||||
|
||||
assert('Moduler#prepend + #instance_methods') do
|
||||
bug6655 = '[ruby-core:45915]'
|
||||
assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655)
|
||||
end
|
||||
|
||||
assert 'Module#prepend + #singleton_methods' do
|
||||
o = Object.new
|
||||
o.singleton_class.class_eval {prepend Module.new}
|
||||
assert_equal([], o.singleton_methods)
|
||||
end
|
||||
|
||||
assert 'Module#prepend + #remove_method' do
|
||||
c = Class.new do
|
||||
prepend Module.new { def foo; end }
|
||||
end
|
||||
assert_raise(NameError) do
|
||||
c.class_eval do
|
||||
remove_method(:foo)
|
||||
end
|
||||
end
|
||||
c.class_eval do
|
||||
def foo; end
|
||||
end
|
||||
removed = nil
|
||||
c.singleton_class.class_eval do
|
||||
define_method(:method_removed) {|id| removed = id}
|
||||
end
|
||||
assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do
|
||||
c.class_eval do
|
||||
remove_method(:foo)
|
||||
end
|
||||
end
|
||||
assert_equal(:foo, removed)
|
||||
end
|
||||
|
||||
assert 'Module#prepend + Class#ancestors' do
|
||||
bug6658 = '[ruby-core:45919]'
|
||||
m = labeled_module("m")
|
||||
c = labeled_class("c") {prepend m}
|
||||
assert_equal([m, c], c.ancestors[0, 2], bug6658)
|
||||
|
||||
bug6662 = '[ruby-dev:45868]'
|
||||
c2 = labeled_class("c2", c)
|
||||
anc = c2.ancestors
|
||||
assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662)
|
||||
end
|
||||
|
||||
assert 'Module#prepend + Module#ancestors' do
|
||||
bug6659 = '[ruby-dev:45861]'
|
||||
m0 = labeled_module("m0") { def x; [:m0, *super] end }
|
||||
m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 }
|
||||
m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 }
|
||||
c0 = labeled_class("c0") { def x; [:c0] end }
|
||||
c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 }
|
||||
c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 }
|
||||
#
|
||||
assert_equal([m0, m1], m1.ancestors, bug6659)
|
||||
#
|
||||
bug6662 = '[ruby-dev:45868]'
|
||||
assert_equal([m0, m1, m2], m2.ancestors, bug6662)
|
||||
assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662)
|
||||
assert_equal([:m0, :m1, :m2, :c1], c1.new.x)
|
||||
assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662)
|
||||
assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
|
||||
#
|
||||
m3 = labeled_module("m3") { include m1; prepend m1 }
|
||||
assert_equal([m3, m0, m1], m3.ancestors)
|
||||
m3 = labeled_module("m3") { prepend m1; include m1 }
|
||||
assert_equal([m0, m1, m3], m3.ancestors)
|
||||
m3 = labeled_module("m3") { prepend m1; prepend m1 }
|
||||
assert_equal([m0, m1, m3], m3.ancestors)
|
||||
m3 = labeled_module("m3") { include m1; include m1 }
|
||||
assert_equal([m3, m0, m1], m3.ancestors)
|
||||
end
|
||||
|
||||
assert 'Module#prepend #instance_methods(false)' do
|
||||
bug6660 = '[ruby-dev:45863]'
|
||||
assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
|
||||
assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
|
||||
end
|
||||
|
||||
assert 'cyclic Module#prepend' do
|
||||
bug7841 = '[ruby-core:52205] [Bug #7841]'
|
||||
m1 = Module.new
|
||||
m2 = Module.new
|
||||
m1.instance_eval { prepend(m2) }
|
||||
assert_raise(ArgumentError, bug7841) do
|
||||
m2.instance_eval { prepend(m1) }
|
||||
end
|
||||
end
|
||||
|
||||
# these assertions will not run without a #assert_seperately method
|
||||
#assert 'test_prepend_optmethod' do
|
||||
# bug7983 = '[ruby-dev:47124] [Bug #7983]'
|
||||
# assert_separately [], %{
|
||||
# module M
|
||||
# def /(other)
|
||||
# to_f / other
|
||||
# end
|
||||
# end
|
||||
# Fixnum.send(:prepend, M)
|
||||
# assert_equal(0.5, 1 / 2, "#{bug7983}")
|
||||
# }
|
||||
# assert_equal(0, 1 / 2)
|
||||
#end
|
||||
|
||||
# mruby has no visibility control
|
||||
assert 'Module#prepend visibility' do
|
||||
bug8005 = '[ruby-core:53106] [Bug #8005]'
|
||||
c = Class.new do
|
||||
prepend Module.new {}
|
||||
def foo() end
|
||||
protected :foo
|
||||
end
|
||||
a = c.new
|
||||
assert_true a.respond_to?(:foo), bug8005
|
||||
assert_nothing_raised(NoMethodError, bug8005) {a.send :foo}
|
||||
end
|
||||
|
||||
# mruby has no visibility control
|
||||
assert 'Module#prepend inherited visibility' do
|
||||
bug8238 = '[ruby-core:54105] [Bug #8238]'
|
||||
module Test4PrependVisibilityInherited
|
||||
class A
|
||||
def foo() A; end
|
||||
private :foo
|
||||
end
|
||||
class B < A
|
||||
public :foo
|
||||
prepend Module.new
|
||||
end
|
||||
end
|
||||
assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}")
|
||||
end
|
||||
|
||||
assert 'Module#prepend + #included_modules' do
|
||||
bug8025 = '[ruby-core:53158] [Bug #8025]'
|
||||
mixin = labeled_module("mixin")
|
||||
c = labeled_module("c") {prepend mixin}
|
||||
im = c.included_modules
|
||||
assert_not_include(im, c, bug8025)
|
||||
assert_include(im, mixin, bug8025)
|
||||
c1 = labeled_class("c1") {prepend mixin}
|
||||
c2 = labeled_class("c2", c1)
|
||||
im = c2.included_modules
|
||||
assert_not_include(im, c1, bug8025)
|
||||
assert_not_include(im, c2, bug8025)
|
||||
assert_include(im, mixin, bug8025)
|
||||
end
|
||||
|
||||
assert 'Module#prepend super in alias' do
|
||||
skip "super does not currently work in aliased methods"
|
||||
bug7842 = '[Bug #7842]'
|
||||
|
||||
p = labeled_module("P") do
|
||||
def m; "P"+super; end
|
||||
end
|
||||
|
||||
a = labeled_class("A") do
|
||||
def m; "A"; end
|
||||
end
|
||||
|
||||
b = labeled_class("B", a) do
|
||||
def m; "B"+super; end
|
||||
alias m2 m
|
||||
prepend p
|
||||
alias m3 m
|
||||
end
|
||||
|
||||
assert_nothing_raised do
|
||||
assert_equal("BA", b.new.m2, bug7842)
|
||||
end
|
||||
|
||||
assert_nothing_raised do
|
||||
assert_equal("PBA", b.new.m3, bug7842)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'Module#prepend each class' do
|
||||
m = labeled_module("M")
|
||||
c1 = labeled_class("C1") {prepend m}
|
||||
c2 = labeled_class("C2", c1) {prepend m}
|
||||
assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class")
|
||||
end
|
||||
|
||||
assert 'Module#prepend no duplication' do
|
||||
m = labeled_module("M")
|
||||
c = labeled_class("C") {prepend m; prepend m}
|
||||
assert_equal([m, c], c.ancestors[0, 2], "should never duplicate")
|
||||
end
|
||||
|
||||
assert 'Module#prepend in superclass' do
|
||||
m = labeled_module("M")
|
||||
c1 = labeled_class("C1")
|
||||
c2 = labeled_class("C2", c1) {prepend m}
|
||||
c1.class_eval {prepend m}
|
||||
assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
|
||||
end
|
||||
|
||||
# requires #assert_seperately
|
||||
#assert 'Module#prepend call super' do
|
||||
# assert_separately([], <<-'end;') #do
|
||||
# bug10847 = '[ruby-core:68093] [Bug #10847]'
|
||||
# module M; end
|
||||
# Float.prepend M
|
||||
# assert_nothing_raised(SystemStackError, bug10847) do
|
||||
# 0.3.numerator
|
||||
# end
|
||||
# end;
|
||||
#end
|
||||
# @!endgroup prepend
|
||||
|
||||
assert('Module#to_s') do
|
||||
module Test4to_sModules
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user