diff mupdf-source/thirdparty/mujs/jsgc.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/mujs/jsgc.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,290 @@
+#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);
+}