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;