diff --git a/js.h b/js.h index a5f60bc..2756f4b 100644 --- a/js.h +++ b/js.h @@ -46,7 +46,7 @@ int js_loadfile(js_State *J, const char *filename); int js_dostring(js_State *J, const char *source); int js_dofile(js_State *J, const char *filename); -void js_gc(js_State *J); +void js_gc(js_State *J, int report); /* binding API: TODO: move from jsrun.h */ diff --git a/jsbuiltin.c b/jsbuiltin.c index 4a44bbe..6e5e20b 100644 --- a/jsbuiltin.c +++ b/jsbuiltin.c @@ -15,6 +15,13 @@ static int jsB_print(js_State *J, int argc) return 0; } +static int jsB_collectGarbage(js_State *J, int argc) +{ + int report = js_toboolean(J, 1); + js_gc(J, report); + return 0; +} + static int jsB_eval(js_State *J, int argc) { const char *s; @@ -167,5 +174,6 @@ void jsB_init(js_State *J) jsB_register(J, "isNaN", jsB_isNaN); jsB_register(J, "isFinite", jsB_isFinite); + jsB_register(J, "collectGarbage", jsB_collectGarbage); jsB_register(J, "print", jsB_print); } diff --git a/jsgc.c b/jsgc.c index d8356e9..bcfa732 100644 --- a/jsgc.c +++ b/jsgc.c @@ -4,6 +4,13 @@ #include "jsobject.h" #include "jsstate.h" +static void jsG_markobject(js_State *J, int mark, js_Object *obj); + +static void jsG_freeenvironment(js_State *J, js_Environment *env) +{ + free(env); +} + static void jsG_freefunction(js_State *J, js_Function *fun) { free(fun->params); @@ -14,11 +21,6 @@ static void jsG_freefunction(js_State *J, js_Function *fun) free(fun); } -static void jsG_freeenvironment(js_State *J, js_Environment *env) -{ - free(env); -} - static void jsG_freeproperty(js_State *J, js_Property *node) { if (node->left->level) jsG_freeproperty(J, node->left); @@ -33,8 +35,114 @@ static void jsG_freeobject(js_State *J, js_Object *obj) free(obj); } -void js_gc(js_State *J) +static void jsG_markfunction(js_State *J, int mark, js_Function *fun) { + int i; + fun->gcmark = mark; + for (i = 0; i < fun->funlen; i++) + if (fun->funtab[i]->gcmark != mark) + jsG_markfunction(J, mark, fun->funtab[i]); +} + +static void jsG_markenvironment(js_State *J, int mark, js_Environment *env) +{ + do { + env->gcmark = mark; + if (env->variables->gcmark != mark) + jsG_markobject(J, mark, env->variables); + env = env->outer; + } while (env && env->gcmark != mark); +} + +static void jsG_markproperty(js_State *J, int mark, js_Property *node) +{ + if (node->left->level) jsG_markproperty(J, mark, node->left); + if (node->right->level) jsG_markproperty(J, mark, node->right); + if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark) + jsG_markobject(J, mark, node->value.u.object); +} + +static void jsG_markobject(js_State *J, int mark, js_Object *obj) +{ + obj->gcmark = mark; + if (obj->properties->level) + jsG_markproperty(J, mark, obj->properties); + if (obj->prototype && obj->prototype->gcmark != mark) + jsG_markobject(J, mark, obj->prototype); + if (obj->scope && obj->scope->gcmark != mark) + jsG_markenvironment(J, mark, obj->scope); + if (obj->function && obj->function->gcmark != mark) + jsG_markfunction(J, mark, obj->function); +} + +static void jsG_markstack(js_State *J, int mark) +{ + js_Value *v = J->stack; + int n = J->top; + while (n--) { + if (v->type == JS_TOBJECT && v->u.object->gcmark != mark) + jsG_markobject(J, mark, v->u.object); + ++v; + } +} + +void js_gc(js_State *J, int report) +{ + js_Function *fun, *nextfun, **prevnextfun; + js_Object *obj, *nextobj, **prevnextobj; + js_Environment *env, *nextenv, **prevnextenv; + int nenv = 0, nfun = 0, nobj = 0; + int genv = 0, gfun = 0, gobj = 0; + int mark; + + mark = J->gcmark = J->gcmark == 1 ? 2 : 1; + + jsG_markstack(J, mark); + if (J->E) + jsG_markenvironment(J, mark, J->E); + + prevnextenv = &J->gcenv; + for (env = J->gcenv; env; env = nextenv) { + nextenv = env->gcnext; + if (env->gcmark != mark) { + *prevnextenv = nextenv; + jsG_freeenvironment(J, env); + ++genv; + } else { + prevnextenv = &env->gcnext; + } + ++nenv; + } + + prevnextfun = &J->gcfun; + for (fun = J->gcfun; fun; fun = nextfun) { + nextfun = fun->gcnext; + if (fun->gcmark != mark) { + *prevnextfun = nextfun; + jsG_freefunction(J, fun); + ++gfun; + } else { + prevnextfun = &fun->gcnext; + } + ++nfun; + } + + prevnextobj = &J->gcobj; + for (obj = J->gcobj; obj; obj = nextobj) { + nextobj = obj->gcnext; + if (obj->gcmark != mark) { + *prevnextobj = nextobj; + jsG_freeobject(J, obj); + ++gobj; + } else { + prevnextobj = &obj->gcnext; + } + ++nobj; + } + + if (report) + printf("garbage collected: %d/%d envs, %d/%d funs, %d/%d objs\n", + genv, nenv, gfun, nfun, gobj, nobj); } void js_close(js_State *J) diff --git a/jsstate.c b/jsstate.c index cfc2981..7aedc4a 100644 --- a/jsstate.c +++ b/jsstate.c @@ -79,6 +79,8 @@ js_State *js_newstate(void) js_State *J = malloc(sizeof *J); memset(J, 0, sizeof(*J)); + J->gcmark = 1; + J->G = jsR_newobject(J, JS_COBJECT, NULL); J->E = jsR_newenvironment(J, J->G, NULL); diff --git a/main.c b/main.c index 20f623f..cbbecf8 100644 --- a/main.c +++ b/main.c @@ -8,8 +8,10 @@ main(int argc, char **argv) J = js_newstate(); - for (i = 1; i < argc; ++i) + for (i = 1; i < argc; ++i) { js_dofile(J, argv[i]); + js_gc(J, 1); + } js_close(J);