1 #include "jsi.h"
2 #include "jscompile.h"
3 #include "jsvalue.h"
4 #include "jsrun.h"
5 
6 #include "regexp.h"
7 
jsG_freeenvironment(js_State * J,js_Environment * env)8 static void jsG_freeenvironment(js_State *J, js_Environment *env)
9 {
10 	js_free(J, env);
11 }
12 
jsG_freefunction(js_State * J,js_Function * fun)13 static void jsG_freefunction(js_State *J, js_Function *fun)
14 {
15 	js_free(J, fun->funtab);
16 	js_free(J, fun->vartab);
17 	js_free(J, fun->code);
18 	js_free(J, fun);
19 }
20 
jsG_freeproperty(js_State * J,js_Property * node)21 static void jsG_freeproperty(js_State *J, js_Property *node)
22 {
23 	if (node->left->level) jsG_freeproperty(J, node->left);
24 	if (node->right->level) jsG_freeproperty(J, node->right);
25 	js_free(J, node);
26 }
27 
jsG_freeiterator(js_State * J,js_Iterator * node)28 static void jsG_freeiterator(js_State *J, js_Iterator *node)
29 {
30 	while (node) {
31 		js_Iterator *next = node->next;
32 		js_free(J, node);
33 		node = next;
34 	}
35 }
36 
jsG_freeobject(js_State * J,js_Object * obj)37 static void jsG_freeobject(js_State *J, js_Object *obj)
38 {
39 	if (obj->properties->level)
40 		jsG_freeproperty(J, obj->properties);
41 	if (obj->type == JS_CREGEXP) {
42 		js_free(J, obj->u.r.source);
43 		js_regfreex(J->alloc, J->actx, obj->u.r.prog);
44 	}
45 	if (obj->type == JS_CITERATOR)
46 		jsG_freeiterator(J, obj->u.iter.head);
47 	if (obj->type == JS_CUSERDATA && obj->u.user.finalize)
48 		obj->u.user.finalize(J, obj->u.user.data);
49 	if (obj->type == JS_CCFUNCTION && obj->u.c.finalize)
50 		obj->u.c.finalize(J, obj->u.c.data);
51 	js_free(J, obj);
52 }
53 
54 /* Mark and add object to scan queue */
jsG_markobject(js_State * J,int mark,js_Object * obj)55 static void jsG_markobject(js_State *J, int mark, js_Object *obj)
56 {
57 	obj->gcmark = mark;
58 	obj->gcroot = J->gcroot;
59 	J->gcroot = obj;
60 }
61 
jsG_markfunction(js_State * J,int mark,js_Function * fun)62 static void jsG_markfunction(js_State *J, int mark, js_Function *fun)
63 {
64 	int i;
65 	fun->gcmark = mark;
66 	for (i = 0; i < fun->funlen; ++i)
67 		if (fun->funtab[i]->gcmark != mark)
68 			jsG_markfunction(J, mark, fun->funtab[i]);
69 }
70 
jsG_markenvironment(js_State * J,int mark,js_Environment * env)71 static void jsG_markenvironment(js_State *J, int mark, js_Environment *env)
72 {
73 	do {
74 		env->gcmark = mark;
75 		if (env->variables->gcmark != mark)
76 			jsG_markobject(J, mark, env->variables);
77 		env = env->outer;
78 	} while (env && env->gcmark != mark);
79 }
80 
jsG_markproperty(js_State * J,int mark,js_Property * node)81 static void jsG_markproperty(js_State *J, int mark, js_Property *node)
82 {
83 	if (node->left->level) jsG_markproperty(J, mark, node->left);
84 	if (node->right->level) jsG_markproperty(J, mark, node->right);
85 
86 	if (node->value.type == JS_TMEMSTR && node->value.u.memstr->gcmark != mark)
87 		node->value.u.memstr->gcmark = mark;
88 	if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark)
89 		jsG_markobject(J, mark, node->value.u.object);
90 	if (node->getter && node->getter->gcmark != mark)
91 		jsG_markobject(J, mark, node->getter);
92 	if (node->setter && node->setter->gcmark != mark)
93 		jsG_markobject(J, mark, node->setter);
94 }
95 
96 /* Mark everything the object can reach. */
jsG_scanobject(js_State * J,int mark,js_Object * obj)97 static void jsG_scanobject(js_State *J, int mark, js_Object *obj)
98 {
99 	if (obj->properties->level)
100 		jsG_markproperty(J, mark, obj->properties);
101 	if (obj->prototype && obj->prototype->gcmark != mark)
102 		jsG_markobject(J, mark, obj->prototype);
103 	if (obj->type == JS_CITERATOR && obj->u.iter.target->gcmark != mark) {
104 		jsG_markobject(J, mark, obj->u.iter.target);
105 	}
106 	if (obj->type == JS_CFUNCTION || obj->type == JS_CSCRIPT) {
107 		if (obj->u.f.scope && obj->u.f.scope->gcmark != mark)
108 			jsG_markenvironment(J, mark, obj->u.f.scope);
109 		if (obj->u.f.function && obj->u.f.function->gcmark != mark)
110 			jsG_markfunction(J, mark, obj->u.f.function);
111 	}
112 }
113 
jsG_markstack(js_State * J,int mark)114 static void jsG_markstack(js_State *J, int mark)
115 {
116 	js_Value *v = J->stack;
117 	int n = J->top;
118 	while (n--) {
119 		if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
120 			v->u.memstr->gcmark = mark;
121 		if (v->type == JS_TOBJECT && v->u.object->gcmark != mark)
122 			jsG_markobject(J, mark, v->u.object);
123 		++v;
124 	}
125 }
126 
js_gc(js_State * J,int report)127 void js_gc(js_State *J, int report)
128 {
129 	js_Function *fun, *nextfun, **prevnextfun;
130 	js_Object *obj, *nextobj, **prevnextobj;
131 	js_String *str, *nextstr, **prevnextstr;
132 	js_Environment *env, *nextenv, **prevnextenv;
133 	unsigned int nenv = 0, nfun = 0, nobj = 0, nstr = 0, nprop = 0;
134 	unsigned int genv = 0, gfun = 0, gobj = 0, gstr = 0, gprop = 0;
135 	int mark;
136 	int i;
137 
138 	if (J->gcpause) {
139 		if (report)
140 			js_report(J, "garbage collector is paused");
141 		return;
142 	}
143 
144 	mark = J->gcmark = J->gcmark == 1 ? 2 : 1;
145 
146 	/* Add initial roots. */
147 
148 	jsG_markobject(J, mark, J->Object_prototype);
149 	jsG_markobject(J, mark, J->Array_prototype);
150 	jsG_markobject(J, mark, J->Function_prototype);
151 	jsG_markobject(J, mark, J->Boolean_prototype);
152 	jsG_markobject(J, mark, J->Number_prototype);
153 	jsG_markobject(J, mark, J->String_prototype);
154 	jsG_markobject(J, mark, J->RegExp_prototype);
155 	jsG_markobject(J, mark, J->Date_prototype);
156 
157 	jsG_markobject(J, mark, J->Error_prototype);
158 	jsG_markobject(J, mark, J->EvalError_prototype);
159 	jsG_markobject(J, mark, J->RangeError_prototype);
160 	jsG_markobject(J, mark, J->ReferenceError_prototype);
161 	jsG_markobject(J, mark, J->SyntaxError_prototype);
162 	jsG_markobject(J, mark, J->TypeError_prototype);
163 	jsG_markobject(J, mark, J->URIError_prototype);
164 
165 	jsG_markobject(J, mark, J->R);
166 	jsG_markobject(J, mark, J->G);
167 
168 	jsG_markstack(J, mark);
169 
170 	jsG_markenvironment(J, mark, J->E);
171 	jsG_markenvironment(J, mark, J->GE);
172 	for (i = 0; i < J->envtop; ++i)
173 		jsG_markenvironment(J, mark, J->envstack[i]);
174 
175 	/* Scan objects until none remain. */
176 
177 	while ((obj = J->gcroot) != NULL) {
178 		J->gcroot = obj->gcroot;
179 		obj->gcroot = NULL;
180 		jsG_scanobject(J, mark, obj);
181 	}
182 
183 	/* Free everything not marked. */
184 
185 	prevnextenv = &J->gcenv;
186 	for (env = J->gcenv; env; env = nextenv) {
187 		nextenv = env->gcnext;
188 		if (env->gcmark != mark) {
189 			*prevnextenv = nextenv;
190 			jsG_freeenvironment(J, env);
191 			++genv;
192 		} else {
193 			prevnextenv = &env->gcnext;
194 		}
195 		++nenv;
196 	}
197 
198 	prevnextfun = &J->gcfun;
199 	for (fun = J->gcfun; fun; fun = nextfun) {
200 		nextfun = fun->gcnext;
201 		if (fun->gcmark != mark) {
202 			*prevnextfun = nextfun;
203 			jsG_freefunction(J, fun);
204 			++gfun;
205 		} else {
206 			prevnextfun = &fun->gcnext;
207 		}
208 		++nfun;
209 	}
210 
211 	prevnextobj = &J->gcobj;
212 	for (obj = J->gcobj; obj; obj = nextobj) {
213 		nprop += obj->count;
214 		nextobj = obj->gcnext;
215 		if (obj->gcmark != mark) {
216 			gprop += obj->count;
217 			*prevnextobj = nextobj;
218 			jsG_freeobject(J, obj);
219 			++gobj;
220 		} else {
221 			prevnextobj = &obj->gcnext;
222 		}
223 		++nobj;
224 	}
225 
226 	prevnextstr = &J->gcstr;
227 	for (str = J->gcstr; str; str = nextstr) {
228 		nextstr = str->gcnext;
229 		if (str->gcmark != mark) {
230 			*prevnextstr = nextstr;
231 			js_free(J, str);
232 			++gstr;
233 		} else {
234 			prevnextstr = &str->gcnext;
235 		}
236 		++nstr;
237 	}
238 
239 	unsigned int ntot = nenv + nfun + nobj + nstr + nprop;
240 	unsigned int gtot = genv + gfun + gobj + gstr + gprop;
241 	unsigned int remaining = ntot - gtot;
242 
243 	J->gccounter = remaining;
244 	J->gcthresh = remaining * JS_GCFACTOR;
245 
246 	if (report) {
247 		char buf[256];
248 		snprintf(buf, sizeof buf, "garbage collected (%d%%): %d/%d envs, %d/%d funs, %d/%d objs, %d/%d props, %d/%d strs",
249 			100*gtot/ntot, genv, nenv, gfun, nfun, gobj, nobj, gprop, nprop, gstr, nstr);
250 		js_report(J, buf);
251 	}
252 }
253 
js_freestate(js_State * J)254 void js_freestate(js_State *J)
255 {
256 	js_Function *fun, *nextfun;
257 	js_Object *obj, *nextobj;
258 	js_Environment *env, *nextenv;
259 	js_String *str, *nextstr;
260 
261 	if (!J)
262 		return;
263 
264 	for (env = J->gcenv; env; env = nextenv)
265 		nextenv = env->gcnext, jsG_freeenvironment(J, env);
266 	for (fun = J->gcfun; fun; fun = nextfun)
267 		nextfun = fun->gcnext, jsG_freefunction(J, fun);
268 	for (obj = J->gcobj; obj; obj = nextobj)
269 		nextobj = obj->gcnext, jsG_freeobject(J, obj);
270 	for (str = J->gcstr; str; str = nextstr)
271 		nextstr = str->gcnext, js_free(J, str);
272 
273 	jsS_freestrings(J);
274 
275 	js_free(J, J->lexbuf.text);
276 	J->alloc(J->actx, J->stack, 0);
277 	J->alloc(J->actx, J, 0);
278 }
279