shithub: libmujs

Download patch

ref: b262cc3673965e53ed73606668dedd3acb045881
parent: 759acbd4ddaa706d2f457616d4e5ea32ea4127f6
author: Tor Andersson <tor@ccxvii.net>
date: Mon Jan 20 12:44:56 EST 2014

Rename many jsR functions to jsV.

Danger! jsV work on values internally without the stack.

Also rearrange how C-constructors are created.

--- a/js.h
+++ b/js.h
@@ -56,7 +56,12 @@
 
 void js_newobject(js_State *J);
 void js_newarray(js_State *J);
+void js_newboolean(js_State *J, int v);
+void js_newnumber(js_State *J, double v);
+void js_newstring(js_State *J, const char *v);
+void js_newerror(js_State *J, const char *message);
 void js_newcfunction(js_State *J, js_CFunction fun, int length);
+void js_newcconstructor(js_State *J, js_CFunction fun, js_CFunction con);
 
 int js_isundefined(js_State *J, int idx);
 int js_isnull(js_State *J, int idx);
--- a/jsbarray.c
+++ b/jsbarray.c
@@ -1,5 +1,5 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int jsB_Array(js_State *J, int n) { return 0; }
@@ -13,23 +13,14 @@
 		return 1;
 	}
 	js_pushboolean(J, 0);
-	return 0;
+	return 1;
 }
 
 void jsB_initarray(js_State *J)
 {
-	js_pushobject(J, jsR_newcconstructor(J, jsB_Array, jsB_new_Array));
+	js_pushobject(J, J->Array_prototype);
+	js_newcconstructor(J, jsB_Array, jsB_new_Array);
 	{
-		jsB_propn(J, "length", 1);
-
-		js_pushobject(J, J->Array_prototype);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-			jsB_propn(J, "length", 0);
-		}
-		js_setproperty(J, -2, "prototype");
-
 		/* ECMA-262-5 */
 		jsB_propf(J, "isArray", A_isArray, 1);
 	}
--- a/jsbboolean.c
+++ b/jsbboolean.c
@@ -1,10 +1,10 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int jsB_new_Boolean(js_State *J, int n)
 {
-	js_pushobject(J, jsR_newboolean(J, js_toboolean(J, 0)));
+	js_newboolean(J, js_toboolean(J, 0));
 	return 1;
 }
 
@@ -34,17 +34,11 @@
 {
 	J->Boolean_prototype->u.boolean = 0;
 
-	js_pushobject(J, jsR_newcconstructor(J, jsB_Boolean, jsB_new_Boolean));
+	js_pushobject(J, J->Boolean_prototype);
 	{
-		jsB_propn(J, "length", 1);
-		js_pushobject(J, J->Boolean_prototype);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-			jsB_propf(J, "toString", Bp_toString, 0);
-			jsB_propf(J, "valueOf", Bp_valueOf, 0);
-		}
-		js_setproperty(J, -2, "prototype");
+		jsB_propf(J, "toString", Bp_toString, 0);
+		jsB_propf(J, "valueOf", Bp_valueOf, 0);
 	}
+	js_newcconstructor(J, jsB_Boolean, jsB_new_Boolean);
 	js_setglobal(J, "Boolean");
 }
--- a/jsberror.c
+++ b/jsberror.c
@@ -1,5 +1,5 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int Ep_toString(js_State *J, int n)
@@ -17,7 +17,7 @@
 
 #define DECL(NAME) \
 	static int jsB_new_##NAME(js_State *J, int n) { \
-		js_pushobject(J, jsR_newobject(J, JS_CERROR, J->NAME##_prototype)); \
+		js_pushobject(J, jsV_newobject(J, JS_CERROR, J->NAME##_prototype)); \
 		if (n > 0) { \
 			js_pushstring(J, js_tostring(J, 0)); \
 			js_setproperty(J, -2, "message"); \
@@ -25,7 +25,7 @@
 		return 1; \
 	} \
 	static int jsB_##NAME(js_State *J, int n) { \
-		js_pushobject(J, jsR_newobject(J, JS_CERROR, J->NAME##_prototype)); \
+		js_pushobject(J, jsV_newobject(J, JS_CERROR, J->NAME##_prototype)); \
 		if (n > 1) { \
 			js_pushstring(J, js_tostring(J, 1)); \
 			js_setproperty(J, -2, "message"); \
@@ -33,23 +33,17 @@
 		return 1; \
 	} \
 	static void jsB_init##NAME(js_State *J) { \
-		js_pushobject(J, jsR_newcconstructor(J, jsB_##NAME, jsB_new_##NAME)); \
+		js_pushobject(J, J->NAME##_prototype); \
 		{ \
-			jsB_propn(J, "length", 1); \
-			js_pushobject(J, J->NAME##_prototype); \
-			{ \
-				js_copy(J, -2); \
-				js_setproperty(J, -2, "constructor"); \
-				jsB_props(J, "name", STR(NAME)); \
-				jsB_props(J, "message", "an error has occurred"); \
-				jsB_propf(J, "toString", Ep_toString, 0); \
-			} \
-			js_setproperty(J, -2, "prototype"); \
+			jsB_props(J, "name", STR(NAME)); \
+			jsB_props(J, "message", "an error has occurred"); \
+			jsB_propf(J, "toString", Ep_toString, 0); \
 		} \
+		js_newcconstructor(J, jsB_##NAME, jsB_new_##NAME); \
 		js_setglobal(J, STR(NAME)); \
 	} \
 	void jsR_throw##NAME(js_State *J, const char *message) { \
-		js_pushobject(J, jsR_newobject(J, JS_CERROR, J->NAME##_prototype)); \
+		js_pushobject(J, jsV_newobject(J, JS_CERROR, J->NAME##_prototype)); \
 		js_pushstring(J, message); \
 		js_setproperty(J, -2, "message"); \
 		js_throw(J); \
--- a/jsbfunction.c
+++ b/jsbfunction.c
@@ -1,6 +1,6 @@
 #include "jsi.h"
 #include "jscompile.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int jsB_new_Function(js_State *J, int n) { return 0; }
@@ -91,19 +91,12 @@
 	J->Function_prototype->u.c.function = jsB_Function_prototype;
 	J->Function_prototype->u.c.constructor = NULL;
 
-	js_pushobject(J, jsR_newcconstructor(J, jsB_Function, jsB_new_Function));
+	js_pushobject(J, J->Function_prototype);
 	{
-		jsB_propn(J, "length", 1);
-
-		js_pushobject(J, J->Function_prototype);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-			jsB_propf(J, "toString", Fp_toString, 2);
-			jsB_propf(J, "apply", Fp_apply, 2);
-			jsB_propf(J, "call", Fp_call, 1);
-		}
-		js_setproperty(J, -2, "prototype");
+		jsB_propf(J, "toString", Fp_toString, 2);
+		jsB_propf(J, "apply", Fp_apply, 2);
+		jsB_propf(J, "call", Fp_call, 1);
 	}
+	js_newcconstructor(J, jsB_Function, jsB_new_Function);
 	js_setglobal(J, "Function");
 }
--- a/jsbmath.c
+++ b/jsbmath.c
@@ -1,5 +1,5 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int Math_abs(js_State *J, int nargs) {
@@ -77,7 +77,7 @@
 
 void jsB_initmath(js_State *J)
 {
-	js_pushobject(J, jsR_newobject(J, JS_CMATH, J->Object_prototype));
+	js_pushobject(J, jsV_newobject(J, JS_CMATH, J->Object_prototype));
 	{
 		jsB_propn(J, "E", M_E);
 		jsB_propn(J, "LN10", M_LN10);
--- a/jsbnumber.c
+++ b/jsbnumber.c
@@ -1,10 +1,10 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int jsB_new_Number(js_State *J, int n)
 {
-	js_pushobject(J, jsR_newnumber(J, n > 0 ? js_tonumber(J, 0) : 0));
+	js_newnumber(J, n > 0 ? js_tonumber(J, 0) : 0);
 	return 1;
 }
 
@@ -26,7 +26,7 @@
 {
 	js_Object *self = js_toobject(J, 0);
 	if (self->type != JS_CNUMBER) jsR_throwTypeError(J, "not a number");
-	js_pushliteral(J, jsR_stringfromnumber(J, self->u.number));
+	js_pushliteral(J, jsV_numbertostring(J, self->u.number));
 	return 1;
 }
 
@@ -67,23 +67,17 @@
 {
 	J->Number_prototype->u.number = 0;
 
-	js_pushobject(J, jsR_newcconstructor(J, jsB_Number, jsB_new_Number));
+	js_pushobject(J, J->Number_prototype);
 	{
-		jsB_propn(J, "length", 1);
-
-		js_pushobject(J, J->Number_prototype);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-			jsB_propf(J, "valueOf", Np_valueOf, 0);
-			jsB_propf(J, "toString", Np_toString, 0);
-			jsB_propf(J, "toLocaleString", Np_toString, 0);
-			jsB_propf(J, "toFixed", Np_toFixed, 1);
-			jsB_propf(J, "toExponential", Np_toExponential, 1);
-			jsB_propf(J, "toPrecision", Np_toPrecision, 1);
-		}
-		js_setproperty(J, -2, "prototype");
-
+		jsB_propf(J, "valueOf", Np_valueOf, 0);
+		jsB_propf(J, "toString", Np_toString, 0);
+		jsB_propf(J, "toLocaleString", Np_toString, 0);
+		jsB_propf(J, "toFixed", Np_toFixed, 1);
+		jsB_propf(J, "toExponential", Np_toExponential, 1);
+		jsB_propf(J, "toPrecision", Np_toPrecision, 1);
+	}
+	js_newcconstructor(J, jsB_Number, jsB_new_Number);
+	{
 		jsB_propn(J, "MAX_VALUE", DBL_MAX);
 		jsB_propn(J, "MIN_VALUE", DBL_MIN);
 		jsB_propn(J, "NaN", NAN);
--- a/jsbobject.c
+++ b/jsbobject.c
@@ -1,5 +1,5 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int jsB_new_Object(js_State *J, int n)
@@ -51,7 +51,7 @@
 {
 	js_Object *self = js_toobject(J, 0);
 	const char *name = js_tostring(J, 1);
-	js_Property *ref = jsR_getownproperty(J, self, name);
+	js_Property *ref = jsV_getownproperty(J, self, name);
 	js_pushboolean(J, ref != NULL);
 	return 1;
 }
@@ -77,28 +77,22 @@
 {
 	js_Object *self = js_toobject(J, 0);
 	const char *name = js_tostring(J, 1);
-	js_Property *ref = jsR_getownproperty(J, self, name);
-	js_pushboolean(J, ref && !ref->dontenum);
+	js_Property *ref = jsV_getownproperty(J, self, name);
+	js_pushboolean(J, ref && !(ref->atts & JS_DONTENUM));
 	return 1;
 }
 
 void jsB_initobject(js_State *J)
 {
-	js_pushobject(J, jsR_newcconstructor(J, jsB_Object, jsB_new_Object));
+	js_pushobject(J, J->Object_prototype);
 	{
-		jsB_propn(J, "length", 1);
-		js_pushobject(J, J->Object_prototype);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-			jsB_propf(J, "toString", Op_toString, 0);
-			jsB_propf(J, "toLocaleString", Op_toString, 0);
-			jsB_propf(J, "valueOf", Op_valueOf, 0);
-			jsB_propf(J, "hasOwnProperty", Op_hasOwnProperty, 1);
-			jsB_propf(J, "isPrototypeOf", Op_isPrototypeOf, 1);
-			jsB_propf(J, "propertyIsEnumerable", Op_propertyIsEnumerable, 1);
-		}
-		js_setproperty(J, -2, "prototype");
+		jsB_propf(J, "toString", Op_toString, 0);
+		jsB_propf(J, "toLocaleString", Op_toString, 0);
+		jsB_propf(J, "valueOf", Op_valueOf, 0);
+		jsB_propf(J, "hasOwnProperty", Op_hasOwnProperty, 1);
+		jsB_propf(J, "isPrototypeOf", Op_isPrototypeOf, 1);
+		jsB_propf(J, "propertyIsEnumerable", Op_propertyIsEnumerable, 1);
 	}
+	js_newcconstructor(J, jsB_Object, jsB_new_Object);
 	js_setglobal(J, "Object");
 }
--- a/jsbstring.c
+++ b/jsbstring.c
@@ -1,11 +1,11 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 #include "jsutf.h"
 
 static int jsB_new_String(js_State *J, int n)
 {
-	js_pushobject(J, jsR_newstring(J, n > 0 ? js_tostring(J, 0) : ""));
+	js_newstring(J, n > 0 ? js_tostring(J, 0) : "");
 	return 1;
 }
 
@@ -93,21 +93,15 @@
 {
 	J->String_prototype->u.string = "";
 
-	js_pushobject(J, jsR_newcconstructor(J, jsB_String, jsB_new_String));
+	js_pushobject(J, J->String_prototype);
 	{
-		jsB_propn(J, "length", 1);
-
-		js_pushobject(J, J->String_prototype);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-			jsB_propf(J, "toString", Sp_toString, 0);
-			jsB_propf(J, "valueOf", Sp_valueOf, 0);
-			jsB_propf(J, "charAt", Sp_charAt, 1);
-			jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1);
-		}
-		js_setproperty(J, -2, "prototype");
-
+		jsB_propf(J, "toString", Sp_toString, 0);
+		jsB_propf(J, "valueOf", Sp_valueOf, 0);
+		jsB_propf(J, "charAt", Sp_charAt, 1);
+		jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1);
+	}
+	js_newcconstructor(J, jsB_String, jsB_new_String);
+	{
 		jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
 	}
 	js_setglobal(J, "String");
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -1,5 +1,5 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsbuiltin.h"
 
 static int jsB_print(js_State *J, int argc)
@@ -42,7 +42,7 @@
 static int jsB_parseFloat(js_State *J, int argc)
 {
 	const char *s = js_tostring(J, 1);
-	js_pushnumber(J, jsR_numberfromstring(J, s));
+	js_pushnumber(J, strtod(s, NULL));
 	return 1;
 }
 
@@ -87,21 +87,21 @@
 void jsB_init(js_State *J)
 {
 	/* Create the prototype objects here, before the constructors */
-	J->Object_prototype = jsR_newobject(J, JS_COBJECT, NULL);
-	J->Array_prototype = jsR_newobject(J, JS_CARRAY, J->Object_prototype);
-	J->Function_prototype = jsR_newobject(J, JS_CCFUNCTION, J->Object_prototype);
-	J->Boolean_prototype = jsR_newobject(J, JS_CBOOLEAN, J->Object_prototype);
-	J->Number_prototype = jsR_newobject(J, JS_CNUMBER, J->Object_prototype);
-	J->String_prototype = jsR_newobject(J, JS_CSTRING, J->Object_prototype);
+	J->Object_prototype = jsV_newobject(J, JS_COBJECT, NULL);
+	J->Array_prototype = jsV_newobject(J, JS_CARRAY, J->Object_prototype);
+	J->Function_prototype = jsV_newobject(J, JS_CCFUNCTION, J->Object_prototype);
+	J->Boolean_prototype = jsV_newobject(J, JS_CBOOLEAN, J->Object_prototype);
+	J->Number_prototype = jsV_newobject(J, JS_CNUMBER, J->Object_prototype);
+	J->String_prototype = jsV_newobject(J, JS_CSTRING, J->Object_prototype);
 
 	/* All the native error types */
-	J->Error_prototype = jsR_newobject(J, JS_CERROR, J->Object_prototype);
-	J->EvalError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
-	J->RangeError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
-	J->ReferenceError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
-	J->SyntaxError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
-	J->TypeError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
-	J->URIError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+	J->Error_prototype = jsV_newobject(J, JS_CERROR, J->Object_prototype);
+	J->EvalError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+	J->RangeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+	J->ReferenceError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+	J->SyntaxError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+	J->TypeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+	J->URIError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
 
 	/* Create the constructors and fill out the prototype objects */
 	jsB_initobject(J);
--- a/jsdump.c
+++ b/jsdump.c
@@ -1,7 +1,7 @@
 #include "jsi.h"
 #include "jsparse.h"
 #include "jscompile.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 
 #include <assert.h>
 
@@ -29,16 +29,16 @@
 
 const char *jsP_aststring(js_AstType type)
 {
-	if (type < 0 || type > nelem(astname))
+	if (type > nelem(astname))
 		return "<unknown>";
 	return astname[type];
 }
 
-const char *jsC_opcodestring(int type)
+const char *jsC_opcodestring(int opcode)
 {
-	if (type < 0 || type > nelem(opname))
+	if (opcode < 0 || opcode > nelem(opname))
 		return "<unknown>";
-	return opname[type];
+	return opname[opcode];
 }
 
 static inline void pc(int c)
--- a/jsgc.c
+++ b/jsgc.c
@@ -1,6 +1,6 @@
 #include "jsi.h"
 #include "jscompile.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsrun.h"
 
 static void jsG_markobject(js_State *J, int mark, js_Object *obj);
@@ -98,9 +98,25 @@
 
 	mark = J->gcmark = J->gcmark == 1 ? 2 : 1;
 
+	jsG_markobject(J, mark, J->Object_prototype);
+	jsG_markobject(J, mark, J->Array_prototype);
+	jsG_markobject(J, mark, J->Function_prototype);
+	jsG_markobject(J, mark, J->Boolean_prototype);
+	jsG_markobject(J, mark, J->Number_prototype);
+	jsG_markobject(J, mark, J->String_prototype);
+
+	jsG_markobject(J, mark, J->Error_prototype);
+	jsG_markobject(J, mark, J->EvalError_prototype);
+	jsG_markobject(J, mark, J->RangeError_prototype);
+	jsG_markobject(J, mark, J->ReferenceError_prototype);
+	jsG_markobject(J, mark, J->SyntaxError_prototype);
+	jsG_markobject(J, mark, J->TypeError_prototype);
+	jsG_markobject(J, mark, J->URIError_prototype);
+
+	jsG_markobject(J, mark, J->G);
+
 	jsG_markstack(J, mark);
-	if (J->E)
-		jsG_markenvironment(J, mark, J->E);
+	jsG_markenvironment(J, mark, J->E);
 
 	prevnextenv = &J->gcenv;
 	for (env = J->gcenv; env; env = nextenv) {
--- a/jsi.h
+++ b/jsi.h
@@ -35,13 +35,9 @@
 JS_NORETURN void jsR_throwTypeError(js_State *J, const char *message);
 JS_NORETURN void jsR_throwURIError(js_State *J, const char *message);
 
-js_Object *jsR_newcconstructor(js_State *J, js_CFunction cfunction, js_CFunction cconstructor);
-
-const char *jsR_stringfromnumber(js_State *J, double number);
-double jsR_numberfromstring(js_State *J, const char *string);
-
 void js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
 void js_newscript(js_State *J, js_Function *function);
+
 void js_dup(js_State *J);
 void js_rot(js_State *J, int n);
 void js_rot2(js_State *J);
--- a/jsobject.c
+++ /dev/null
@@ -1,112 +1,0 @@
-#include "jsi.h"
-#include "jscompile.h"
-#include "jsobject.h"
-#include "jsrun.h"
-#include "jsutf.h"
-
-static js_Object *jsR_newfunction(js_State *J, js_Function *function, js_Environment *scope)
-{
-	js_Object *obj = jsR_newobject(J, JS_CFUNCTION, J->Function_prototype);
-	obj->u.f.function = function;
-	obj->u.f.scope = scope;
-	return obj;
-}
-
-static js_Object *jsR_newscript(js_State *J, js_Function *function)
-{
-	js_Object *obj = jsR_newobject(J, JS_CSCRIPT, NULL);
-	obj->u.f.function = function;
-	obj->u.f.scope = NULL;
-	return obj;
-}
-
-static js_Object *jsR_newcfunction(js_State *J, js_CFunction cfunction)
-{
-	js_Object *obj = jsR_newobject(J, JS_CCFUNCTION, J->Function_prototype);
-	obj->u.c.function = cfunction;
-	obj->u.c.constructor = NULL;
-	return obj;
-}
-
-js_Object *jsR_newcconstructor(js_State *J, js_CFunction cfunction, js_CFunction cconstructor)
-{
-	js_Object *obj = jsR_newobject(J, JS_CCFUNCTION, J->Function_prototype);
-	obj->u.c.function = cfunction;
-	obj->u.c.constructor = cconstructor;
-	return obj;
-}
-
-js_Object *jsR_newboolean(js_State *J, int v)
-{
-	js_Object *obj = jsR_newobject(J, JS_CBOOLEAN, J->Boolean_prototype);
-	obj->u.boolean = v;
-	return obj;
-}
-
-js_Object *jsR_newnumber(js_State *J, double v)
-{
-	js_Object *obj = jsR_newobject(J, JS_CNUMBER, J->Number_prototype);
-	obj->u.number = v;
-	return obj;
-}
-
-js_Object *jsR_newstring(js_State *J, const char *v)
-{
-	js_Object *obj = jsR_newobject(J, JS_CSTRING, J->String_prototype);
-	obj->u.string = v;
-	{
-		js_Property *ref;
-		ref = jsR_setproperty(J, obj, "length");
-		ref->value.type = JS_TNUMBER;
-		ref->value.u.number = utflen(v);
-		ref->readonly = 1;
-		ref->dontenum = 1;
-		ref->dontconf = 1;
-	}
-	return obj;
-}
-
-void js_newobject(js_State *J)
-{
-	js_pushobject(J, jsR_newobject(J, JS_COBJECT, J->Object_prototype));
-}
-
-void js_newarray(js_State *J)
-{
-	js_pushobject(J, jsR_newobject(J, JS_CARRAY, J->Array_prototype));
-}
-
-void js_newfunction(js_State *J, js_Function *F, js_Environment *scope)
-{
-	js_pushobject(J, jsR_newfunction(J, F, scope));
-	{
-		js_pushnumber(J, F->numparams);
-		js_setproperty(J, -2, "length");
-		js_newobject(J);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-		}
-		js_setproperty(J, -2, "prototype");
-	}
-}
-
-void js_newscript(js_State *J, js_Function *F)
-{
-	js_pushobject(J, jsR_newscript(J, F));
-}
-
-void js_newcfunction(js_State *J, js_CFunction fun, int length)
-{
-	js_pushobject(J, jsR_newcfunction(J, fun));
-	{
-		js_pushnumber(J, length);
-		js_setproperty(J, -2, "length");
-		js_newobject(J);
-		{
-			js_copy(J, -2);
-			js_setproperty(J, -2, "constructor");
-		}
-		js_setproperty(J, -2, "prototype");
-	}
-}
--- a/jsobject.h
+++ /dev/null
@@ -1,108 +1,0 @@
-#ifndef js_object_h
-#define js_object_h
-
-typedef enum js_Type js_Type;
-typedef enum js_Class js_Class;
-
-typedef struct js_Property js_Property;
-
-enum js_Type {
-	JS_TUNDEFINED,
-	JS_TNULL,
-	JS_TBOOLEAN,
-	JS_TNUMBER,
-	JS_TSTRING,
-	JS_TOBJECT,
-};
-
-enum js_Class {
-	JS_COBJECT,
-	JS_CARRAY,
-	JS_CFUNCTION,
-	JS_CSCRIPT, /* function created from global/eval code */
-	JS_CCFUNCTION, /* built-in function */
-	JS_CERROR,
-	JS_CBOOLEAN,
-	JS_CNUMBER,
-	JS_CSTRING,
-	JS_CREGEXP,
-	JS_CDATE,
-	JS_CMATH,
-};
-
-struct js_Value
-{
-	js_Type type;
-	union {
-		int boolean;
-		double number;
-		const char *string;
-		js_Object *object;
-	} u;
-};
-
-struct js_Object
-{
-	js_Class type;
-	js_Property *properties;
-	js_Object *prototype;
-	union {
-		int boolean;
-		double number;
-		const char *string;
-		struct {
-			js_Function *function;
-			js_Environment *scope;
-		} f;
-		struct {
-			js_CFunction function;
-			js_CFunction constructor;
-		} c;
-	} u;
-	js_Object *gcnext;
-	int gcmark;
-};
-
-struct js_Property
-{
-	const char *name;
-	js_Property *left, *right;
-	int level;
-	unsigned short readonly, dontenum, dontconf;
-	js_Value value;
-};
-
-js_Value js_tovalue(js_State *J, int idx);
-js_Value js_toprimitive(js_State *J, int idx, int hint);
-js_Object *js_toobject(js_State *J, int idx);
-
-void js_pushvalue(js_State *J, js_Value v);
-void js_pushobject(js_State *J, js_Object *v);
-
-/* jsvalue.c */
-int jsR_toboolean(js_State *J, const js_Value *v);
-double jsR_tonumber(js_State *J, const js_Value *v);
-const char *jsR_tostring(js_State *J, const js_Value *v);
-js_Object *jsR_toobject(js_State *J, const js_Value *v);
-js_Value jsR_toprimitive(js_State *J, const js_Value *v, int preferred);
-
-/* jsproperty.c */
-js_Object *jsR_newobject(js_State *J, js_Class type, js_Object *prototype);
-js_Property *jsR_getownproperty(js_State *J, js_Object *obj, const char *name);
-js_Property *jsR_getproperty(js_State *J, js_Object *obj, const char *name);
-js_Property *jsR_setproperty(js_State *J, js_Object *obj, const char *name);
-js_Property *jsR_nextproperty(js_State *J, js_Object *obj, const char *name);
-
-/* jsobject.c */
-js_Object *jsR_newboolean(js_State *J, int v);
-js_Object *jsR_newnumber(js_State *J, double v);
-js_Object *jsR_newstring(js_State *J, const char *v);
-
-/* jsrun.c */
-void jsR_pushobject(js_State *J, js_Object *v);
-js_Object *js_toobject(js_State *J, int idx);
-
-void js_dumpobject(js_State *J, js_Object *obj);
-void js_dumpvalue(js_State *J, js_Value v);
-
-#endif
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -1,5 +1,5 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 
 /*
 	Use an AA-tree to quickly look up properties in objects:
@@ -26,9 +26,7 @@
 	node->name = js_intern(J, name);
 	node->left = node->right = &sentinel;
 	node->level = 1;
-	node->readonly = 0;
-	node->dontenum = 0;
-	node->dontconf = 0;
+	node->atts = 0;
 	node->value.type = JS_TUNDEFINED;
 	node->value.u.number = 0;
 	return node;
@@ -131,7 +129,7 @@
 	return parent;
 }
 
-js_Object *jsR_newobject(js_State *J, js_Class type, js_Object *prototype)
+js_Object *jsV_newobject(js_State *J, js_Class type, js_Object *prototype)
 {
 	js_Object *obj = calloc(sizeof(js_Object), 1);
 	obj->gcmark = 0;
@@ -145,12 +143,12 @@
 	return obj;
 }
 
-js_Property *jsR_getownproperty(js_State *J, js_Object *obj, const char *name)
+js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
 {
 	return lookup(obj->properties, name);
 }
 
-js_Property *jsR_getproperty(js_State *J, js_Object *obj, const char *name)
+js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
 {
 	do {
 		js_Property *ref = lookup(obj->properties, name);
@@ -161,7 +159,7 @@
 	return NULL;
 }
 
-js_Property *jsR_setproperty(js_State *J, js_Object *obj, const char *name)
+js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
 {
 	js_Property *result;
 	obj->properties = insert(J, obj->properties, name, &result);
@@ -168,7 +166,7 @@
 	return result;
 }
 
-js_Property *jsR_nextproperty(js_State *J, js_Object *obj, const char *name)
+js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name)
 {
 	if (!name)
 		return lookupfirst(obj->properties);
--- a/jsrun.c
+++ b/jsrun.c
@@ -1,6 +1,6 @@
 #include "jsi.h"
 #include "jscompile.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsrun.h"
 
 static void jsR_run(js_State *J, js_Function *F);
@@ -145,42 +145,42 @@
 
 int js_toboolean(js_State *J, int idx)
 {
-	return jsR_toboolean(J, stackidx(J, idx));
+	return jsV_toboolean(J, stackidx(J, idx));
 }
 
 double js_tonumber(js_State *J, int idx)
 {
-	return jsR_tonumber(J, stackidx(J, idx));
+	return jsV_tonumber(J, stackidx(J, idx));
 }
 
 double js_tointeger(js_State *J, int idx)
 {
-	return tointeger(jsR_tonumber(J, stackidx(J, idx)));
+	return tointeger(jsV_tonumber(J, stackidx(J, idx)));
 }
 
 int js_toint32(js_State *J, int idx)
 {
-	return toint32(jsR_tonumber(J, stackidx(J, idx)));
+	return toint32(jsV_tonumber(J, stackidx(J, idx)));
 }
 
 unsigned int js_touint32(js_State *J, int idx)
 {
-	return touint32(jsR_tonumber(J, stackidx(J, idx)));
+	return touint32(jsV_tonumber(J, stackidx(J, idx)));
 }
 
 const char *js_tostring(js_State *J, int idx)
 {
-	return jsR_tostring(J, stackidx(J, idx));
+	return jsV_tostring(J, stackidx(J, idx));
 }
 
 js_Object *js_toobject(js_State *J, int idx)
 {
-	return jsR_toobject(J, stackidx(J, idx));
+	return jsV_toobject(J, stackidx(J, idx));
 }
 
 js_Value js_toprimitive(js_State *J, int idx, int hint)
 {
-	return jsR_toprimitive(J, stackidx(J, idx), hint);
+	return jsV_toprimitive(J, stackidx(J, idx), hint);
 }
 
 /* Stack manipulation */
@@ -256,7 +256,7 @@
 
 void js_getglobal(js_State *J, const char *name)
 {
-	js_Property *ref = jsR_getproperty(J, J->G, name);
+	js_Property *ref = jsV_getproperty(J, J->G, name);
 	if (ref)
 		js_pushvalue(J, ref->value);
 	else
@@ -265,7 +265,7 @@
 
 void js_setglobal(js_State *J, const char *name)
 {
-	js_Property *ref = jsR_setproperty(J, J->G, name);
+	js_Property *ref = jsV_setproperty(J, J->G, name);
 	if (ref)
 		ref->value = js_tovalue(J, -1);
 	js_pop(J, 1);
@@ -274,7 +274,7 @@
 void js_getownproperty(js_State *J, int idx, const char *name)
 {
 	js_Object *obj = js_toobject(J, idx);
-	js_Property *ref = jsR_getownproperty(J, obj, name);
+	js_Property *ref = jsV_getownproperty(J, obj, name);
 	if (ref)
 		js_pushvalue(J, ref->value);
 	else
@@ -284,7 +284,7 @@
 void js_getproperty(js_State *J, int idx, const char *name)
 {
 	js_Object *obj = js_toobject(J, idx);
-	js_Property *ref = jsR_getproperty(J, obj, name);
+	js_Property *ref = jsV_getproperty(J, obj, name);
 	if (ref)
 		js_pushvalue(J, ref->value);
 	else
@@ -294,7 +294,7 @@
 void js_setproperty(js_State *J, int idx, const char *name)
 {
 	js_Object *obj = js_toobject(J, idx);
-	js_Property *ref = jsR_setproperty(J, obj, name);
+	js_Property *ref = jsV_setproperty(J, obj, name);
 	if (ref)
 		ref->value = js_tovalue(J, -1);
 	js_pop(J, 1);
@@ -303,7 +303,7 @@
 int js_nextproperty(js_State *J, int idx)
 {
 	js_Object *obj = js_toobject(J, idx);
-	js_Property *ref = jsR_nextproperty(J, obj, js_tostring(J, -1));
+	js_Property *ref = jsV_nextproperty(J, obj, js_tostring(J, -1));
 	js_pop(J, 1);
 	if (ref) {
 		js_pushliteral(J, ref->name);
@@ -330,7 +330,7 @@
 
 static js_Property *js_decvar(js_State *J, const char *name)
 {
-	return jsR_setproperty(J, J->E->variables, name);
+	return jsV_setproperty(J, J->E->variables, name);
 }
 
 static js_Property *js_getvar(js_State *J, const char *name)
@@ -337,7 +337,7 @@
 {
 	js_Environment *E = J->E;
 	do {
-		js_Property *ref = jsR_getproperty(J, E->variables, name);
+		js_Property *ref = jsV_getproperty(J, E->variables, name);
 		if (ref)
 			return ref;
 		E = E->outer;
@@ -349,12 +349,12 @@
 {
 	js_Environment *E = J->E;
 	do {
-		js_Property *ref = jsR_getproperty(J, E->variables, name);
+		js_Property *ref = jsV_getproperty(J, E->variables, name);
 		if (ref)
 			return ref;
 		E = E->outer;
 	} while (E);
-	return jsR_setproperty(J, J->G, name);
+	return jsV_setproperty(J, J->G, name);
 }
 
 /* Function calls */
@@ -366,7 +366,7 @@
 
 	saveE = J->E;
 
-	J->E = jsR_newenvironment(J, jsR_newobject(J, JS_COBJECT, NULL), scope);
+	J->E = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope);
 	for (i = 0; i < n; i++) {
 		js_Property *ref = js_decvar(J, F->params[i]);
 		if (i < n)
@@ -439,7 +439,7 @@
 	js_pop(J, 1);
 
 	/* create a new object with above prototype, and shift it into the 'this' slot */
-	js_pushobject(J, jsR_newobject(J, JS_COBJECT, prototype));
+	js_pushobject(J, jsV_newobject(J, JS_COBJECT, prototype));
 	if (n > 0)
 		js_rot(J, n + 1);
 
@@ -585,7 +585,7 @@
 		case OP_IN:
 			str = js_tostring(J, -2);
 			obj = js_toobject(J, -1);
-			ref = jsR_getproperty(J, obj, str);
+			ref = jsV_getproperty(J, obj, str);
 			js_pop(J, 2);
 			js_pushboolean(J, ref != NULL);
 			break;
@@ -596,7 +596,7 @@
 			js_getproperty(J, -1, str);
 
 			obj = js_toobject(J, -2);
-			ref = jsR_getproperty(J, obj, str);
+			ref = jsV_getproperty(J, obj, str);
 			js_pop(J, 2);
 			if (ref)
 				js_pushvalue(J, ref->value);
@@ -607,7 +607,7 @@
 		case OP_SETPROP:
 			obj = js_toobject(J, -3);
 			str = js_tostring(J, -2);
-			ref = jsR_setproperty(J, obj, str);
+			ref = jsV_setproperty(J, obj, str);
 			if (ref)
 				ref->value = js_tovalue(J, -1);
 			js_rot3pop2(J);
@@ -622,10 +622,10 @@
 			else
 				str = js_tostring(J, -1);
 
-			ref = jsR_nextproperty(J, obj, str);
+			ref = jsV_nextproperty(J, obj, str);
 			if (!ref && obj->prototype) {
 				obj = obj->prototype;
-				ref = jsR_nextproperty(J, obj, NULL);
+				ref = jsV_nextproperty(J, obj, NULL);
 			}
 
 			js_pop(J, 2);
--- a/jsstate.c
+++ b/jsstate.c
@@ -1,7 +1,7 @@
 #include "jsi.h"
 #include "jsparse.h"
 #include "jscompile.h"
-#include "jsobject.h"
+#include "jsvalue.h"
 #include "jsrun.h"
 #include "jsbuiltin.h"
 
@@ -107,7 +107,7 @@
 
 	J->gcmark = 1;
 
-	J->G = jsR_newobject(J, JS_COBJECT, NULL);
+	J->G = jsV_newobject(J, JS_COBJECT, NULL);
 	J->E = jsR_newenvironment(J, J->G, NULL);
 
 	jsB_init(J);
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -1,24 +1,11 @@
 #include "jsi.h"
-#include "jsobject.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "jsutf.h"
 
-const char *jsR_stringfromnumber(js_State *J, double n)
+/* obj.toString() */
+static int jsV_toString(js_State *J, js_Object *obj)
 {
-	char buf[32];
-	if (isnan(n)) return "NaN";
-	if (isinf(n)) return n < 0 ? "-Infinity" : "Infinity";
-	if (n == 0) return "0";
-	sprintf(buf, "%.17g", n); /* DBL_DECIMAL_DIG == 17 */
-	return js_intern(J, buf);
-}
-
-double jsR_numberfromstring(js_State *J, const char *s)
-{
-	/* TODO: use lexer to parse string grammar */
-	return strtod(s, NULL);
-}
-
-static int jsR_toString(js_State *J, js_Object *obj)
-{
 	js_pushobject(J, obj);
 	js_getproperty(J, -1, "toString");
 	if (js_iscallable(J, -1)) {
@@ -33,7 +20,8 @@
 	return 0;
 }
 
-static int jsR_valueOf(js_State *J, js_Object *obj)
+/* obj.valueOf() */
+static int jsV_valueOf(js_State *J, js_Object *obj)
 {
 	js_pushobject(J, obj);
 	js_getproperty(J, -1, "valueOf");
@@ -49,7 +37,8 @@
 	return 0;
 }
 
-js_Value jsR_toprimitive(js_State *J, const js_Value *v, int preferred)
+/* ToPrimitive() on a value */
+js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred)
 {
 	js_Value vv;
 	js_Object *obj;
@@ -63,13 +52,13 @@
 		preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER;
 
 	if (preferred == JS_HSTRING) {
-		if (jsR_toString(J, obj) || jsR_valueOf(J, obj)) {
+		if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) {
 			vv = js_tovalue(J, -1);
 			js_pop(J, 1);
 			return vv;
 		}
 	} else {
-		if (jsR_valueOf(J, obj) || jsR_toString(J, obj)) {
+		if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) {
 			vv = js_tovalue(J, -1);
 			js_pop(J, 1);
 			return vv;
@@ -78,7 +67,8 @@
 	jsR_throwTypeError(J, "cannot convert object to primitive");
 }
 
-int jsR_toboolean(js_State *J, const js_Value *v)
+/* ToBoolean() on a value */
+int jsV_toboolean(js_State *J, const js_Value *v)
 {
 	switch (v->type) {
 	case JS_TUNDEFINED: return 0;
@@ -91,60 +81,199 @@
 	return 0;
 }
 
-double jsR_tonumber(js_State *J, const js_Value *v)
+/* ToNumber() on a string */
+double jsV_stringtonumber(js_State *J, const char *s)
 {
+	/* TODO: use lexer to parse string grammar */
+	return strtod(s, NULL);
+}
+
+/* ToNumber() on a value */
+double jsV_tonumber(js_State *J, const js_Value *v)
+{
 	switch (v->type) {
 	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_TSTRING: return jsR_numberfromstring(J, v->u.string);
+	case JS_TSTRING: return jsV_stringtonumber(J, v->u.string);
 	case JS_TOBJECT:
 		{
-			js_Value vv = jsR_toprimitive(J, v, JS_HNUMBER);
-			return jsR_tonumber(J, &vv);
+			js_Value vv = jsV_toprimitive(J, v, JS_HNUMBER);
+			return jsV_tonumber(J, &vv);
 		}
 	}
 	return 0;
 }
 
-const char *jsR_tostring(js_State *J, const js_Value *v)
+/* ToString() on a number */
+const char *jsV_numbertostring(js_State *J, double n)
 {
+	char buf[32];
+	if (isnan(n)) return "NaN";
+	if (isinf(n)) return n < 0 ? "-Infinity" : "Infinity";
+	if (n == 0) return "0";
+	sprintf(buf, "%.17g", n); /* DBL_DECIMAL_DIG == 17 */
+	return js_intern(J, buf);
+}
+
+/* ToString() on a value */
+const char *jsV_tostring(js_State *J, const js_Value *v)
+{
 	switch (v->type) {
 	case JS_TUNDEFINED: return "undefined";
 	case JS_TNULL: return "null";
 	case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
-	case JS_TNUMBER: return jsR_stringfromnumber(J, v->u.number);
+	case JS_TNUMBER: return jsV_numbertostring(J, v->u.number);
 	case JS_TSTRING: return v->u.string;
 	case JS_TOBJECT:
 		{
-			js_Value vv = jsR_toprimitive(J, v, JS_HSTRING);
-			return jsR_tostring(J, &vv);
+			js_Value vv = jsV_toprimitive(J, v, JS_HSTRING);
+			return jsV_tostring(J, &vv);
 		}
 	}
 	return "undefined";
 }
 
-js_Object *jsR_toobject(js_State *J, const js_Value *v)
+/* Objects */
+
+static js_Object *jsV_newboolean(js_State *J, int v)
 {
+	js_Object *obj = jsV_newobject(J, JS_CBOOLEAN, J->Boolean_prototype);
+	obj->u.boolean = v;
+	return obj;
+}
+
+static js_Object *jsV_newnumber(js_State *J, double v)
+{
+	js_Object *obj = jsV_newobject(J, JS_CNUMBER, J->Number_prototype);
+	obj->u.number = v;
+	return obj;
+}
+
+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_DONTDELETE;
+	}
+	return obj;
+}
+
+/* ToObject() on a value */
+js_Object *jsV_toobject(js_State *J, const js_Value *v)
+{
 	switch (v->type) {
 	case JS_TUNDEFINED: jsR_throwTypeError(J, "cannot convert undefined to object");
 	case JS_TNULL: jsR_throwTypeError(J, "cannot convert null to object");
-	case JS_TBOOLEAN: return jsR_newboolean(J, v->u.boolean);
-	case JS_TNUMBER: return jsR_newnumber(J, v->u.number);
-	case JS_TSTRING: return jsR_newstring(J, v->u.string);
+	case JS_TBOOLEAN: return jsV_newboolean(J, v->u.boolean);
+	case JS_TNUMBER: return jsV_newnumber(J, v->u.number);
+	case JS_TSTRING: return jsV_newstring(J, v->u.string);
 	case JS_TOBJECT: return v->u.object;
 	}
 	jsR_throwTypeError(J, "cannot convert value to object");
 }
 
+void js_newobject(js_State *J)
+{
+	js_pushobject(J, jsV_newobject(J, JS_COBJECT, J->Object_prototype));
+}
+
+void js_newarray(js_State *J)
+{
+	js_pushobject(J, jsV_newobject(J, JS_CARRAY, J->Array_prototype));
+}
+
+void js_newboolean(js_State *J, int v)
+{
+	js_pushobject(J, jsV_newboolean(J, v));
+}
+
+void js_newnumber(js_State *J, double v)
+{
+	js_pushobject(J, jsV_newnumber(J, v));
+}
+
+void js_newstring(js_State *J, const char *v)
+{
+	js_pushobject(J, jsV_newstring(J, v));
+}
+
+void js_newfunction(js_State *J, js_Function *fun, js_Environment *scope)
+{
+	js_Object *obj = jsV_newobject(J, JS_CFUNCTION, J->Function_prototype);
+	obj->u.f.function = fun;
+	obj->u.f.scope = scope;
+	js_pushobject(J, obj);
+	{
+		js_pushnumber(J, fun->numparams);
+		js_setproperty(J, -2, "length");
+		js_newobject(J);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+		}
+		js_setproperty(J, -2, "prototype");
+	}
+}
+
+void js_newscript(js_State *J, js_Function *fun)
+{
+	js_Object *obj = jsV_newobject(J, JS_CSCRIPT, NULL);
+	obj->u.f.function = fun;
+	obj->u.f.scope = NULL;
+	js_pushobject(J, obj);
+}
+
+void js_newcfunction(js_State *J, js_CFunction cfun, int length)
+{
+	js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
+	obj->u.c.function = cfun;
+	obj->u.c.constructor = NULL;
+	js_pushobject(J, obj);
+	{
+		js_pushnumber(J, length);
+		js_setproperty(J, -2, "length");
+		js_newobject(J);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+		}
+		js_setproperty(J, -2, "prototype");
+	}
+}
+
+/* prototype -- constructor */
+void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon)
+{
+	js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
+	obj->u.c.function = cfun;
+	obj->u.c.constructor = ccon;
+	js_pushobject(J, obj); /* proto obj */
+	{
+		js_pushnumber(J, 1);
+		js_setproperty(J, -2, "length");
+		js_rot2(J); /* obj proto */
+		js_copy(J, -2); /* obj proto obj */
+		js_setproperty(J, -2, "constructor");
+		js_setproperty(J, -2, "prototype");
+	}
+}
+
+/* Non-trivial operations on values. These are implemented using the stack. */
+
 void js_concat(js_State *J)
 {
 	js_Value va = js_toprimitive(J, -2, JS_HNONE);
 	js_Value vb = js_toprimitive(J, -1, JS_HNONE);
 	if (va.type == JS_TSTRING || vb.type == JS_TSTRING) {
-		const char *sa = jsR_tostring(J, &va);
-		const char *sb = jsR_tostring(J, &vb);
+		const char *sa = jsV_tostring(J, &va);
+		const char *sb = jsV_tostring(J, &vb);
 		char *sab = malloc(strlen(sa) + strlen(sb) + 1);
 		strcpy(sab, sa);
 		strcat(sab, sb);
@@ -152,8 +281,8 @@
 		js_pushstring(J, sab);
 		free(sab);
 	} else {
-		double x = jsR_tonumber(J, &va);
-		double y = jsR_tonumber(J, &vb);
+		double x = jsV_tonumber(J, &va);
+		double y = jsV_tonumber(J, &vb);
 		js_pop(J, 2);
 		js_pushnumber(J, x + y);
 	}
@@ -167,8 +296,8 @@
 	if (va.type == JS_TSTRING && vb.type == JS_TSTRING) {
 		return strcmp(va.u.string, va.u.string);
 	} else {
-		double x = jsR_tonumber(J, &va);
-		double y = jsR_tonumber(J, &vb);
+		double x = jsV_tonumber(J, &va);
+		double y = jsV_tonumber(J, &vb);
 		return x < y ? -1 : x > y ? 1 : 0;
 	}
 }
@@ -194,16 +323,16 @@
 	if (va.type == JS_TUNDEFINED && vb.type == JS_TNULL) return 1;
 
 	if (va.type == JS_TNUMBER && (vb.type == JS_TSTRING || vb.type == JS_TBOOLEAN))
-		return va.u.number == jsR_tonumber(J, &vb);
+		return va.u.number == jsV_tonumber(J, &vb);
 	if ((va.type == JS_TSTRING || va.type == JS_TBOOLEAN) && vb.type == JS_TNUMBER)
-		return jsR_tonumber(J, &va) == vb.u.number;
+		return jsV_tonumber(J, &va) == vb.u.number;
 
 	if ((va.type == JS_TSTRING || va.type == JS_TNUMBER) && vb.type == JS_TOBJECT) {
-		vb = jsR_toprimitive(J, &vb, JS_HNONE);
+		vb = jsV_toprimitive(J, &vb, JS_HNONE);
 		goto retry;
 	}
 	if (va.type == JS_TOBJECT && (vb.type == JS_TSTRING || vb.type == JS_TNUMBER)) {
-		va = jsR_toprimitive(J, &va, JS_HNONE);
+		va = jsV_toprimitive(J, &va, JS_HNONE);
 		goto retry;
 	}
 
--- /dev/null
+++ b/jsvalue.h
@@ -1,0 +1,103 @@
+#ifndef js_object_h
+#define js_object_h
+
+typedef enum js_Type js_Type;
+typedef enum js_Class js_Class;
+
+typedef struct js_Property js_Property;
+
+enum js_Type {
+	JS_TUNDEFINED,
+	JS_TNULL,
+	JS_TBOOLEAN,
+	JS_TNUMBER,
+	JS_TSTRING,
+	JS_TOBJECT,
+};
+
+enum js_Class {
+	JS_COBJECT,
+	JS_CARRAY,
+	JS_CFUNCTION,
+	JS_CSCRIPT, /* function created from global/eval code */
+	JS_CCFUNCTION, /* built-in function */
+	JS_CERROR,
+	JS_CBOOLEAN,
+	JS_CNUMBER,
+	JS_CSTRING,
+	JS_CREGEXP,
+	JS_CDATE,
+	JS_CMATH,
+};
+
+struct js_Value
+{
+	js_Type type;
+	union {
+		int boolean;
+		double number;
+		const char *string;
+		js_Object *object;
+	} u;
+};
+
+struct js_Object
+{
+	js_Class type;
+	js_Property *properties;
+	js_Object *prototype;
+	union {
+		int boolean;
+		double number;
+		const char *string;
+		struct {
+			js_Function *function;
+			js_Environment *scope;
+		} f;
+		struct {
+			js_CFunction function;
+			js_CFunction constructor;
+		} c;
+	} u;
+	js_Object *gcnext;
+	int gcmark;
+};
+
+struct js_Property
+{
+	const char *name;
+	js_Property *left, *right;
+	int level;
+	int atts;
+	js_Value value;
+};
+
+/* jsrun.c */
+js_Value js_tovalue(js_State *J, int idx);
+js_Value js_toprimitive(js_State *J, int idx, int hint);
+js_Object *js_toobject(js_State *J, int idx);
+void js_pushvalue(js_State *J, js_Value v);
+void js_pushobject(js_State *J, js_Object *v);
+
+/* jsvalue.c */
+int jsV_toboolean(js_State *J, const js_Value *v);
+double jsV_tonumber(js_State *J, const js_Value *v);
+const char *jsV_tostring(js_State *J, const js_Value *v);
+js_Object *jsV_toobject(js_State *J, const js_Value *v);
+js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred);
+
+const char *jsV_numbertostring(js_State *J, double number);
+double jsV_stringtonumber(js_State *J, const char *string);
+
+/* jsproperty.c */
+js_Object *jsV_newobject(js_State *J, js_Class type, js_Object *prototype);
+js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name);
+
+/* jsdump.c */
+void js_dumpobject(js_State *J, js_Object *obj);
+void js_dumpvalue(js_State *J, js_Value v);
+
+#endif