diff options
| -rw-r--r-- | include/mruby/gc.h | 16 | ||||
| -rw-r--r-- | include/mruby/hash.h | 2 | ||||
| -rw-r--r-- | mrbgems/default.gembox | 3 | ||||
| -rw-r--r-- | mrbgems/mruby-objectspace/mrbgem.rake | 4 | ||||
| -rw-r--r-- | mrbgems/mruby-objectspace/src/mruby_objectspace.c | 117 | ||||
| -rw-r--r-- | mrbgems/mruby-objectspace/test/objectspace.rb | 38 | ||||
| -rw-r--r-- | src/gc.c | 20 | ||||
| -rw-r--r-- | src/hash.c | 4 | ||||
| -rw-r--r-- | test/assert.rb | 21 |
9 files changed, 223 insertions, 2 deletions
diff --git a/include/mruby/gc.h b/include/mruby/gc.h new file mode 100644 index 000000000..2f7dc73cf --- /dev/null +++ b/include/mruby/gc.h @@ -0,0 +1,16 @@ +/* +** gc.h - garbage collector for mruby +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRUBY_GC_H +#define MRUBY_GC_H + +#include "mruby.h" +#include "mruby/value.h" + +typedef void (each_object_callback)(mrb_state *mrb, struct RBasic* obj, void *data); +void mrb_objspace_each_objects(mrb_state *mrb, each_object_callback* callback, void *data); + +#endif /* MRUBY_GC_H */ diff --git a/include/mruby/hash.h b/include/mruby/hash.h index 68fbf5a76..c331db157 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -29,6 +29,8 @@ mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_valu mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key); mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); +mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self); +mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); /* RHASH_TBL allocates st_table if not available. */ #define RHASH(obj) ((struct RHash*)((obj).value.p)) diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox index 76a6c11b6..a53b07d8a 100644 --- a/mrbgems/default.gembox +++ b/mrbgems/default.gembox @@ -41,6 +41,9 @@ MRuby::GemBox.new do |conf| # Use Random class conf.gem :core => "mruby-random" + # Use ObjectSpace class + conf.gem :core => "mruby-objectspace" + # Generate mirb command conf.gem :core => "mruby-bin-mirb" diff --git a/mrbgems/mruby-objectspace/mrbgem.rake b/mrbgems/mruby-objectspace/mrbgem.rake new file mode 100644 index 000000000..100df4cdc --- /dev/null +++ b/mrbgems/mruby-objectspace/mrbgem.rake @@ -0,0 +1,4 @@ +MRuby::Gem::Specification.new('mruby-objectspace') do |spec| + spec.license = 'MIT' + spec.authors = 'mruby developers' +end diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c new file mode 100644 index 000000000..21e623823 --- /dev/null +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -0,0 +1,117 @@ +#include <mruby.h> +#include <mruby/gc.h> +#include <mruby/hash.h> +#include <mruby/value.h> + +struct os_count_struct { + size_t total; + size_t freed; + size_t counts[MRB_TT_MAXDEFINE+1]; +}; + +void +os_count_object_type(mrb_state *mrb, struct RBasic* obj, void *data) +{ + struct os_count_struct* obj_count; + obj_count = (struct os_count_struct*)(data); + + if (is_dead(mrb, obj)) { + obj_count->freed++; + } else { + obj_count->counts[obj->tt]++; + obj_count->total++; + } +} + +/* + * call-seq: + * ObjectSpace.count_objects([result_hash]) -> hash + * + * Counts objects for each type. + * + * It returns a hash, such as: + * { + * :TOTAL=>10000, + * :FREE=>3011, + * :MRB_TT_OBJECT=>6, + * :MRB_TT_CLASS=>404, + * # ... + * } + * + * If the optional argument +result_hash+ is given, + * it is overwritten and returned. This is intended to avoid probe effect. + * + */ + +mrb_value +os_count_objects(mrb_state *mrb, mrb_value self) +{ + struct os_count_struct obj_count; + size_t i; + mrb_value hash; + struct heap_page* page = mrb->heaps; + + if (mrb_get_args(mrb, "|H", &hash) == 0) { + hash = mrb_hash_new(mrb); + } + + if (!mrb_test(mrb_hash_empty_p(mrb, hash))) { + mrb_hash_clear(mrb, hash); + } + + for (i = 0; i <= MRB_TT_MAXDEFINE; i++) { + obj_count.counts[i] = 0; + } + obj_count.total = 0; + obj_count.freed = 0; + + mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count); + + mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_cstr(mrb, "TOTAL")), mrb_fixnum_value(obj_count.total)); + mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_cstr(mrb, "FREE")), mrb_fixnum_value(obj_count.freed)); + + for (i = 0; i < MRB_TT_MAXDEFINE; i++) { + mrb_value type; + switch (i) { +#define COUNT_TYPE(t) case (t): type = mrb_symbol_value(mrb_intern_cstr(mrb, #t)); break; + COUNT_TYPE(MRB_TT_FALSE); + COUNT_TYPE(MRB_TT_FREE); + COUNT_TYPE(MRB_TT_TRUE); + COUNT_TYPE(MRB_TT_FIXNUM); + COUNT_TYPE(MRB_TT_SYMBOL); + COUNT_TYPE(MRB_TT_UNDEF); + COUNT_TYPE(MRB_TT_FLOAT); + COUNT_TYPE(MRB_TT_VOIDP); + COUNT_TYPE(MRB_TT_OBJECT); + COUNT_TYPE(MRB_TT_CLASS); + COUNT_TYPE(MRB_TT_MODULE); + COUNT_TYPE(MRB_TT_ICLASS); + COUNT_TYPE(MRB_TT_SCLASS); + COUNT_TYPE(MRB_TT_PROC); + COUNT_TYPE(MRB_TT_ARRAY); + COUNT_TYPE(MRB_TT_HASH); + COUNT_TYPE(MRB_TT_STRING); + COUNT_TYPE(MRB_TT_RANGE); + COUNT_TYPE(MRB_TT_EXCEPTION); + COUNT_TYPE(MRB_TT_FILE); + COUNT_TYPE(MRB_TT_ENV); + COUNT_TYPE(MRB_TT_DATA); +#undef COUNT_TYPE + default: type = mrb_fixnum_value(i); break; + } + if (obj_count.counts[i]) + mrb_hash_set(mrb, hash, type, mrb_fixnum_value(obj_count.counts[i])); + } + + return hash; +} + +void +mrb_mruby_objectspace_gem_init(mrb_state* mrb) { + struct RClass *os = mrb_define_module(mrb, "ObjectSpace"); + mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_ANY()); +} + +void +mrb_mruby_objectspace_gem_final(mrb_state* mrb) { +} diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb new file mode 100644 index 000000000..37137eb04 --- /dev/null +++ b/mrbgems/mruby-objectspace/test/objectspace.rb @@ -0,0 +1,38 @@ +assert('ObjectSpace.count_objects') do + h = {} + ObjectSpace.count_objects(h) + assert_kind_of(Hash, h) + assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) + assert_true(h.values.all? {|x| x.is_a?(Integer) }) + + assert_true(h.has_key?(:TOTAL)) + assert_true(h.has_key?(:FREE)) + + h = ObjectSpace.count_objects + assert_kind_of(Hash, h) + assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) + assert_true(h.values.all? {|x| x.is_a?(Integer) }) + + assert_raise(TypeError) { ObjectSpace.count_objects(1) } + + h0 = {:MRB_TT_FOO=>1000} + h = ObjectSpace.count_objects(h0) + assert_false(h0.has_key?(:MRB_TT_FOO)) + + GC.start + h_after = {} + h_before = ObjectSpace.count_objects + + objs = [] + 1000.times do + objs << {} + end + objs = nil + ObjectSpace.count_objects(h) + GC.start + ObjectSpace.count_objects(h_after) + + assert_equal(h_before[:MRB_TT_HASH] + 1000, h[:MRB_TT_HASH]) + assert_equal(h_before[:MRB_TT_HASH], h_after[:MRB_TT_HASH]) + +end
\ No newline at end of file @@ -20,6 +20,7 @@ #include "mruby/range.h" #include "mruby/string.h" #include "mruby/variable.h" +#include "mruby/gc.h" /* = Tri-color Incremental Garbage Collection @@ -1141,6 +1142,24 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self) return mrb_bool_value(enable); } +void +mrb_objspace_each_objects(mrb_state *mrb, each_object_callback* callback, void *data) +{ + struct heap_page* page = mrb->heaps; + + while (page != NULL) { + RVALUE *p, *pend; + + p = page->objects; + pend = p + MRB_HEAP_PAGE_SIZE; + for (;p < pend; p++) { + (*callback)(mrb, &p->as.basic, data); + } + + page = page->next; + } +} + #ifdef GC_TEST #ifdef GC_DEBUG static mrb_value gc_test(mrb_state *, mrb_value); @@ -1151,6 +1170,7 @@ void mrb_init_gc(mrb_state *mrb) { struct RClass *gc; + gc = mrb_define_module(mrb, "GC"); mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE()); diff --git a/src/hash.c b/src/hash.c index 882dd3121..5d78a6ea7 100644 --- a/src/hash.c +++ b/src/hash.c @@ -651,7 +651,7 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash) * */ -static mrb_value +mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); @@ -768,7 +768,7 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self) * {}.empty? #=> true * */ -static mrb_value +mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { khash_t(ht) *h = RHASH_TBL(self); diff --git a/test/assert.rb b/test/assert.rb index a69066828..cb2e28b89 100644 --- a/test/assert.rb +++ b/test/assert.rb @@ -86,6 +86,19 @@ def assert_true(ret, msg = nil, diff = nil) ret end +def assert_false(ret, msg = nil, diff = nil) + if $mrbtest_assert + $mrbtest_assert_idx += 1 + if ret + msg = "Expected #{ret.inspect} to be false" unless msg + diff = assertion_diff(false, ret) unless diff + + $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) + end + end + !ret +end + def assert_equal(exp, act, msg = nil) msg = "Expected to be equal" unless msg diff = assertion_diff(exp, act) @@ -136,6 +149,14 @@ def assert_raise(*exp) end ## +# Fails unless +obj+ is a kind of +cls+. +def assert_kind_of(cls, obj, msg = nil) + msg = "Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}" unless msg + diff = assertion_diff(cls, obj.class) + assert_true(obj.kind_of?(cls), msg, diff) +end + +## # Report the test result and print all assertions # which were reported broken. def report() |
