diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/mujs/jsproperty.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,341 @@
+#include "jsi.h"
+
+#include <assert.h>
+
+/*
+	Use an AA-tree to quickly look up properties in objects:
+
+	The level of every leaf node is one.
+	The level of every left child is one less than its parent.
+	The level of every right child is equal or one less than its parent.
+	The level of every right grandchild is less than its grandparent.
+	Every node of level greater than one has two children.
+
+	A link where the child's level is equal to that of its parent is called a horizontal link.
+	Individual right horizontal links are allowed, but consecutive ones are forbidden.
+	Left horizontal links are forbidden.
+
+	skew() fixes left horizontal links.
+	split() fixes consecutive right horizontal links.
+*/
+
+static js_Property sentinel = {
+	&sentinel, &sentinel,
+	0, 0,
+	{ { {0}, JS_TUNDEFINED } },
+	NULL, NULL, ""
+};
+
+static js_Property *newproperty(js_State *J, js_Object *obj, const char *name)
+{
+	int n = strlen(name) + 1;
+	js_Property *node = js_malloc(J, offsetof(js_Property, name) + n);
+	node->left = node->right = &sentinel;
+	node->level = 1;
+	node->atts = 0;
+	node->value.t.type = JS_TUNDEFINED;
+	node->value.u.number = 0;
+	node->getter = NULL;
+	node->setter = NULL;
+	memcpy(node->name, name, n);
+	++obj->count;
+	++J->gccounter;
+	return node;
+}
+
+static js_Property *lookup(js_Property *node, const char *name)
+{
+	while (node != &sentinel) {
+		int c = strcmp(name, node->name);
+		if (c == 0)
+			return node;
+		else if (c < 0)
+			node = node->left;
+		else
+			node = node->right;
+	}
+	return NULL;
+}
+
+static js_Property *skew(js_Property *node)
+{
+	if (node->left->level == node->level) {
+		js_Property *temp = node;
+		node = node->left;
+		temp->left = node->right;
+		node->right = temp;
+	}
+	return node;
+}
+
+static js_Property *split(js_Property *node)
+{
+	if (node->right->right->level == node->level) {
+		js_Property *temp = node;
+		node = node->right;
+		temp->right = node->left;
+		node->left = temp;
+		++node->level;
+	}
+	return node;
+}
+
+static js_Property *insert(js_State *J, js_Object *obj, js_Property *node, const char *name, js_Property **result)
+{
+	if (node != &sentinel) {
+		int c = strcmp(name, node->name);
+		if (c < 0)
+			node->left = insert(J, obj, node->left, name, result);
+		else if (c > 0)
+			node->right = insert(J, obj, node->right, name, result);
+		else
+			return *result = node;
+		node = skew(node);
+		node = split(node);
+		return node;
+	}
+	return *result = newproperty(J, obj, name);
+}
+
+static void freeproperty(js_State *J, js_Object *obj, js_Property *node)
+{
+	js_free(J, node);
+	--obj->count;
+}
+
+static js_Property *unlinkproperty(js_Property *node, const char *name, js_Property **garbage)
+{
+	js_Property *temp, *a, *b;
+	if (node != &sentinel) {
+		int c = strcmp(name, node->name);
+		if (c < 0) {
+			node->left = unlinkproperty(node->left, name, garbage);
+		} else if (c > 0) {
+			node->right = unlinkproperty(node->right, name, garbage);
+		} else {
+			*garbage = node;
+			if (node->left == &sentinel && node->right == &sentinel) {
+				return &sentinel;
+			}
+			else if (node->left == &sentinel) {
+				a = node->right;
+				while (a->left != &sentinel)
+					a = a->left;
+				b = unlinkproperty(node->right, a->name, &temp);
+				temp->level = node->level;
+				temp->left = node->left;
+				temp->right = b;
+				node = temp;
+			}
+			else {
+				a = node->left;
+				while (a->right != &sentinel)
+					a = a->right;
+				b = unlinkproperty(node->left, a->name, &temp);
+				temp->level = node->level;
+				temp->left = b;
+				temp->right = node->right;
+				node = temp;
+			}
+		}
+
+		if (node->left->level < node->level - 1 || node->right->level < node->level - 1)
+		{
+			if (node->right->level > --node->level)
+				node->right->level = node->level;
+			node = skew(node);
+			node->right = skew(node->right);
+			node->right->right = skew(node->right->right);
+			node = split(node);
+			node->right = split(node->right);
+		}
+	}
+	return node;
+}
+
+static js_Property *deleteproperty(js_State *J, js_Object *obj, js_Property *tree, const char *name)
+{
+	js_Property *garbage = &sentinel;
+	tree = unlinkproperty(tree, name, &garbage);
+	if (garbage != &sentinel)
+		freeproperty(J, obj, garbage);
+	return tree;
+}
+
+js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
+{
+	js_Object *obj = js_malloc(J, sizeof *obj);
+	memset(obj, 0, sizeof *obj);
+	obj->gcmark = 0;
+	obj->gcnext = J->gcobj;
+	J->gcobj = obj;
+	++J->gccounter;
+
+	obj->type = type;
+	obj->properties = &sentinel;
+	obj->prototype = prototype;
+	obj->extensible = 1;
+	return obj;
+}
+
+js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
+{
+	return lookup(obj->properties, name);
+}
+
+js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own)
+{
+	*own = 1;
+	do {
+		js_Property *ref = lookup(obj->properties, name);
+		if (ref)
+			return ref;
+		obj = obj->prototype;
+		*own = 0;
+	} while (obj);
+	return NULL;
+}
+
+js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
+{
+	do {
+		js_Property *ref = lookup(obj->properties, name);
+		if (ref)
+			return ref;
+		obj = obj->prototype;
+	} while (obj);
+	return NULL;
+}
+
+static js_Property *jsV_getenumproperty(js_State *J, js_Object *obj, const char *name)
+{
+	do {
+		js_Property *ref = lookup(obj->properties, name);
+		if (ref && !(ref->atts & JS_DONTENUM))
+			return ref;
+		obj = obj->prototype;
+	} while (obj);
+	return NULL;
+}
+
+js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
+{
+	js_Property *result;
+
+	if (!obj->extensible) {
+		result = lookup(obj->properties, name);
+		if (J->strict && !result)
+			js_typeerror(J, "object is non-extensible");
+		return result;
+	}
+
+	obj->properties = insert(J, obj, obj->properties, name, &result);
+
+	return result;
+}
+
+void jsV_delproperty(js_State *J, js_Object *obj, const char *name)
+{
+	obj->properties = deleteproperty(J, obj, obj->properties, name);
+}
+
+/* Flatten hierarchy of enumerable properties into an iterator object */
+
+static js_Iterator *itnewnode(js_State *J, const char *name, js_Iterator *next) {
+	int n = strlen(name) + 1;
+	js_Iterator *node = js_malloc(J, offsetof(js_Iterator, name) + n);
+	node->next = next;
+	memcpy(node->name, name, n);
+	return node;
+}
+
+static js_Iterator *itwalk(js_State *J, js_Iterator *iter, js_Property *prop, js_Object *seen)
+{
+	if (prop->right != &sentinel)
+		iter = itwalk(J, iter, prop->right, seen);
+	if (!(prop->atts & JS_DONTENUM)) {
+		if (!seen || !jsV_getenumproperty(J, seen, prop->name)) {
+			iter = itnewnode(J, prop->name, iter);
+		}
+	}
+	if (prop->left != &sentinel)
+		iter = itwalk(J, iter, prop->left, seen);
+	return iter;
+}
+
+static js_Iterator *itflatten(js_State *J, js_Object *obj)
+{
+	js_Iterator *iter = NULL;
+	if (obj->prototype)
+		iter = itflatten(J, obj->prototype);
+	if (obj->properties != &sentinel)
+		iter = itwalk(J, iter, obj->properties, obj->prototype);
+	return iter;
+}
+
+js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
+{
+	js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
+	io->u.iter.target = obj;
+	io->u.iter.i = 0;
+	io->u.iter.n = 0;
+	if (own) {
+		io->u.iter.head = NULL;
+		if (obj->properties != &sentinel)
+			io->u.iter.head = itwalk(J, io->u.iter.head, obj->properties, NULL);
+	} else {
+		io->u.iter.head = itflatten(J, obj);
+	}
+	io->u.iter.current = io->u.iter.head;
+
+	if (obj->type == JS_CSTRING)
+		io->u.iter.n = obj->u.s.length;
+
+	if (obj->type == JS_CARRAY && obj->u.a.simple)
+		io->u.iter.n = obj->u.a.flat_length;
+
+	return io;
+}
+
+const char *jsV_nextiterator(js_State *J, js_Object *io)
+{
+	if (io->type != JS_CITERATOR)
+		js_typeerror(J, "not an iterator");
+	if (io->u.iter.i < io->u.iter.n) {
+		js_itoa(J->scratch, io->u.iter.i);
+		io->u.iter.i++;
+		return J->scratch;
+	}
+	while (io->u.iter.current) {
+		const char *name = io->u.iter.current->name;
+		io->u.iter.current = io->u.iter.current->next;
+		if (jsV_getproperty(J, io->u.iter.target, name))
+			return name;
+	}
+	return NULL;
+}
+
+/* Walk all the properties and delete them one by one for arrays */
+
+void jsV_resizearray(js_State *J, js_Object *obj, int newlen)
+{
+	char buf[32];
+	const char *s;
+	int k;
+	assert(!obj->u.a.simple);
+	if (newlen < obj->u.a.length) {
+		if (obj->u.a.length > obj->count * 2) {
+			js_Object *it = jsV_newiterator(J, obj, 1);
+			while ((s = jsV_nextiterator(J, it))) {
+				k = jsV_numbertointeger(jsV_stringtonumber(J, s));
+				if (k >= newlen && !strcmp(s, jsV_numbertostring(J, buf, k)))
+					jsV_delproperty(J, obj, s);
+			}
+		} else {
+			for (k = newlen; k < obj->u.a.length; ++k) {
+				jsV_delproperty(J, obj, js_itoa(buf, k));
+			}
+		}
+	}
+	obj->u.a.length = newlen;
+}