ref: 704d16409fcf5fde1724131fdffb85cb35331206
parent: 96ca91ec7c4ff8fc1705e48d5b67e9e0570f5fde
author: Tor Andersson <[email protected]>
date: Fri Jan 17 19:02:04 EST 2014
Collect garbage.
--- a/js.h
+++ b/js.h
@@ -46,7 +46,7 @@
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 */
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -15,6 +15,13 @@
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 @@
jsB_register(J, "isNaN", jsB_isNaN);
jsB_register(J, "isFinite", jsB_isFinite);
+ jsB_register(J, "collectGarbage", jsB_collectGarbage);
jsB_register(J, "print", jsB_print);
}
--- 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 @@
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 @@
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)
--- a/jsstate.c
+++ b/jsstate.c
@@ -79,6 +79,8 @@
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);
--- a/main.c
+++ b/main.c
@@ -8,8 +8,10 @@
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);