From ef79101296b8c178e3110f2a792d2cb99a6824d3 Mon Sep 17 00:00:00 2001 From: paladin-t Date: Fri, 11 Dec 2015 17:25:21 +0800 Subject: [PATCH] *polished sample script; *fixed a memory leak with gc caused by reference cycle; *finished internal class development. --- HISTORY | 2 + core/my_basic.c | 193 ++++++++++++++++++++++++++++++++++++-------- sample/sample01.bas | 2 +- sample/sample02.bas | 20 ++--- sample/sample03.bas | 16 ++-- sample/sample04.bas | 30 +++---- sample/sample05.bas | 12 +-- 7 files changed, 203 insertions(+), 72 deletions(-) diff --git a/HISTORY b/HISTORY index a018265..b876423 100755 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,8 @@ Dec. 11 2015 Added an IS statement to detect type equality of a value Added support to store a routine in a variable by CALL statement +Fixed a memory leak with GC caused by reference cycle +Finished internal class development Dec. 10 2015 Developing class, added a NEW statement to duplicate a class instance diff --git a/core/my_basic.c b/core/my_basic.c index 70e9984..0a2e5ff 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -82,7 +82,7 @@ extern "C" { /** Macros */ #define _VER_MAJOR 1 #define _VER_MINOR 1 -#define _VER_REVISION 105 +#define _VER_REVISION 106 #define _VER_SUFFIX #define _MB_VERSION ((_VER_MAJOR * 0x01000000) + (_VER_MINOR * 0x00010000) + (_VER_REVISION)) #define _STRINGIZE(A) _MAKE_STRINGIZE(A) @@ -312,6 +312,8 @@ typedef short _lock_t; #ifdef MB_ENABLE_GC typedef struct _gc_t { _ht_node_t* table; + _ht_node_t* recursive_table; + _ht_node_t* collected_table; int_t collecting; } _gc_t; #endif /* MB_ENABLE_GC */ @@ -1072,6 +1074,10 @@ static char* _extract_string(_object_t* obj); case _DT_USERTYPE_REF: \ _unref(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref); \ break; +#define _ADDGC_USERTYPE_REF(__o, __g) \ + case _DT_USERTYPE_REF: \ + _gc_add(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref, (__g)); \ + break; #define _REF_ARRAY(__o) \ case _DT_ARRAY: \ if(!(__o)->ref) \ @@ -1082,6 +1088,11 @@ static char* _extract_string(_object_t* obj); if(!(__o)->ref) \ _unref(&(__o)->data.array->ref, (__o)->data.array); \ break; +#define _ADDGC_ARRAY(__o, __g) \ + case _DT_ARRAY: \ + if(!(__o)->ref) \ + _gc_add(&(__o)->data.array->ref, (__o)->data.array, (__g)); \ + break; #ifdef MB_ENABLE_COLLECTION_LIB # define _REF_COLL(__o) \ case _DT_LIST: \ @@ -1097,6 +1108,13 @@ static char* _extract_string(_object_t* obj); case _DT_DICT: \ _unref(&(__o)->data.dict->ref, (__o)->data.dict); \ break; +# define _ADDGC_COLL(__o, __g) \ + case _DT_LIST: \ + _gc_add(&(__o)->data.list->ref, (__o)->data.list, (__g)); \ + break; \ + case _DT_DICT: \ + _gc_add(&(__o)->data.dict->ref, (__o)->data.dict, (__g)); \ + break; #else /* MB_ENABLE_COLLECTION_LIB */ # define _REF_COLL(__o) ((void)(__o)); # define _UNREF_COLL(__o) ((void)(__o)); @@ -1111,9 +1129,15 @@ static char* _extract_string(_object_t* obj); if(!(__o)->ref) \ _unref(&(__o)->data.instance->ref, (__o)->data.instance); \ break; +# define _ADDGC_CLASS(__o, __g) \ + case _DT_CLASS: \ + if(!(__o)->ref) \ + _gc_add(&(__o)->data.instance->ref, (__o)->data.instance, (__g)); \ + break; #else /* MB_ENABLE_CLASS */ # define _REF_CLASS(__o) ((void)(__o)); # define _UNREF_CLASS(__o) ((void)(__o)); +# define _ADDGC_CLASS(__o, __g) ((void)(__o)); ((void)(__g)); #endif /* MB_ENABLE_CLASS */ #define _REF(__o) \ switch((__o)->type) { \ @@ -1131,6 +1155,14 @@ static char* _extract_string(_object_t* obj); _UNREF_CLASS(__o) \ default: break; \ } +#define _ADDGC(__o, __g) \ + switch((__o)->type) { \ + _ADDGC_USERTYPE_REF(__o, __g) \ + _ADDGC_ARRAY(__o, __g) \ + _ADDGC_COLL(__o, __g) \ + _ADDGC_CLASS(__o, __g) \ + default: break; \ + } static bool_t _lock_ref_object(_lock_t* lk, _ref_t* ref, void* obj); static bool_t _unlock_ref_object(_lock_t* lk, _ref_t* ref, void* obj); @@ -1142,10 +1174,12 @@ static void _create_ref(_ref_t* ref, _unref_func_t dtor, _data_e t, mb_interpret static void _destroy_ref(_ref_t* ref); #ifdef MB_ENABLE_GC -static void _gc_add(_ref_t* ref, void* data); +static void _gc_add(_ref_t* ref, void* data, _gc_t* gc); static void _gc_remove(_ref_t* ref, void* data); static int _gc_add_reachable(void* data, void* extra, void* ht); static void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht); +static int _gc_destroy_garbage_in_list(void* data, void* extra, _gc_t* gc); +static int _gc_destroy_garbage_in_dict(void* data, void* extra, _gc_t* gc); static int _gc_destroy_garbage(void* data, void* extra); static void _gc_try_trigger(mb_interpreter_t* s); static void _gc_collect_garbage(mb_interpreter_t* s); @@ -1265,6 +1299,7 @@ static void _swap_public_value(mb_value_t* tgt, mb_value_t* src); static int _clear_scope_chain(mb_interpreter_t* s); static int _dispose_scope_chain(mb_interpreter_t* s); static void _tidy_scope_chain(mb_interpreter_t* s); +static void _tidy_intermediate_value(_ref_t* ref, void* data); static void _stepped(mb_interpreter_t* s, _ls_node_t* ast); static int _execute_statement(mb_interpreter_t* s, _ls_node_t** l); @@ -4115,7 +4150,9 @@ unsigned _unref(_ref_t* ref, void* data) { result = --(*(ref->count)); #ifdef MB_ENABLE_GC - _gc_add(ref, data); + _gc_add(ref, data, 0); + if(ref->count && !(*ref->count)) + _tidy_intermediate_value(ref, data); ref->on_unref(ref, data); if(!ref->count) _gc_remove(ref, data); @@ -4148,23 +4185,33 @@ void _destroy_ref(_ref_t* ref) { } #ifdef MB_ENABLE_GC -void _gc_add(_ref_t* ref, void* data) { +void _gc_add(_ref_t* ref, void* data, _gc_t* gc) { /* Add a referenced object to GC */ + _ht_node_t* table = 0; + mb_assert(ref && data); + if(gc && _ht_find(gc->collected_table, ref)) + return; + if(ref->type == _DT_ARRAY) return; if(!ref->s->gc.table) return; - if(ref->s->gc.collecting) + if(ref->s->gc.collecting > 1) return; - if(ref->count && *ref->count) - _ht_set_or_insert(ref->s->gc.table, ref, data); + if(ref->s->gc.collecting) + table = ref->s->gc.recursive_table; else - _ht_remove(ref->s->gc.table, ref, 0); + table = ref->s->gc.table; + + if(ref->count && *ref->count) + _ht_set_or_insert(table, ref, data); + else + _ht_remove(table, ref, 0); } void _gc_remove(_ref_t* ref, void* data) { @@ -4251,27 +4298,9 @@ void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht) { /* Get all reachable referenced objects */ _running_context_t* running = 0; _ht_node_t* global_scope = 0; - _object_t tmp; mb_assert(s && ht); - _MAKE_NIL(&tmp); - _public_value_to_internal_object(&s->running_context->intermediate_value, &tmp); - switch(tmp.type) { - case _DT_ARRAY: /* Fall through */ -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: /* Fall through */ - case _DT_DICT: /* Fall through */ -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: /* Fall through */ -#endif /* MB_ENABLE_CLASS */ - case _DT_USERTYPE_REF: - _gc_add_reachable(&tmp, 0, ht); - - break; - } - running = s->running_context; while(running) { global_scope = running->var_dict; @@ -4283,10 +4312,50 @@ void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht) { } } +int _gc_destroy_garbage_in_list(void* data, void* extra, _gc_t* gc) { + /* Destroy only the capsule (wrapper) of an object, leave the data behind, and add it to GC if possible */ + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + _ADDGC(obj, gc); + safe_free(obj); + + result = _OP_RESULT_DEL_NODE; + + return result; +} + +int _gc_destroy_garbage_in_dict(void* data, void* extra, _gc_t* gc) { + /* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + _ADDGC(obj, gc); + safe_free(obj); + + obj = (_object_t*)extra; + _ADDGC(obj, gc); + safe_free(obj); + + result = _OP_RESULT_DEL_NODE; + + return result; +} + int _gc_destroy_garbage(void* data, void* extra) { /* Destroy a garbage */ int result = _OP_RESULT_NORMAL; + _gc_t* gc = 0; _ref_t* ref = 0; + bool_t cld = false; #ifdef MB_ENABLE_COLLECTION_LIB _list_t* lst = 0; _dict_t* dct = 0; @@ -4295,18 +4364,27 @@ int _gc_destroy_garbage(void* data, void* extra) { mb_assert(data && extra); ref = (_ref_t*)extra; + gc = &ref->s->gc; switch(ref->type) { #ifdef MB_ENABLE_COLLECTION_LIB case _DT_LIST: lst = (_list_t*)data; - _ls_foreach(lst->list, _destroy_object_capsule_only); + if(gc->collecting <= 1 && !_ht_find(gc->recursive_table, ref)) { + _LS_FOREACH(lst->list, _do_nothing_on_object, _gc_destroy_garbage_in_list, gc); + } else { + _ls_foreach(lst->list, _destroy_object_capsule_only); + } _ls_clear(lst->list); lst->count = 0; break; case _DT_DICT: dct = (_dict_t*)data; - _ht_foreach(dct->dict, _destroy_object_capsule_only_with_extra); + if(gc->collecting <= 1 && !_ht_find(gc->recursive_table, ref)) { + _HT_FOREACH(dct->dict, _do_nothing_on_object, _gc_destroy_garbage_in_dict, gc); + } else { + _ht_foreach(dct->dict, _destroy_object_capsule_only_with_extra); + } _ht_clear(dct->dict); break; @@ -4314,7 +4392,12 @@ int _gc_destroy_garbage(void* data, void* extra) { default: break; } - _unref(ref, data); + if(ref->count) { + cld = (*(ref->count)) == 1; + _unref(ref, data); + if(cld) + _ht_set_or_insert(gc->collected_table, ref, data); + } result = _OP_RESULT_DEL_NODE; @@ -4345,8 +4428,14 @@ void _gc_collect_garbage(mb_interpreter_t* s) { _HT_FOREACH(valid, _do_nothing_on_object, _ht_remove_exist, s->gc.table); /* Collect garbage */ _ht_foreach(s->gc.table, _gc_destroy_garbage); - /* Tidy */ _ht_clear(s->gc.table); + if(s->gc.recursive_table->count) { + s->gc.collecting++; + _ht_foreach(s->gc.recursive_table, _gc_destroy_garbage); + _ht_clear(s->gc.recursive_table); + s->gc.collecting--; + } + /* Tidy */ _ht_clear(valid); _ht_destroy(valid); s->gc.collecting--; @@ -6629,6 +6718,7 @@ int _dispose_scope_chain(mb_interpreter_t* s) { result++; running = prev; } + s->running_context = 0; return result; } @@ -6654,6 +6744,35 @@ void _tidy_scope_chain(mb_interpreter_t* s) { #endif /* MB_ENABLE_CLASS */ } +void _tidy_intermediate_value(_ref_t* ref, void* data) { + /* Tidy the intermediate value */ + _object_t tmp; + + mb_assert(ref && data); + + if(!ref->s->running_context) + return; + + _MAKE_NIL(&tmp); + _public_value_to_internal_object(&ref->s->running_context->intermediate_value, &tmp); + if(tmp.data.usertype == data) { + switch(tmp.type) { + case _DT_ARRAY: /* Fall through */ +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: /* Fall through */ + case _DT_DICT: /* Fall through */ +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: /* Fall through */ +#endif /* MB_ENABLE_CLASS */ + case _DT_USERTYPE_REF: + mb_make_nil(ref->s->running_context->intermediate_value); + + break; + } + } +} + void _stepped(mb_interpreter_t* s, _ls_node_t* ast) { /* Called each step */ _object_t* obj = 0; @@ -7405,6 +7524,8 @@ int mb_open(struct mb_interpreter_t** s) { #ifdef MB_ENABLE_GC (*s)->gc.table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); + (*s)->gc.recursive_table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); + (*s)->gc.collected_table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); #endif /* MB_ENABLE_GC */ (*s)->sub_stack = _ls_create(); @@ -7446,13 +7567,21 @@ int mb_close(struct mb_interpreter_t** s) { #ifdef MB_ENABLE_GC _gc_collect_garbage(*s); - _ht_destroy((*s)->gc.table); - (*s)->gc.table = 0; #endif /* MB_ENABLE_GC */ _tidy_scope_chain(*s); _dispose_scope_chain(*s); +#ifdef MB_ENABLE_GC + _gc_collect_garbage(*s); + _ht_destroy((*s)->gc.table); + _ht_destroy((*s)->gc.recursive_table); + _ht_destroy((*s)->gc.collected_table); + (*s)->gc.table = 0; + (*s)->gc.recursive_table = 0; + (*s)->gc.collected_table = 0; +#endif /* MB_ENABLE_GC */ + _ls_foreach((*s)->temp_values, _destroy_object); _ls_destroy((*s)->temp_values); _ls_foreach((*s)->lazy_destroy_objects, _destroy_object); diff --git a/sample/sample01.bas b/sample/sample01.bas index d605a41..d135c23 100755 --- a/sample/sample01.bas +++ b/sample/sample01.bas @@ -4,4 +4,4 @@ s$ = "hello " s$ = s$ + "world" -PRINT s$ + "!" \ No newline at end of file +print s$ + "!" \ No newline at end of file diff --git a/sample/sample02.bas b/sample/sample02.bas index 09a0a9b..7c4e638 100755 --- a/sample/sample02.bas +++ b/sample/sample02.bas @@ -4,14 +4,14 @@ e = 50 -PRINT "Primes in ", e, ": ", 2, ", " +print "Primes in ", e, ": ", 2, ", " -FOR n = 3 TO e - m = 2 - is = 1 - WHILE m < n - IF n MOD m = 0 THEN is = 0 ELSE m = m + 1 - IF is = 0 THEN EXIT - WEND - IF is = 1 THEN PRINT n, ", " -NEXT n \ No newline at end of file +for n = 3 to e + m = 2 + isp = 1 + while m < n + if n mod m = 0 then isp = 0 else m = m + 1 + if isp = 0 then exit + wend + if isp = 1 then print n, ", " +next n \ No newline at end of file diff --git a/sample/sample03.bas b/sample/sample03.bas index 545c0b5..781808e 100755 --- a/sample/sample03.bas +++ b/sample/sample03.bas @@ -2,21 +2,21 @@ ' Copyright (c) 2011 - 2015 Wang Renxin. All rights reserved. ' For more information, see https://github.com/paladin-t/my_basic/ -PRINT "Input: " -INPUT ns$ -n = VAL(ns$) +print "Input: " +input ns$ +n = val(ns$) x = n * 2 - 1 a = 1 - 1 / 3 w = (x - 5) / 2 + 1 v = 100 / w -FOR y = 5 TO x STEP 2 +for y = 5 to x step 2 iy = (y - 1) / 4 - iy = FLOOR(iy) - IF iy = (y - 1) / 4 THEN a = a + 1 / y ELSE a = a - 1 / y + iy = floor(iy) + if iy = (y - 1) / 4 then a = a + 1 / y else a = a - 1 / y u = u + v -NEXT +next a = a * 4 -PRINT "PI = ", a \ No newline at end of file +print "Pi = ", a \ No newline at end of file diff --git a/sample/sample04.bas b/sample/sample04.bas index 7c9be5d..c62ec06 100755 --- a/sample/sample04.bas +++ b/sample/sample04.bas @@ -2,22 +2,22 @@ ' Copyright (c) 2011 - 2015 Wang Renxin. All rights reserved. ' For more information, see https://github.com/paladin-t/my_basic/ -BEGIN: +begin: n = 10 - DIM arr(n) - GOSUB CALC - GOSUB SHOW -END + dim arr(n) + gosub calc + gosub show +end -CALC: +calc: arr(0) = 1 - FOR i = 1 TO n - 1 - IF i = 1 THEN arr(i) = 1 ELSE arr(i) = arr(i - 1) + arr(i - 2) - NEXT -RETURN + for i = 1 to n - 1 + if i = 1 then arr(i) = 1 else arr(i) = arr(i - 1) + arr(i - 2) + next +return -SHOW: - FOR i = 0 TO n - 1 - PRINT arr(i), ", " - NEXT -RETURN \ No newline at end of file +show: + for i = 0 to n - 1 + print arr(i), ", " + next +return \ No newline at end of file diff --git a/sample/sample05.bas b/sample/sample05.bas index d06928d..96bd5d9 100755 --- a/sample/sample05.bas +++ b/sample/sample05.bas @@ -2,14 +2,14 @@ ' Copyright (c) 2011 - 2015 Wang Renxin. All rights reserved. ' For more information, see https://github.com/paladin-t/my_basic/ -DEF AREA(a, b) - RETURN CALL MUL(a, b) -ENDDEF +def area(a, b) + return call mul(a, b) +enddef -DEF MUL(a, b) +def mul(a, b) return a * b -ENDDEF +enddef a = 3 b = 4 -PRINT a; b; AREA(a, b); \ No newline at end of file +print a; b; area(a, b); \ No newline at end of file