comparison mupdf-source/thirdparty/mujs/jsproperty.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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 #include "jsi.h"
2
3 #include <assert.h>
4
5 /*
6 Use an AA-tree to quickly look up properties in objects:
7
8 The level of every leaf node is one.
9 The level of every left child is one less than its parent.
10 The level of every right child is equal or one less than its parent.
11 The level of every right grandchild is less than its grandparent.
12 Every node of level greater than one has two children.
13
14 A link where the child's level is equal to that of its parent is called a horizontal link.
15 Individual right horizontal links are allowed, but consecutive ones are forbidden.
16 Left horizontal links are forbidden.
17
18 skew() fixes left horizontal links.
19 split() fixes consecutive right horizontal links.
20 */
21
22 static js_Property sentinel = {
23 &sentinel, &sentinel,
24 0, 0,
25 { { {0}, JS_TUNDEFINED } },
26 NULL, NULL, ""
27 };
28
29 static js_Property *newproperty(js_State *J, js_Object *obj, const char *name)
30 {
31 int n = strlen(name) + 1;
32 js_Property *node = js_malloc(J, offsetof(js_Property, name) + n);
33 node->left = node->right = &sentinel;
34 node->level = 1;
35 node->atts = 0;
36 node->value.t.type = JS_TUNDEFINED;
37 node->value.u.number = 0;
38 node->getter = NULL;
39 node->setter = NULL;
40 memcpy(node->name, name, n);
41 ++obj->count;
42 ++J->gccounter;
43 return node;
44 }
45
46 static js_Property *lookup(js_Property *node, const char *name)
47 {
48 while (node != &sentinel) {
49 int c = strcmp(name, node->name);
50 if (c == 0)
51 return node;
52 else if (c < 0)
53 node = node->left;
54 else
55 node = node->right;
56 }
57 return NULL;
58 }
59
60 static js_Property *skew(js_Property *node)
61 {
62 if (node->left->level == node->level) {
63 js_Property *temp = node;
64 node = node->left;
65 temp->left = node->right;
66 node->right = temp;
67 }
68 return node;
69 }
70
71 static js_Property *split(js_Property *node)
72 {
73 if (node->right->right->level == node->level) {
74 js_Property *temp = node;
75 node = node->right;
76 temp->right = node->left;
77 node->left = temp;
78 ++node->level;
79 }
80 return node;
81 }
82
83 static js_Property *insert(js_State *J, js_Object *obj, js_Property *node, const char *name, js_Property **result)
84 {
85 if (node != &sentinel) {
86 int c = strcmp(name, node->name);
87 if (c < 0)
88 node->left = insert(J, obj, node->left, name, result);
89 else if (c > 0)
90 node->right = insert(J, obj, node->right, name, result);
91 else
92 return *result = node;
93 node = skew(node);
94 node = split(node);
95 return node;
96 }
97 return *result = newproperty(J, obj, name);
98 }
99
100 static void freeproperty(js_State *J, js_Object *obj, js_Property *node)
101 {
102 js_free(J, node);
103 --obj->count;
104 }
105
106 static js_Property *unlinkproperty(js_Property *node, const char *name, js_Property **garbage)
107 {
108 js_Property *temp, *a, *b;
109 if (node != &sentinel) {
110 int c = strcmp(name, node->name);
111 if (c < 0) {
112 node->left = unlinkproperty(node->left, name, garbage);
113 } else if (c > 0) {
114 node->right = unlinkproperty(node->right, name, garbage);
115 } else {
116 *garbage = node;
117 if (node->left == &sentinel && node->right == &sentinel) {
118 return &sentinel;
119 }
120 else if (node->left == &sentinel) {
121 a = node->right;
122 while (a->left != &sentinel)
123 a = a->left;
124 b = unlinkproperty(node->right, a->name, &temp);
125 temp->level = node->level;
126 temp->left = node->left;
127 temp->right = b;
128 node = temp;
129 }
130 else {
131 a = node->left;
132 while (a->right != &sentinel)
133 a = a->right;
134 b = unlinkproperty(node->left, a->name, &temp);
135 temp->level = node->level;
136 temp->left = b;
137 temp->right = node->right;
138 node = temp;
139 }
140 }
141
142 if (node->left->level < node->level - 1 || node->right->level < node->level - 1)
143 {
144 if (node->right->level > --node->level)
145 node->right->level = node->level;
146 node = skew(node);
147 node->right = skew(node->right);
148 node->right->right = skew(node->right->right);
149 node = split(node);
150 node->right = split(node->right);
151 }
152 }
153 return node;
154 }
155
156 static js_Property *deleteproperty(js_State *J, js_Object *obj, js_Property *tree, const char *name)
157 {
158 js_Property *garbage = &sentinel;
159 tree = unlinkproperty(tree, name, &garbage);
160 if (garbage != &sentinel)
161 freeproperty(J, obj, garbage);
162 return tree;
163 }
164
165 js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
166 {
167 js_Object *obj = js_malloc(J, sizeof *obj);
168 memset(obj, 0, sizeof *obj);
169 obj->gcmark = 0;
170 obj->gcnext = J->gcobj;
171 J->gcobj = obj;
172 ++J->gccounter;
173
174 obj->type = type;
175 obj->properties = &sentinel;
176 obj->prototype = prototype;
177 obj->extensible = 1;
178 return obj;
179 }
180
181 js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
182 {
183 return lookup(obj->properties, name);
184 }
185
186 js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own)
187 {
188 *own = 1;
189 do {
190 js_Property *ref = lookup(obj->properties, name);
191 if (ref)
192 return ref;
193 obj = obj->prototype;
194 *own = 0;
195 } while (obj);
196 return NULL;
197 }
198
199 js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
200 {
201 do {
202 js_Property *ref = lookup(obj->properties, name);
203 if (ref)
204 return ref;
205 obj = obj->prototype;
206 } while (obj);
207 return NULL;
208 }
209
210 static js_Property *jsV_getenumproperty(js_State *J, js_Object *obj, const char *name)
211 {
212 do {
213 js_Property *ref = lookup(obj->properties, name);
214 if (ref && !(ref->atts & JS_DONTENUM))
215 return ref;
216 obj = obj->prototype;
217 } while (obj);
218 return NULL;
219 }
220
221 js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
222 {
223 js_Property *result;
224
225 if (!obj->extensible) {
226 result = lookup(obj->properties, name);
227 if (J->strict && !result)
228 js_typeerror(J, "object is non-extensible");
229 return result;
230 }
231
232 obj->properties = insert(J, obj, obj->properties, name, &result);
233
234 return result;
235 }
236
237 void jsV_delproperty(js_State *J, js_Object *obj, const char *name)
238 {
239 obj->properties = deleteproperty(J, obj, obj->properties, name);
240 }
241
242 /* Flatten hierarchy of enumerable properties into an iterator object */
243
244 static js_Iterator *itnewnode(js_State *J, const char *name, js_Iterator *next) {
245 int n = strlen(name) + 1;
246 js_Iterator *node = js_malloc(J, offsetof(js_Iterator, name) + n);
247 node->next = next;
248 memcpy(node->name, name, n);
249 return node;
250 }
251
252 static js_Iterator *itwalk(js_State *J, js_Iterator *iter, js_Property *prop, js_Object *seen)
253 {
254 if (prop->right != &sentinel)
255 iter = itwalk(J, iter, prop->right, seen);
256 if (!(prop->atts & JS_DONTENUM)) {
257 if (!seen || !jsV_getenumproperty(J, seen, prop->name)) {
258 iter = itnewnode(J, prop->name, iter);
259 }
260 }
261 if (prop->left != &sentinel)
262 iter = itwalk(J, iter, prop->left, seen);
263 return iter;
264 }
265
266 static js_Iterator *itflatten(js_State *J, js_Object *obj)
267 {
268 js_Iterator *iter = NULL;
269 if (obj->prototype)
270 iter = itflatten(J, obj->prototype);
271 if (obj->properties != &sentinel)
272 iter = itwalk(J, iter, obj->properties, obj->prototype);
273 return iter;
274 }
275
276 js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
277 {
278 js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
279 io->u.iter.target = obj;
280 io->u.iter.i = 0;
281 io->u.iter.n = 0;
282 if (own) {
283 io->u.iter.head = NULL;
284 if (obj->properties != &sentinel)
285 io->u.iter.head = itwalk(J, io->u.iter.head, obj->properties, NULL);
286 } else {
287 io->u.iter.head = itflatten(J, obj);
288 }
289 io->u.iter.current = io->u.iter.head;
290
291 if (obj->type == JS_CSTRING)
292 io->u.iter.n = obj->u.s.length;
293
294 if (obj->type == JS_CARRAY && obj->u.a.simple)
295 io->u.iter.n = obj->u.a.flat_length;
296
297 return io;
298 }
299
300 const char *jsV_nextiterator(js_State *J, js_Object *io)
301 {
302 if (io->type != JS_CITERATOR)
303 js_typeerror(J, "not an iterator");
304 if (io->u.iter.i < io->u.iter.n) {
305 js_itoa(J->scratch, io->u.iter.i);
306 io->u.iter.i++;
307 return J->scratch;
308 }
309 while (io->u.iter.current) {
310 const char *name = io->u.iter.current->name;
311 io->u.iter.current = io->u.iter.current->next;
312 if (jsV_getproperty(J, io->u.iter.target, name))
313 return name;
314 }
315 return NULL;
316 }
317
318 /* Walk all the properties and delete them one by one for arrays */
319
320 void jsV_resizearray(js_State *J, js_Object *obj, int newlen)
321 {
322 char buf[32];
323 const char *s;
324 int k;
325 assert(!obj->u.a.simple);
326 if (newlen < obj->u.a.length) {
327 if (obj->u.a.length > obj->count * 2) {
328 js_Object *it = jsV_newiterator(J, obj, 1);
329 while ((s = jsV_nextiterator(J, it))) {
330 k = jsV_numbertointeger(jsV_stringtonumber(J, s));
331 if (k >= newlen && !strcmp(s, jsV_numbertostring(J, buf, k)))
332 jsV_delproperty(J, obj, s);
333 }
334 } else {
335 for (k = newlen; k < obj->u.a.length; ++k) {
336 jsV_delproperty(J, obj, js_itoa(buf, k));
337 }
338 }
339 }
340 obj->u.a.length = newlen;
341 }