shithub: libmujs

Download patch

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);