summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2013-05-18 11:19:25 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2013-05-18 11:19:25 +0900
commit834ea241e06d084e666bab1644a61f00492db787 (patch)
tree8f4875cf9c8f78336031c964136fe029f9e93e69
parent36216ac6f7373c879ca103a30b55cab21a339415 (diff)
parent8161f0f6d7f89ad723163943d2d3abc38cd02df5 (diff)
downloadmruby-834ea241e06d084e666bab1644a61f00492db787.tar.gz
mruby-834ea241e06d084e666bab1644a61f00492db787.zip
Merge branch 'master' of https://github.com/Archytaus/mruby into Archytaus-master
-rw-r--r--include/mruby/gc.h16
-rw-r--r--include/mruby/hash.h2
-rw-r--r--mrbgems/default.gembox3
-rw-r--r--mrbgems/mruby-objectspace/mrbgem.rake4
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c117
-rw-r--r--mrbgems/mruby-objectspace/test/objectspace.rb38
-rw-r--r--src/gc.c20
-rw-r--r--src/hash.c4
-rw-r--r--test/assert.rb21
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
diff --git a/src/gc.c b/src/gc.c
index b6493b859..2e5678f7d 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -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()