Coverage Report

Created: 2025-09-04 06:50

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