shithub: libmujs

Download patch

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);