#include "jsi.h" #include "regexp.h" static void jsG_freeenvironment(js_State *J, js_Environment *env) { js_free(J, env); } static void jsG_freefunction(js_State *J, js_Function *fun) { js_free(J, fun->funtab); js_free(J, fun->vartab); js_free(J, fun->code); js_free(J, fun); } static void jsG_freeproperty(js_State *J, js_Property *node) { if (node->left->level) jsG_freeproperty(J, node->left); if (node->right->level) jsG_freeproperty(J, node->right); js_free(J, node); } static void jsG_freeiterator(js_State *J, js_Iterator *node) { while (node) { js_Iterator *next = node->next; js_free(J, node); node = next; } } static void jsG_freeobject(js_State *J, js_Object *obj) { if (obj->properties->level) jsG_freeproperty(J, obj->properties); if (obj->type == JS_CREGEXP) { js_free(J, obj->u.r.source); js_regfreex(J->alloc, J->actx, obj->u.r.prog); } if (obj->type == JS_CSTRING) { if (obj->u.s.string != obj->u.s.shrstr) js_free(J, obj->u.s.string); } if (obj->type == JS_CARRAY && obj->u.a.simple) js_free(J, obj->u.a.array); if (obj->type == JS_CITERATOR) jsG_freeiterator(J, obj->u.iter.head); if (obj->type == JS_CUSERDATA && obj->u.user.finalize) obj->u.user.finalize(J, obj->u.user.data); if (obj->type == JS_CCFUNCTION && obj->u.c.finalize) obj->u.c.finalize(J, obj->u.c.data); js_free(J, obj); } /* Mark and add object to scan queue */ static void jsG_markobject(js_State *J, int mark, js_Object *obj) { obj->gcmark = mark; obj->gcroot = J->gcroot; J->gcroot = obj; } 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.t.type == JS_TMEMSTR && node->value.u.memstr->gcmark != mark) node->value.u.memstr->gcmark = mark; if (node->value.t.type == JS_TOBJECT && node->value.u.object->gcmark != mark) jsG_markobject(J, mark, node->value.u.object); if (node->getter && node->getter->gcmark != mark) jsG_markobject(J, mark, node->getter); if (node->setter && node->setter->gcmark != mark) jsG_markobject(J, mark, node->setter); } /* Mark everything the object can reach. */ static void jsG_scanobject(js_State *J, int mark, js_Object *obj) { 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->type == JS_CARRAY && obj->u.a.simple) { int i; for (i = 0; i < obj->u.a.flat_length; ++i) { js_Value *v = &obj->u.a.array[i]; if (v->t.type == JS_TMEMSTR && v->u.memstr->gcmark != mark) v->u.memstr->gcmark = mark; if (v->t.type == JS_TOBJECT && v->u.object->gcmark != mark) jsG_markobject(J, mark, v->u.object); } } if (obj->type == JS_CITERATOR && obj->u.iter.target->gcmark != mark) { jsG_markobject(J, mark, obj->u.iter.target); } if (obj->type == JS_CFUNCTION || obj->type == JS_CSCRIPT) { if (obj->u.f.scope && obj->u.f.scope->gcmark != mark) jsG_markenvironment(J, mark, obj->u.f.scope); if (obj->u.f.function && obj->u.f.function->gcmark != mark) jsG_markfunction(J, mark, obj->u.f.function); } } static void jsG_markstack(js_State *J, int mark) { js_Value *v = J->stack; int n = J->top; while (n--) { if (v->t.type == JS_TMEMSTR && v->u.memstr->gcmark != mark) v->u.memstr->gcmark = mark; if (v->t.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_String *str, *nextstr, **prevnextstr; js_Environment *env, *nextenv, **prevnextenv; unsigned int nenv = 0, nfun = 0, nobj = 0, nstr = 0, nprop = 0; unsigned int genv = 0, gfun = 0, gobj = 0, gstr = 0, gprop = 0; int mark; int i; if (J->gcpause) { if (report) js_report(J, "garbage collector is paused"); return; } mark = J->gcmark = J->gcmark == 1 ? 2 : 1; /* Add initial roots. */ jsG_markobject(J, mark, J->Object_prototype); jsG_markobject(J, mark, J->Array_prototype); jsG_markobject(J, mark, J->Function_prototype); jsG_markobject(J, mark, J->Boolean_prototype); jsG_markobject(J, mark, J->Number_prototype); jsG_markobject(J, mark, J->String_prototype); jsG_markobject(J, mark, J->RegExp_prototype); jsG_markobject(J, mark, J->Date_prototype); jsG_markobject(J, mark, J->Error_prototype); jsG_markobject(J, mark, J->EvalError_prototype); jsG_markobject(J, mark, J->RangeError_prototype); jsG_markobject(J, mark, J->ReferenceError_prototype); jsG_markobject(J, mark, J->SyntaxError_prototype); jsG_markobject(J, mark, J->TypeError_prototype); jsG_markobject(J, mark, J->URIError_prototype); jsG_markobject(J, mark, J->R); jsG_markobject(J, mark, J->G); jsG_markstack(J, mark); jsG_markenvironment(J, mark, J->E); jsG_markenvironment(J, mark, J->GE); for (i = 0; i < J->envtop; ++i) jsG_markenvironment(J, mark, J->envstack[i]); /* Scan objects until none remain. */ while ((obj = J->gcroot) != NULL) { J->gcroot = obj->gcroot; obj->gcroot = NULL; jsG_scanobject(J, mark, obj); } /* Free everything not marked. */ 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) { nprop += obj->count; nextobj = obj->gcnext; if (obj->gcmark != mark) { gprop += obj->count; *prevnextobj = nextobj; jsG_freeobject(J, obj); ++gobj; } else { prevnextobj = &obj->gcnext; } ++nobj; } prevnextstr = &J->gcstr; for (str = J->gcstr; str; str = nextstr) { nextstr = str->gcnext; if (str->gcmark != mark) { *prevnextstr = nextstr; js_free(J, str); ++gstr; } else { prevnextstr = &str->gcnext; } ++nstr; } unsigned int ntot = nenv + nfun + nobj + nstr + nprop; unsigned int gtot = genv + gfun + gobj + gstr + gprop; unsigned int remaining = ntot - gtot; J->gccounter = remaining; J->gcthresh = remaining * JS_GCFACTOR; if (report) { char buf[256]; snprintf(buf, sizeof buf, "garbage collected (%d%%): %d/%d envs, %d/%d funs, %d/%d objs, %d/%d props, %d/%d strs", 100*gtot/ntot, genv, nenv, gfun, nfun, gobj, nobj, gprop, nprop, gstr, nstr); js_report(J, buf); } } void js_freestate(js_State *J) { js_Function *fun, *nextfun; js_Object *obj, *nextobj; js_Environment *env, *nextenv; js_String *str, *nextstr; if (!J) return; for (env = J->gcenv; env; env = nextenv) nextenv = env->gcnext, jsG_freeenvironment(J, env); for (fun = J->gcfun; fun; fun = nextfun) nextfun = fun->gcnext, jsG_freefunction(J, fun); for (obj = J->gcobj; obj; obj = nextobj) nextobj = obj->gcnext, jsG_freeobject(J, obj); for (str = J->gcstr; str; str = nextstr) nextstr = str->gcnext, js_free(J, str); jsS_freestrings(J); js_free(J, J->lexbuf.text); J->alloc(J->actx, J->stack, 0); J->alloc(J->actx, J, 0); }