shithub: libmujs

Download patch

ref: 2e2738b2930ccb0fbd04a37f6f214f9967b33a88
parent: dd149b98bd600ce2673d9123bacbafa6b58080af
author: Tor Andersson <tor.andersson@artifex.com>
date: Fri Dec 2 09:13:29 EST 2022

Change js_Value to union to avoid some compiler optimization warnings.

Use a union of a union and the padding + type tag, to let the shrstr
object size be the full 16 bytes to avoid compiler complaints about
stepping out of bounds of the array when it optimizes heavily.

--- a/jsarray.c
+++ b/jsarray.c
@@ -247,8 +247,8 @@
 	double v;
 	int c;
 
-	int unx = (a->type == JS_TUNDEFINED);
-	int uny = (b->type == JS_TUNDEFINED);
+	int unx = (a->t.type == JS_TUNDEFINED);
+	int uny = (b->t.type == JS_TUNDEFINED);
 	if (unx) return !uny;
 	if (uny) return -1;
 
--- a/jsgc.c
+++ b/jsgc.c
@@ -85,9 +85,9 @@
 	if (node->left->level) jsG_markproperty(J, mark, node->left);
 	if (node->right->level) jsG_markproperty(J, mark, node->right);
 
-	if (node->value.type == JS_TMEMSTR && node->value.u.memstr->gcmark != mark)
+	if (node->value.t.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)
+	if (node->value.t.type == JS_TOBJECT && node->value.u.object->gcmark != mark)
 		jsG_markobject(J, mark, node->value.u.object);
 	if (node->getter && node->getter->gcmark != mark)
 		jsG_markobject(J, mark, node->getter);
@@ -106,9 +106,9 @@
 		int i;
 		for (i = 0; i < obj->u.a.flat_length; ++i) {
 			js_Value *v = &obj->u.a.array[i];
-			if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
+			if (v->t.type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
 				v->u.memstr->gcmark = mark;
-			if (v->type == JS_TOBJECT && v->u.object->gcmark != mark)
+			if (v->t.type == JS_TOBJECT && v->u.object->gcmark != mark)
 				jsG_markobject(J, mark, v->u.object);
 		}
 	}
@@ -128,9 +128,9 @@
 	js_Value *v = J->stack;
 	int n = J->top;
 	while (n--) {
-		if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
+		if (v->t.type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
 			v->u.memstr->gcmark = mark;
-		if (v->type == JS_TOBJECT && v->u.object->gcmark != mark)
+		if (v->t.type == JS_TOBJECT && v->u.object->gcmark != mark)
 			jsG_markobject(J, mark, v->u.object);
 		++v;
 	}
--- a/jsi.h
+++ b/jsi.h
@@ -64,8 +64,8 @@
 void *js_realloc(js_State *J, void *ptr, int size);
 void js_free(js_State *J, void *ptr);
 
+typedef union js_Value js_Value;
 typedef struct js_Regexp js_Regexp;
-typedef struct js_Value js_Value;
 typedef struct js_Object js_Object;
 typedef struct js_String js_String;
 typedef struct js_Ast js_Ast;
@@ -329,18 +329,20 @@
 	purpose as the string zero terminator.
 */
 
-struct js_Value
+union js_Value
 {
+	struct {
+		char pad[15];
+		char type; /* type tag overlaps with final byte of shrstr */
+	} t;
 	union {
+		char shrstr[16];
 		int boolean;
 		double number;
-		char shrstr[8];
 		const char *litstr;
 		js_String *memstr;
 		js_Object *object;
 	} u;
-	char pad[7]; /* extra storage for shrstr */
-	char type; /* type tag and zero terminator for shrstr */
 };
 
 struct js_String
--- a/jsobject.c
+++ b/jsobject.c
@@ -312,7 +312,7 @@
 	if (ref->left->level)
 		O_create_walk(J, obj, ref->left);
 	if (!(ref->atts & JS_DONTENUM)) {
-		if (ref->value.type != JS_TOBJECT)
+		if (ref->value.t.type != JS_TOBJECT)
 			js_typeerror(J, "not an object");
 		ToPropertyDescriptor(J, obj, ref->name, ref->value.u.object);
 	}
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -22,7 +22,7 @@
 static js_Property sentinel = {
 	&sentinel, &sentinel,
 	0, 0,
-	{ {0}, {0}, JS_TUNDEFINED },
+	{ { {0}, JS_TUNDEFINED } },
 	NULL, NULL, ""
 };
 
@@ -33,7 +33,7 @@
 	node->left = node->right = &sentinel;
 	node->level = 1;
 	node->atts = 0;
-	node->value.type = JS_TUNDEFINED;
+	node->value.t.type = JS_TUNDEFINED;
 	node->value.u.number = 0;
 	node->getter = NULL;
 	node->setter = NULL;
--- a/jsrun.c
+++ b/jsrun.c
@@ -13,7 +13,7 @@
 
 static void js_trystackoverflow(js_State *J)
 {
-	STACK[TOP].type = JS_TLITSTR;
+	STACK[TOP].t.type = JS_TLITSTR;
 	STACK[TOP].u.litstr = "exception stack overflow";
 	++TOP;
 	js_throw(J);
@@ -21,7 +21,7 @@
 
 static void js_stackoverflow(js_State *J)
 {
-	STACK[TOP].type = JS_TLITSTR;
+	STACK[TOP].t.type = JS_TLITSTR;
 	STACK[TOP].u.litstr = "stack overflow";
 	++TOP;
 	js_throw(J);
@@ -29,7 +29,7 @@
 
 static void js_outofmemory(js_State *J)
 {
-	STACK[TOP].type = JS_TLITSTR;
+	STACK[TOP].t.type = JS_TLITSTR;
 	STACK[TOP].u.litstr = "out of memory";
 	++TOP;
 	js_throw(J);
@@ -88,7 +88,7 @@
 void js_pushundefined(js_State *J)
 {
 	CHECKSTACK(1);
-	STACK[TOP].type = JS_TUNDEFINED;
+	STACK[TOP].t.type = JS_TUNDEFINED;
 	++TOP;
 }
 
@@ -95,7 +95,7 @@
 void js_pushnull(js_State *J)
 {
 	CHECKSTACK(1);
-	STACK[TOP].type = JS_TNULL;
+	STACK[TOP].t.type = JS_TNULL;
 	++TOP;
 }
 
@@ -102,7 +102,7 @@
 void js_pushboolean(js_State *J, int v)
 {
 	CHECKSTACK(1);
-	STACK[TOP].type = JS_TBOOLEAN;
+	STACK[TOP].t.type = JS_TBOOLEAN;
 	STACK[TOP].u.boolean = !!v;
 	++TOP;
 }
@@ -110,7 +110,7 @@
 void js_pushnumber(js_State *J, double v)
 {
 	CHECKSTACK(1);
-	STACK[TOP].type = JS_TNUMBER;
+	STACK[TOP].t.type = JS_TNUMBER;
 	STACK[TOP].u.number = v;
 	++TOP;
 }
@@ -121,13 +121,13 @@
 	if (n > JS_STRLIMIT)
 		js_rangeerror(J, "invalid string length");
 	CHECKSTACK(1);
-	if (n <= soffsetof(js_Value, type)) {
+	if (n <= soffsetof(js_Value, t.type)) {
 		char *s = STACK[TOP].u.shrstr;
 		while (n--) *s++ = *v++;
 		*s = 0;
-		STACK[TOP].type = JS_TSHRSTR;
+		STACK[TOP].t.type = JS_TSHRSTR;
 	} else {
-		STACK[TOP].type = JS_TMEMSTR;
+		STACK[TOP].t.type = JS_TMEMSTR;
 		STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
 	}
 	++TOP;
@@ -138,13 +138,13 @@
 	if (n > JS_STRLIMIT)
 		js_rangeerror(J, "invalid string length");
 	CHECKSTACK(1);
-	if (n <= soffsetof(js_Value, type)) {
+	if (n <= soffsetof(js_Value, t.type)) {
 		char *s = STACK[TOP].u.shrstr;
 		while (n--) *s++ = *v++;
 		*s = 0;
-		STACK[TOP].type = JS_TSHRSTR;
+		STACK[TOP].t.type = JS_TSHRSTR;
 	} else {
-		STACK[TOP].type = JS_TMEMSTR;
+		STACK[TOP].t.type = JS_TMEMSTR;
 		STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
 	}
 	++TOP;
@@ -153,7 +153,7 @@
 void js_pushliteral(js_State *J, const char *v)
 {
 	CHECKSTACK(1);
-	STACK[TOP].type = JS_TLITSTR;
+	STACK[TOP].t.type = JS_TLITSTR;
 	STACK[TOP].u.litstr = v;
 	++TOP;
 }
@@ -161,7 +161,7 @@
 void js_pushobject(js_State *J, js_Object *v)
 {
 	CHECKSTACK(1);
-	STACK[TOP].type = JS_TOBJECT;
+	STACK[TOP].t.type = JS_TOBJECT;
 	STACK[TOP].u.object = v;
 	++TOP;
 }
@@ -177,7 +177,7 @@
 	if (BOT > 0)
 		STACK[TOP] = STACK[BOT-1];
 	else
-		STACK[TOP].type = JS_TUNDEFINED;
+		STACK[TOP].t.type = JS_TUNDEFINED;
 	++TOP;
 }
 
@@ -192,7 +192,7 @@
 
 static js_Value *stackidx(js_State *J, int idx)
 {
-	static js_Value undefined = { {0}, {0}, JS_TUNDEFINED };
+	static js_Value undefined = { { {0}, JS_TUNDEFINED } };
 	idx = idx < 0 ? TOP + idx : BOT + idx;
 	if (idx < 0 || idx >= TOP)
 		return &undefined;
@@ -204,20 +204,20 @@
 	return stackidx(J, idx);
 }
 
-int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->type != JS_TUNDEFINED; }
-int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TUNDEFINED; }
-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_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; }
-int js_iscoercible(js_State *J, int idx) { js_Value *v = stackidx(J, idx); return v->type != JS_TUNDEFINED && v->type != JS_TNULL; }
+int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TUNDEFINED; }
+int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TUNDEFINED; }
+int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNULL; }
+int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TBOOLEAN; }
+int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNUMBER; }
+int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->t.type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; }
+int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TOBJECT; }
+int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TOBJECT; }
+int js_iscoercible(js_State *J, int idx) { js_Value *v = stackidx(J, idx); return v->t.type != JS_TUNDEFINED && v->t.type != JS_TNULL; }
 
 int js_iscallable(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TOBJECT)
+	if (v->t.type == JS_TOBJECT)
 		return v->u.object->type == JS_CFUNCTION ||
 			v->u.object->type == JS_CSCRIPT ||
 			v->u.object->type == JS_CCFUNCTION;
@@ -227,19 +227,19 @@
 int js_isarray(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	return v->type == JS_TOBJECT && v->u.object->type == JS_CARRAY;
+	return v->t.type == JS_TOBJECT && v->u.object->type == JS_CARRAY;
 }
 
 int js_isregexp(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	return v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP;
+	return v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP;
 }
 
 int js_isuserdata(js_State *J, int idx, const char *tag)
 {
 	js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
+	if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
 		return !strcmp(tag, v->u.object->u.user.tag);
 	return 0;
 }
@@ -247,13 +247,13 @@
 int js_iserror(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	return v->type == JS_TOBJECT && v->u.object->type == JS_CERROR;
+	return v->t.type == JS_TOBJECT && v->u.object->type == JS_CERROR;
 }
 
 const char *js_typeof(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	switch (v->type) {
+	switch (v->t.type) {
 	default:
 	case JS_TSHRSTR: return "string";
 	case JS_TUNDEFINED: return "undefined";
@@ -272,7 +272,7 @@
 int js_type(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	switch (v->type) {
+	switch (v->t.type) {
 	default:
 	case JS_TSHRSTR: return JS_ISSTRING;
 	case JS_TUNDEFINED: return JS_ISUNDEFINED;
@@ -341,7 +341,7 @@
 js_Regexp *js_toregexp(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP)
+	if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP)
 		return &v->u.object->u.r;
 	js_typeerror(J, "not a regexp");
 }
@@ -349,7 +349,7 @@
 void *js_touserdata(js_State *J, int idx, const char *tag)
 {
 	js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
+	if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
 		if (!strcmp(tag, v->u.object->u.user.tag))
 			return v->u.object->u.user.data;
 	js_typeerror(J, "not a %s", tag);
@@ -358,9 +358,9 @@
 static js_Object *jsR_tofunction(js_State *J, int idx)
 {
 	js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TUNDEFINED || v->type == JS_TNULL)
+	if (v->t.type == JS_TUNDEFINED || v->t.type == JS_TNULL)
 		return NULL;
-	if (v->type == JS_TOBJECT)
+	if (v->t.type == JS_TOBJECT)
 		if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
 			return v->u.object;
 	js_typeerror(J, "not a function");
@@ -912,7 +912,7 @@
 	js_Value *v = stackidx(J, -1);
 	const char *s;
 	char buf[32];
-	switch (v->type) {
+	switch (v->t.type) {
 	case JS_TUNDEFINED: s = "_Undefined"; break;
 	case JS_TNULL: s = "_Null"; break;
 	case JS_TBOOLEAN:
@@ -1457,7 +1457,7 @@
 
 static void js_dumpvalue(js_State *J, js_Value v)
 {
-	switch (v.type) {
+	switch (v.t.type) {
 	case JS_TUNDEFINED: printf("undefined"); break;
 	case JS_TNULL: printf("null"); break;
 	case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
@@ -1537,7 +1537,7 @@
 static int jsR_isindex(js_State *J, int idx, int *k)
 {
 	js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TNUMBER) {
+	if (v->t.type == JS_TNUMBER) {
 		*k = v->u.number;
 		return *k == v->u.number && *k >= 0;
 	}
--- a/jsstate.c
+++ b/jsstate.c
@@ -5,7 +5,7 @@
 
 static int js_ptry(js_State *J) {
 	if (J->trytop == JS_TRYLIMIT) {
-		J->stack[J->top].type = JS_TLITSTR;
+		J->stack[J->top].t.type = JS_TLITSTR;
 		J->stack[J->top].u.litstr = "exception stack overflow";
 		++J->top;
 		return 1;
@@ -285,7 +285,7 @@
 	js_State *J;
 
 	assert(sizeof(js_Value) == 16);
-	assert(soffsetof(js_Value, type) == 15);
+	assert(soffsetof(js_Value, t.type) == 15);
 
 	if (!alloc)
 		alloc = js_defaultalloc;
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -1,8 +1,8 @@
 #include "jsi.h"
 #include "utf.h"
 
-#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 : "")
+#define JSV_ISSTRING(v) (v->t.type==JS_TSHRSTR || v->t.type==JS_TMEMSTR || v->t.type==JS_TLITSTR)
+#define JSV_TOSTRING(v) (v->t.type==JS_TSHRSTR ? v->u.shrstr : v->t.type==JS_TLITSTR ? v->u.litstr : v->t.type==JS_TMEMSTR ? v->u.memstr->p : "")
 
 double js_strtol(const char *s, char **p, int base)
 {
@@ -118,7 +118,7 @@
 {
 	js_Object *obj;
 
-	if (v->type != JS_TOBJECT)
+	if (v->t.type != JS_TOBJECT)
 		return;
 
 	obj = v->u.object;
@@ -143,7 +143,7 @@
 	if (J->strict)
 		js_typeerror(J, "cannot convert object to primitive");
 
-	v->type = JS_TLITSTR;
+	v->t.type = JS_TLITSTR;
 	v->u.litstr = "[object]";
 	return;
 }
@@ -151,7 +151,7 @@
 /* ToBoolean() on a value */
 int jsV_toboolean(js_State *J, js_Value *v)
 {
-	switch (v->type) {
+	switch (v->t.type) {
 	default:
 	case JS_TSHRSTR: return v->u.shrstr[0] != 0;
 	case JS_TUNDEFINED: return 0;
@@ -239,7 +239,7 @@
 /* ToNumber() on a value */
 double jsV_tonumber(js_State *J, js_Value *v)
 {
-	switch (v->type) {
+	switch (v->t.type) {
 	default:
 	case JS_TSHRSTR: return jsV_stringtonumber(J, v->u.shrstr);
 	case JS_TUNDEFINED: return NAN;
@@ -324,7 +324,7 @@
 {
 	char buf[32];
 	const char *p;
-	switch (v->type) {
+	switch (v->t.type) {
 	default:
 	case JS_TSHRSTR: return v->u.shrstr;
 	case JS_TUNDEFINED: return "undefined";
@@ -336,15 +336,15 @@
 		p = jsV_numbertostring(J, buf, v->u.number);
 		if (p == buf) {
 			int n = strlen(p);
-			if (n <= soffsetof(js_Value, type)) {
+			if (n <= soffsetof(js_Value, t.type)) {
 				char *s = v->u.shrstr;
 				while (n--) *s++ = *p++;
 				*s = 0;
-				v->type = JS_TSHRSTR;
+				v->t.type = JS_TSHRSTR;
 				return v->u.shrstr;
 			} else {
 				v->u.memstr = jsV_newmemstring(J, p, n);
-				v->type = JS_TMEMSTR;
+				v->t.type = JS_TMEMSTR;
 				return v->u.memstr->p;
 			}
 		}
@@ -389,7 +389,7 @@
 js_Object *jsV_toobject(js_State *J, js_Value *v)
 {
 	js_Object *o;
-	switch (v->type) {
+	switch (v->t.type) {
 	default:
 	case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object");
 	case JS_TNULL: js_typeerror(J, "cannot convert null to object");
@@ -400,7 +400,7 @@
 	case JS_TBOOLEAN: o = jsV_newboolean(J, v->u.boolean); break;
 	case JS_TNUMBER: o = jsV_newnumber(J, v->u.number); break;
 	}
-	v->type = JS_TOBJECT;
+	v->t.type = JS_TOBJECT;
 	v->u.object = o;
 	return o;
 }
@@ -624,38 +624,38 @@
 retry:
 	if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
 		return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
-	if (x->type == y->type) {
-		if (x->type == JS_TUNDEFINED) return 1;
-		if (x->type == JS_TNULL) return 1;
-		if (x->type == JS_TNUMBER) return x->u.number == y->u.number;
-		if (x->type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
-		if (x->type == JS_TOBJECT) return x->u.object == y->u.object;
+	if (x->t.type == y->t.type) {
+		if (x->t.type == JS_TUNDEFINED) return 1;
+		if (x->t.type == JS_TNULL) return 1;
+		if (x->t.type == JS_TNUMBER) return x->u.number == y->u.number;
+		if (x->t.type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
+		if (x->t.type == JS_TOBJECT) return x->u.object == y->u.object;
 		return 0;
 	}
 
-	if (x->type == JS_TNULL && y->type == JS_TUNDEFINED) return 1;
-	if (x->type == JS_TUNDEFINED && y->type == JS_TNULL) return 1;
+	if (x->t.type == JS_TNULL && y->t.type == JS_TUNDEFINED) return 1;
+	if (x->t.type == JS_TUNDEFINED && y->t.type == JS_TNULL) return 1;
 
-	if (x->type == JS_TNUMBER && JSV_ISSTRING(y))
+	if (x->t.type == JS_TNUMBER && JSV_ISSTRING(y))
 		return x->u.number == jsV_tonumber(J, y);
-	if (JSV_ISSTRING(x) && y->type == JS_TNUMBER)
+	if (JSV_ISSTRING(x) && y->t.type == JS_TNUMBER)
 		return jsV_tonumber(J, x) == y->u.number;
 
-	if (x->type == JS_TBOOLEAN) {
-		x->type = JS_TNUMBER;
+	if (x->t.type == JS_TBOOLEAN) {
+		x->t.type = JS_TNUMBER;
 		x->u.number = x->u.boolean ? 1 : 0;
 		goto retry;
 	}
-	if (y->type == JS_TBOOLEAN) {
-		y->type = JS_TNUMBER;
+	if (y->t.type == JS_TBOOLEAN) {
+		y->t.type = JS_TNUMBER;
 		y->u.number = y->u.boolean ? 1 : 0;
 		goto retry;
 	}
-	if ((JSV_ISSTRING(x) || x->type == JS_TNUMBER) && y->type == JS_TOBJECT) {
+	if ((JSV_ISSTRING(x) || x->t.type == JS_TNUMBER) && y->t.type == JS_TOBJECT) {
 		jsV_toprimitive(J, y, JS_HNONE);
 		goto retry;
 	}
-	if (x->type == JS_TOBJECT && (JSV_ISSTRING(y) || y->type == JS_TNUMBER)) {
+	if (x->t.type == JS_TOBJECT && (JSV_ISSTRING(y) || y->t.type == JS_TNUMBER)) {
 		jsV_toprimitive(J, x, JS_HNONE);
 		goto retry;
 	}
@@ -671,11 +671,11 @@
 	if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
 		return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
 
-	if (x->type != y->type) return 0;
-	if (x->type == JS_TUNDEFINED) return 1;
-	if (x->type == JS_TNULL) return 1;
-	if (x->type == JS_TNUMBER) return x->u.number == y->u.number;
-	if (x->type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
-	if (x->type == JS_TOBJECT) return x->u.object == y->u.object;
+	if (x->t.type != y->t.type) return 0;
+	if (x->t.type == JS_TUNDEFINED) return 1;
+	if (x->t.type == JS_TNULL) return 1;
+	if (x->t.type == JS_TNUMBER) return x->u.number == y->u.number;
+	if (x->t.type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
+	if (x->t.type == JS_TOBJECT) return x->u.object == y->u.object;
 	return 0;
 }