ref: 653830808758ab3396ba111cad8ade7d80e25228
parent: 4eae3ec10c4a874fe4b021b01362f6f3362a18b0
author: Tor Andersson <tor.andersson@artifex.com>
date: Sat Nov 29 11:01:36 EST 2014
Add short strings (with data embedded in js_Value). Allows js_tostring to avoid either interning strings converted from numbers or creating lots of garbage collected strings.
--- a/jsdump.c
+++ b/jsdump.c
@@ -799,8 +799,9 @@
case JS_TNULL: printf("null"); break;
case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
case JS_TNUMBER: printf("%.9g", v.u.number); break;
- case JS_TLITERAL: printf("'%s'", v.u.literal); break;
- case JS_TSTRING: printf("'%s'", v.u.string->p); break;
+ case JS_TSHRSTR: printf("'%s'", v.u.shrstr); break;
+ case JS_TLITSTR: printf("'%s'", v.u.litstr); break;
+ case JS_TMEMSTR: printf("'%s'", v.u.memstr->p); break;
case JS_TOBJECT:
if (v.u.object == J->G) {
printf("[Global]");
--- a/jsgc.c
+++ b/jsgc.c
@@ -73,8 +73,8 @@
static void jsG_markproperty(js_State *J, int mark, js_Property *node)
{
while (node) {
- if (node->value.type == JS_TSTRING && node->value.u.string->gcmark != mark)
- node->value.u.string->gcmark = mark;
+ if (node->value.type == JS_TMEMSTR && node->value.u.memstr->gcmark != mark)
+ node->value.u.memstr->gcmark = mark;
if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark)
jsG_markobject(J, mark, node->value.u.object);
if (node->getter && node->getter->gcmark != mark)
@@ -108,8 +108,8 @@
js_Value *v = J->stack;
int n = J->top;
while (n--) {
- if (v->type == JS_TSTRING && v->u.string->gcmark != mark)
- v->u.string->gcmark = mark;
+ if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
+ v->u.memstr->gcmark = mark;
if (v->type == JS_TOBJECT && v->u.object->gcmark != mark)
jsG_markobject(J, mark, v->u.object);
++v;
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -23,7 +23,7 @@
&sentinel, &sentinel,
NULL, NULL,
0, 0,
- { 0, { 0 } },
+ { {0}, {0}, JS_TUNDEFINED },
NULL, NULL
};
--- a/jsrun.c
+++ b/jsrun.c
@@ -15,8 +15,8 @@
static void js_stackoverflow(js_State *J)
{
- STACK[TOP].type = JS_TLITERAL;
- STACK[TOP].u.literal = "stack overflow";
+ STACK[TOP].type = JS_TLITSTR;
+ STACK[TOP].u.litstr = "stack overflow";
++TOP;
js_throw(J);
}
@@ -23,8 +23,8 @@
static void js_outofmemory(js_State *J)
{
- STACK[TOP].type = JS_TLITERAL;
- STACK[TOP].u.literal = "out of memory";
+ STACK[TOP].type = JS_TLITSTR;
+ STACK[TOP].u.litstr = "out of memory";
++TOP;
js_throw(J);
}
@@ -50,7 +50,7 @@
J->alloc(J->actx, ptr, 0);
}
-static js_String *jsR_newstring(js_State *J, const char *s, int n)
+js_String *jsV_newmemstring(js_State *J, const char *s, int n)
{
js_String *v = js_malloc(J, offsetof(js_String, p) + n + 1);
memcpy(v->p, s, n);
@@ -103,9 +103,15 @@
void js_pushstring(js_State *J, const char *v)
{
+ int n = strlen(v);
CHECKSTACK(1);
- STACK[TOP].type = JS_TSTRING;
- STACK[TOP].u.string = jsR_newstring(J, v, strlen(v));
+ if (n < 16) {
+ STACK[TOP].type = JS_TSHRSTR;
+ strcpy(STACK[TOP].u.shrstr, v);
+ } else {
+ STACK[TOP].type = JS_TMEMSTR;
+ STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
+ }
++TOP;
}
@@ -112,8 +118,13 @@
void js_pushlstring(js_State *J, const char *v, unsigned int n)
{
CHECKSTACK(1);
- STACK[TOP].type = JS_TSTRING;
- STACK[TOP].u.string = jsR_newstring(J, v, n);
+ if (n < 16) {
+ STACK[TOP].type = JS_TSHRSTR;
+ strcpy(STACK[TOP].u.shrstr, v);
+ } else {
+ STACK[TOP].type = JS_TMEMSTR;
+ STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
+ }
++TOP;
}
@@ -120,19 +131,11 @@
void js_pushliteral(js_State *J, const char *v)
{
CHECKSTACK(1);
- STACK[TOP].type = JS_TLITERAL;
- STACK[TOP].u.literal = v;
+ STACK[TOP].type = JS_TLITSTR;
+ STACK[TOP].u.litstr = v;
++TOP;
}
-void js_pushintern(js_State *J, const char *v)
-{
- CHECKSTACK(1);
- STACK[TOP].type = JS_TLITERAL;
- STACK[TOP].u.literal = js_intern(J, v);
- ++TOP;
-}
-
void js_pushobject(js_State *J, js_Object *v)
{
CHECKSTACK(1);
@@ -157,7 +160,7 @@
static js_Value *stackidx(js_State *J, int idx)
{
- static js_Value undefined = { JS_TUNDEFINED, { 0 } };
+ static js_Value undefined = { {0}, {0}, JS_TUNDEFINED };
idx = idx < 0 ? TOP + idx : BOT + idx;
if (idx < 0 || idx >= TOP)
return &undefined;
@@ -174,7 +177,7 @@
int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TNULL; }
int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TBOOLEAN; }
int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TNUMBER; }
-int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->type; return t == JS_TLITERAL || t == JS_TSTRING; }
+int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; }
int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->type != JS_TOBJECT; }
int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TOBJECT; }
@@ -219,12 +222,13 @@
js_Value *v = stackidx(J, idx);
switch (v->type) {
default:
+ case JS_TSHRSTR: return "string";
case JS_TUNDEFINED: return "undefined";
case JS_TNULL: return "object";
case JS_TBOOLEAN: return "boolean";
case JS_TNUMBER: return "number";
- case JS_TLITERAL: return "string";
- case JS_TSTRING: return "string";
+ case JS_TLITSTR: return "string";
+ case JS_TMEMSTR: return "string";
case JS_TOBJECT:
if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
return "function";
@@ -270,11 +274,6 @@
const char *js_tostring(js_State *J, int idx)
{
return jsV_tostring(J, stackidx(J, idx));
-}
-
-const char *js_tointern(js_State *J, int idx)
-{
- return js_intern(J, jsV_tostring(J, stackidx(J, idx)));
}
js_Object *js_toobject(js_State *J, int idx)
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -4,8 +4,8 @@
#include "jsvalue.h"
#include "utf.h"
-#define JSV_ISSTRING(v) (v->type==JS_TSTRING || v->type==JS_TLITERAL)
-#define JSV_TOSTRING(v) (v->type==JS_TSTRING ? v->u.string->p : v->type==JS_TLITERAL ? v->u.literal : NULL)
+#define JSV_ISSTRING(v) (v->type==JS_TSHRSTR || v->type==JS_TMEMSTR || v->type==JS_TLITSTR)
+#define JSV_TOSTRING(v) (v->type==JS_TSHRSTR ? v->u.shrstr : v->type==JS_TLITSTR ? v->u.litstr : v->type==JS_TMEMSTR ? v->u.memstr->p : NULL)
double jsV_numbertointeger(double n)
{
@@ -115,12 +115,13 @@
{
switch (v->type) {
default:
+ case JS_TSHRSTR: return v->u.shrstr[0] != 0;
case JS_TUNDEFINED: return 0;
case JS_TNULL: return 0;
case JS_TBOOLEAN: return v->u.boolean;
case JS_TNUMBER: return v->u.number != 0 && !isnan(v->u.number);
- case JS_TLITERAL: return v->u.literal[0] != 0;
- case JS_TSTRING: return v->u.string->p[0] != 0;
+ case JS_TLITSTR: return v->u.litstr[0] != 0;
+ case JS_TMEMSTR: return v->u.memstr->p[0] != 0;
case JS_TOBJECT: return 1;
}
}
@@ -195,12 +196,13 @@
{
switch (v->type) {
default:
+ case JS_TSHRSTR: return jsV_stringtonumber(J, v->u.shrstr);
case JS_TUNDEFINED: return NAN;
case JS_TNULL: return 0;
case JS_TBOOLEAN: return v->u.boolean;
case JS_TNUMBER: return v->u.number;
- case JS_TLITERAL: return jsV_stringtonumber(J, v->u.literal);
- case JS_TSTRING: return jsV_stringtonumber(J, v->u.string->p);
+ case JS_TLITSTR: return jsV_stringtonumber(J, v->u.litstr);
+ case JS_TMEMSTR: return jsV_stringtonumber(J, v->u.memstr->p);
case JS_TOBJECT:
jsV_toprimitive(J, v, JS_HNUMBER);
return jsV_tonumber(J, v);
@@ -267,15 +269,30 @@
const char *jsV_tostring(js_State *J, js_Value *v)
{
char buf[32];
+ const char *p;
switch (v->type) {
default:
+ case JS_TSHRSTR: return v->u.shrstr;
case JS_TUNDEFINED: return "undefined";
case JS_TNULL: return "null";
case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
- case JS_TLITERAL: return v->u.literal;
- case JS_TSTRING: return v->u.string->p;
+ case JS_TLITSTR: return v->u.litstr;
+ case JS_TMEMSTR: return v->u.memstr->p;
case JS_TNUMBER:
- return js_intern(J, jsV_numbertostring(J, buf, v->u.number));
+ p = jsV_numbertostring(J, buf, v->u.number);
+ if (p == buf) {
+ int n = strlen(p);
+ if (n < 16) {
+ v->type = JS_TSHRSTR;
+ strcpy(v->u.shrstr, p);
+ return v->u.shrstr;
+ } else {
+ v->type = JS_TMEMSTR;
+ v->u.memstr = jsV_newmemstring(J, p, n);
+ return v->u.memstr->p;
+ }
+ }
+ return p;
case JS_TOBJECT:
jsV_toprimitive(J, v, JS_HSTRING);
return jsV_tostring(J, v);
@@ -311,12 +328,13 @@
{
switch (v->type) {
default:
+ case JS_TSHRSTR: return jsV_newstring(J, v->u.shrstr);
case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object");
case JS_TNULL: js_typeerror(J, "cannot convert null to object");
case JS_TBOOLEAN: return jsV_newboolean(J, v->u.boolean);
case JS_TNUMBER: return jsV_newnumber(J, v->u.number);
- case JS_TLITERAL: return jsV_newstring(J, v->u.literal);
- case JS_TSTRING: return jsV_newstring(J, v->u.string->p);
+ case JS_TLITSTR: return jsV_newstring(J, v->u.litstr);
+ case JS_TMEMSTR: return jsV_newstring(J, v->u.memstr->p);
case JS_TOBJECT: return v->u.object;
}
}
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -12,12 +12,13 @@
};
enum js_Type {
+ JS_TSHRSTR, /* type tag doubles as string zero-terminator */
JS_TUNDEFINED,
JS_TNULL,
JS_TBOOLEAN,
JS_TNUMBER,
- JS_TLITERAL,
- JS_TSTRING,
+ JS_TLITSTR,
+ JS_TMEMSTR,
JS_TOBJECT,
};
@@ -41,14 +42,16 @@
struct js_Value
{
- enum js_Type type;
union {
int boolean;
double number;
- const char *literal;
- js_String *string;
+ char shrstr[8];
+ const char *litstr;
+ js_String *memstr;
js_Object *object;
} u;
+ char pad[7];
+ char type;
};
struct js_String
@@ -126,6 +129,7 @@
};
/* jsrun.c */
+js_String *jsV_newmemstring(js_State *J, const char *s, int n);
js_Value *js_tovalue(js_State *J, int idx);
void js_toprimitive(js_State *J, int idx, int hint);
js_Object *js_toobject(js_State *J, int idx);
--- a/mujs.h
+++ b/mujs.h
@@ -114,7 +114,6 @@
void js_pushstring(js_State *J, const char *v);
void js_pushlstring(js_State *J, const char *v, unsigned int n);
void js_pushliteral(js_State *J, const char *v);
-void js_pushintern(js_State *J, const char *v);
void js_newobject(js_State *J);
void js_newarray(js_State *J);
@@ -145,7 +144,6 @@
int js_toboolean(js_State *J, int idx);
double js_tonumber(js_State *J, int idx);
const char *js_tostring(js_State *J, int idx);
-const char *js_tointern(js_State *J, int idx);
void *js_touserdata(js_State *J, int idx, const char *tag);
double js_tointeger(js_State *J, int idx);