summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/mruby/class.h1
-rw-r--r--src/class.c67
-rw-r--r--test/t/module.rb33
3 files changed, 99 insertions, 2 deletions
diff --git a/include/mruby/class.h b/include/mruby/class.h
index 9d5260a24..60310ae9d 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -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)))
diff --git a/src/class.c b/src/class.c
index 8a9fdaca6..3b1ea2321 100644
--- a/src/class.c
+++ b/src/class.c
@@ -194,6 +194,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),
@@ -763,12 +764,13 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
else {
c->super = mrb->object_class;
}
+ c->origin = c;
c->mt = kh_init(mt, mrb);
return c;
}
-MRB_API void
-mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+MRB_API inline void
+include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search_super)
{
struct RClass *ins_pos;
@@ -782,6 +784,7 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
}
while (p) {
if (c != p && p->tt == MRB_TT_CLASS) {
+ if (!search_super) break;
superclass_seen = 1;
}
else if (p->mt == m->mt) {
@@ -810,6 +813,63 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
}
}
+MRB_API void
+mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+{
+ include_module_at(mrb, c, m, FALSE);
+}
+
+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);
+ //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */
+ origin->super = c->super;
+ c->super = origin;
+ c->origin = origin;
+ origin->mt = c->mt;
+ c->mt = kh_init(mt, mrb);
+ }
+ include_module_at(mrb, c, m, FALSE); // changed =
+ if (changed) {
+ //rb_vm_check_redefinition_by_prepend(klass);
+ }
+}
+
+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
mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
{
@@ -2090,6 +2150,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 */
diff --git a/test/t/module.rb b/test/t/module.rb
index ecb969475..9faaf6e2c 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -474,6 +474,39 @@ end
# Not ISO specified
+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 C0
+ include M0
+ prepend M1
+ def m1; [:C0, super, :C0] end
+ end
+ class C1 < C0
+ prepend M2, M3
+ include M4
+ def m1; [:C1, super, :C1] end
+ end
+
+ obj = C1.new
+ expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2]
+ assert_equal(expected, obj.m1)
+end
+
assert('Module#to_s') do
module Test4to_sModules
end