Commit 834ea241 authored by Yukihiro "Matz" Matsumoto's avatar Yukihiro "Matz" Matsumoto

Merge branch 'master' of https://github.com/Archytaus/mruby into Archytaus-master

parents 36216ac6 8161f0f6
/*
** 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 */
......@@ -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))
......
......@@ -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"
......
MRuby::Gem::Specification.new('mruby-objectspace') do |spec|
spec.license = 'MIT'
spec.authors = 'mruby developers'
end
#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) {
}
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());
......
......@@ -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);
......
......@@ -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)
......@@ -135,6 +148,14 @@ def assert_raise(*exp)
ret
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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment