shithub: libmujs

Download patch

ref: b219aef882a27ed9c2aed10687999a221eea216e
parent: d59d0e2721540568c3f9c0e0ae2b464c6b289ad6
author: Tor Andersson <tor@ccxvii.net>
date: Fri Feb 7 11:40:22 EST 2014

Add virtual string array index properties for characters.

--- a/jsdump.c
+++ b/jsdump.c
@@ -802,8 +802,8 @@
 		case JS_CCFUNCTION: printf("[CFunction %p]", v.u.object->u.c.function); break;
 		case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break;
 		case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break;
-		case JS_CSTRING: printf("[String'%s']", v.u.object->u.string); break;
-		case JS_CERROR: printf("[Error %s]", v.u.object->u.string); break;
+		case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break;
+		case JS_CERROR: printf("[Error %s]", v.u.object->u.s.string); break;
 		case JS_CITERATOR: printf("[Iterator %p]", v.u.object); break;
 		case JS_CUSERDATA:
 			printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data);
--- a/jsi.h
+++ b/jsi.h
@@ -39,6 +39,9 @@
 void js_newscript(js_State *J, js_Function *function);
 
 js_Regexp *js_toregexp(js_State *J, int idx);
+int js_isarrayindex(js_State *J, const char *str, unsigned int *idx);
+int js_runeat(js_State *J, const char *s, int i);
+void js_pushcharat(js_State *J, const char *s, int pos);
 
 void js_dup(js_State *J);
 void js_rot2(js_State *J);
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -218,10 +218,14 @@
 
 static int itshadow(js_State *J, js_Object *top, js_Object *bot, const char *name)
 {
+	unsigned int k;
 	while (top != bot) {
 		js_Property *prop = lookup(top->properties, name);
 		if (prop && !(prop->atts & JS_DONTENUM))
 			return 1;
+		if (top->type == JS_CSTRING)
+			if (js_isarrayindex(J, name, &k) && k < top->u.s.length)
+				return 1;
 		top = top->prototype;
 	}
 	return 0;
@@ -231,22 +235,38 @@
 {
 	js_Object *obj = top;
 	js_Iterator *tail = NULL;
+	char buf[32];
+	unsigned int k;
+
+#define ITADD(x) \
+	js_Iterator *node = malloc(sizeof *node); \
+	node->name = x; \
+	node->next = NULL; \
+	if (!tail) { \
+		io->u.iter.head = tail = node; \
+	} else { \
+		tail->next = node; \
+		tail = node; \
+	}
+
 	while (obj) {
 		js_Property *prop = obj->head;
 		while (prop) {
 			if (!(prop->atts & JS_DONTENUM) && !itshadow(J, top, obj, prop->name)) {
-				js_Iterator *node = malloc(sizeof *node);
-				node->name = prop->name;
-				node->next = NULL;
-				if (!tail) {
-					io->u.iter.head = tail = node;
-				} else {
-					tail->next = node;
-					tail = node;
-				}
+				ITADD(prop->name);
 			}
 			prop = prop->next;
 		}
+
+		if (obj->type == JS_CSTRING) {
+			for (k = 0; k < obj->u.s.length; ++k) {
+				sprintf(buf, "%u", k);
+				if (!itshadow(J, top, obj, buf)) {
+					ITADD(js_intern(J, buf));
+				}
+			}
+		}
+
 		if (own)
 			break;
 		obj = obj->prototype;
@@ -264,6 +284,7 @@
 
 const char *jsV_nextiterator(js_State *J, js_Object *io)
 {
+	unsigned int k;
 	if (io->type != JS_CITERATOR)
 		js_typeerror(J, "not an iterator");
 	while (io->u.iter.head) {
@@ -273,6 +294,9 @@
 		io->u.iter.head = next;
 		if (jsV_getproperty(J, io->u.iter.target, name))
 			return name;
+		if (io->u.iter.target->type == JS_CSTRING)
+			if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length)
+				return name;
 	}
 	return NULL;
 }
--- a/jsrun.c
+++ b/jsrun.c
@@ -357,9 +357,18 @@
 
 /* Property access that takes care of attributes and getters/setters */
 
+int js_isarrayindex(js_State *J, const char *str, unsigned int *idx)
+{
+	char buf[32];
+	*idx = jsV_numbertouint32(jsV_stringtonumber(J, str));
+	sprintf(buf, "%u", *idx);
+	return !strcmp(buf, str);
+}
+
 static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name)
 {
 	js_Property *ref;
+	unsigned int k;
 
 	if (obj->type == JS_CARRAY) {
 		if (!strcmp(name, "length")) {
@@ -368,7 +377,16 @@
 		}
 	}
 
-	// TODO: String array indexing
+	if (obj->type == JS_CSTRING) {
+		if (!strcmp(name, "length")) {
+			js_pushnumber(J, obj->u.s.length);
+			return 1;
+		}
+		if (js_isarrayindex(J, name, &k)) {
+			js_pushcharat(J, obj->u.s.string, k);
+			return 1;
+		}
+	}
 
 	if (obj->type == JS_CREGEXP) {
 		if (!strcmp(name, "source")) {
@@ -417,12 +435,10 @@
 static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, const js_Value *value)
 {
 	js_Property *ref;
+	unsigned int k;
 	int own;
 
 	if (obj->type == JS_CARRAY) {
-		unsigned int k;
-		char buf[32];
-
 		if (!strcmp(name, "length")) {
 			double rawlen = jsV_tonumber(J, value);
 			unsigned int newlen = jsV_numbertouint32(rawlen);
@@ -431,16 +447,18 @@
 			jsV_resizearray(J, obj, newlen);
 			return;
 		}
-
-		k = jsV_numbertouint32(jsV_stringtonumber(J, name));
-		if (k >= obj->u.a.length) {
-			sprintf(buf, "%u", k);
-			if (!strcmp(buf, name))
+		if (js_isarrayindex(J, name, &k))
+			if (k >= obj->u.a.length)
 				obj->u.a.length = k + 1;
-		}
 	}
 
-	// TODO: String array indexing
+	if (obj->type == JS_CSTRING) {
+		if (!strcmp(name, "length"))
+			return;
+		if (js_isarrayindex(J, name, &k))
+			if (js_runeat(J, obj->u.s.string, k))
+				return;
+	}
 
 	if (obj->type == JS_CREGEXP) {
 		if (!strcmp(name, "source")) return;
@@ -476,12 +494,19 @@
 	int atts, const js_Value *value, js_Object *getter, js_Object *setter)
 {
 	js_Property *ref;
+	unsigned int k;
 
 	if (obj->type == JS_CARRAY)
 		if (!strcmp(name, "length"))
 			return;
 
-	// TODO: String array indexing
+	if (obj->type == JS_CSTRING) {
+		if (!strcmp(name, "length"))
+			return;
+		if (js_isarrayindex(J, name, &k))
+			if (js_runeat(J, obj->u.s.string, k))
+				return;
+	}
 
 	if (obj->type == JS_CREGEXP) {
 		if (!strcmp(name, "source")) return;
@@ -506,11 +531,18 @@
 static int jsR_delproperty(js_State *J, js_Object *obj, const char *name)
 {
 	js_Property *ref;
+	unsigned int k;
 
 	if (obj->type == JS_CARRAY)
 		if (!strcmp(name, "length")) return 0;
 
-	// TODO: String array indexing
+	if (obj->type == JS_CSTRING) {
+		if (!strcmp(name, "length"))
+			return 0;
+		if (js_isarrayindex(J, name, &k))
+			if (js_runeat(J, obj->u.s.string, k))
+				return 0;
+	}
 
 	if (obj->type == JS_CREGEXP) {
 		if (!strcmp(name, "source")) return 0;
--- a/jsstring.c
+++ b/jsstring.c
@@ -23,7 +23,7 @@
 {
 	js_Object *self = js_toobject(J, 0);
 	if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
-	js_pushliteral(J, self->u.string);
+	js_pushliteral(J, self->u.s.string);
 	return 1;
 }
 
@@ -31,11 +31,11 @@
 {
 	js_Object *self = js_toobject(J, 0);
 	if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
-	js_pushliteral(J, self->u.string);
+	js_pushliteral(J, self->u.s.string);
 	return 1;
 }
 
-static inline Rune runeAt(const char *s, int i)
+int js_runeat(js_State *J, const char *s, int i)
 {
 	Rune rune = 0;
 	while (i-- >= 0) {
@@ -65,12 +65,24 @@
 	return s;
 }
 
+void js_pushcharat(js_State *J, const char *s, int pos)
+{
+	char buf[UTFmax + 1];
+	Rune rune = js_runeat(J, s, pos);
+	if (rune > 0) {
+		buf[runetochar(buf, &rune)] = 0;
+		js_pushstring(J, buf);
+	} else {
+		js_pushundefined(J);
+	}
+}
+
 static int Sp_charAt(js_State *J, int argc)
 {
 	char buf[UTFmax + 1];
 	const char *s = js_tostring(J, 0);
 	int pos = js_tointeger(J, 1);
-	Rune rune = runeAt(s, pos);
+	Rune rune = js_runeat(J, s, pos);
 	if (rune > 0) {
 		buf[runetochar(buf, &rune)] = 0;
 		js_pushstring(J, buf);
@@ -84,7 +96,7 @@
 {
 	const char *s = js_tostring(J, 0);
 	int pos = js_tointeger(J, 1);
-	Rune rune = runeAt(s, pos);
+	Rune rune = js_runeat(J, s, pos);
 	if (rune > 0)
 		js_pushnumber(J, rune);
 	else
@@ -652,7 +664,8 @@
 
 void jsB_initstring(js_State *J)
 {
-	J->String_prototype->u.string = "";
+	J->String_prototype->u.s.string = "";
+	J->String_prototype->u.s.length = 0;
 
 	js_pushobject(J, J->String_prototype);
 	{
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -191,14 +191,8 @@
 static js_Object *jsV_newstring(js_State *J, const char *v)
 {
 	js_Object *obj = jsV_newobject(J, JS_CSTRING, J->String_prototype);
-	obj->u.string = v;
-	{
-		js_Property *ref;
-		ref = jsV_setproperty(J, obj, "length");
-		ref->value.type = JS_TNUMBER;
-		ref->value.u.number = utflen(v);
-		ref->atts = JS_READONLY | JS_DONTENUM | JS_DONTCONF;
-	}
+	obj->u.s.string = v;
+	obj->u.s.length = utflen(v);
 	return obj;
 }
 
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -62,7 +62,10 @@
 	union {
 		int boolean;
 		double number;
-		const char *string;
+		struct {
+			const char *string;
+			unsigned int length;
+		} s;
 		struct {
 			unsigned int length;
 		} a;