diff --git a/jsdump.c b/jsdump.c index 18f3cf2..6161eea 100644 --- a/jsdump.c +++ b/jsdump.c @@ -802,8 +802,8 @@ void js_dumpvalue(js_State *J, js_Value v) 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); diff --git a/jsi.h b/jsi.h index 7e93786..e3dccc4 100644 --- a/jsi.h +++ b/jsi.h @@ -39,6 +39,9 @@ void js_newfunction(js_State *J, js_Function *function, js_Environment *scope); 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); diff --git a/jsproperty.c b/jsproperty.c index ea7e89e..c96bd9f 100644 --- a/jsproperty.c +++ b/jsproperty.c @@ -218,10 +218,14 @@ void jsV_delproperty(js_State *J, js_Object *obj, const char *name) 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 @@ static void itwalk(js_State *J, js_Object *io, js_Object *top, int own) { 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 @@ js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own) 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 @@ const char *jsV_nextiterator(js_State *J, js_Object *io) 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; } diff --git a/jsrun.c b/jsrun.c index f6da603..5e9fbb0 100644 --- a/jsrun.c +++ b/jsrun.c @@ -357,9 +357,18 @@ void js_rot(js_State *J, int n) /* 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 @@ static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name) } } - // 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_getproperty(js_State *J, js_Object *obj, const char *name) 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 @@ static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, const 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 @@ static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, 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 void jsR_defproperty(js_State *J, js_Object *obj, const char *name, 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; diff --git a/jsstring.c b/jsstring.c index b572cb2..7bd3343 100644 --- a/jsstring.c +++ b/jsstring.c @@ -23,7 +23,7 @@ static int Sp_toString(js_State *J, int argc) { 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 @@ static int Sp_valueOf(js_State *J, int argc) { 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 @@ static inline const char *utfidx(const char *s, int i) 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 @@ static int Sp_charCodeAt(js_State *J, int argc) { 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 @@ static int Sp_split(js_State *J, int argc) 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); { diff --git a/jsvalue.c b/jsvalue.c index 84f8611..228faac 100644 --- a/jsvalue.c +++ b/jsvalue.c @@ -191,14 +191,8 @@ static js_Object *jsV_newnumber(js_State *J, double v) 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; } diff --git a/jsvalue.h b/jsvalue.h index 48b8841..02cd89c 100644 --- a/jsvalue.h +++ b/jsvalue.h @@ -62,7 +62,10 @@ struct js_Object union { int boolean; double number; - const char *string; + struct { + const char *string; + unsigned int length; + } s; struct { unsigned int length; } a;