shithub: libmujs

Download patch

ref: 90b54a9eecd56972de90ed69e377b9469b0274de
parent: 4595872bb39a7216df82425b148bd1914520e1de
author: Tor Andersson <tor@ccxvii.net>
date: Fri Jan 24 12:11:49 EST 2014

Add userdata class.

js_newuserdata(J, tag, ptr) creates a userdata object, wrapping the ptr.
It takes the prototype for the new object from the top of the stack, just
like js_newcconstructor does.

js_touserdata(J, tag, idx) extracts the userdata pointer. Throws a
TypeError if the object is not a userdata object with a matching tag.

js_isuserdata(J, tag, idx) checks if a value on the stack is a userdata
object with a matching tag.

--- a/js.h
+++ b/js.h
@@ -111,7 +111,8 @@
 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);
+void js_newcconstructor(js_State *J, js_CFunction fun, js_CFunction con, int length);
+void js_newuserdata(js_State *J, const char *tag, void *data);
 
 int js_isundefined(js_State *J, int idx);
 int js_isnull(js_State *J, int idx);
@@ -121,10 +122,12 @@
 int js_isprimitive(js_State *J, int idx);
 int js_isobject(js_State *J, int idx);
 int js_iscallable(js_State *J, int idx);
+int js_isuserdata(js_State *J, const char *tag, int idx);
 
 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);
+void *js_touserdata(js_State *J, const char *tag, int idx);
 
 double js_tointeger(js_State *J, int idx);
 int js_toint32(js_State *J, int idx);
--- a/jsarray.c
+++ b/jsarray.c
@@ -19,7 +19,7 @@
 void jsB_initarray(js_State *J)
 {
 	js_pushobject(J, J->Array_prototype);
-	js_newcconstructor(J, jsB_Array, jsB_new_Array);
+	js_newcconstructor(J, jsB_Array, jsB_new_Array, 1);
 	{
 		/* ECMA-262-5 */
 		jsB_propf(J, "isArray", A_isArray, 1);
--- a/jsboolean.c
+++ b/jsboolean.c
@@ -39,6 +39,6 @@
 		jsB_propf(J, "toString", Bp_toString, 0);
 		jsB_propf(J, "valueOf", Bp_valueOf, 0);
 	}
-	js_newcconstructor(J, jsB_Boolean, jsB_new_Boolean);
+	js_newcconstructor(J, jsB_Boolean, jsB_new_Boolean, 1);
 	js_defglobal(J, "Boolean", JS_DONTENUM);
 }
--- a/jsdump.c
+++ b/jsdump.c
@@ -711,6 +711,9 @@
 		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_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);
+			break;
 		default: printf("[Object %p]", v.u.object); break;
 		}
 		break;
--- a/jserror.c
+++ b/jserror.c
@@ -89,13 +89,13 @@
 			jsB_props(J, "message", "an error has occurred");
 			jsB_propf(J, "toString", Ep_toString, 0);
 	}
-	js_newcconstructor(J, jsB_Error, jsB_Error);
+	js_newcconstructor(J, jsB_Error, jsB_Error, 1);
 	js_defglobal(J, "Error", JS_DONTENUM);
 
 	#define IERROR(NAME) \
 		js_pushobject(J, J->NAME##_prototype); \
 		jsB_props(J, "name", Q(NAME)); \
-		js_newcconstructor(J, jsB_##NAME, jsB_##NAME); \
+		js_newcconstructor(J, jsB_##NAME, jsB_##NAME, 1); \
 		js_defglobal(J, Q(NAME), JS_DONTENUM);
 
 	IERROR(EvalError);
--- a/jsfunction.c
+++ b/jsfunction.c
@@ -98,6 +98,6 @@
 		jsB_propf(J, "apply", Fp_apply, 2);
 		jsB_propf(J, "call", Fp_call, 1);
 	}
-	js_newcconstructor(J, jsB_Function, jsB_Function);
+	js_newcconstructor(J, jsB_Function, jsB_Function, 1);
 	js_defglobal(J, "Function", JS_DONTENUM);
 }
--- a/jsnumber.c
+++ b/jsnumber.c
@@ -76,7 +76,7 @@
 		jsB_propf(J, "toExponential", Np_toExponential, 1);
 		jsB_propf(J, "toPrecision", Np_toPrecision, 1);
 	}
-	js_newcconstructor(J, jsB_Number, jsB_new_Number);
+	js_newcconstructor(J, jsB_Number, jsB_new_Number, 1);
 	{
 		jsB_propn(J, "MAX_VALUE", DBL_MAX);
 		jsB_propn(J, "MIN_VALUE", DBL_MIN);
--- a/jsobject.c
+++ b/jsobject.c
@@ -37,7 +37,14 @@
 	case JS_CDATE: js_pushliteral(J, "[object Date]"); break;
 	case JS_CMATH: js_pushliteral(J, "[object Math]"); break;
 	case JS_CITERATOR: js_pushliteral(J, "[Iterator]"); break;
-	default: return 0;
+	case JS_CUSERDATA:
+		js_pushliteral(J, "[object ");
+		js_pushliteral(J, self->u.user.tag);
+		js_concat(J);
+		js_pushliteral(J, "]");
+		js_concat(J);
+		break;
+	default: js_pushliteral(J, "[Object unknown]"); break;
 	}
 	return 1;
 }
@@ -94,6 +101,6 @@
 		jsB_propf(J, "isPrototypeOf", Op_isPrototypeOf, 1);
 		jsB_propf(J, "propertyIsEnumerable", Op_propertyIsEnumerable, 1);
 	}
-	js_newcconstructor(J, jsB_Object, jsB_new_Object);
+	js_newcconstructor(J, jsB_Object, jsB_new_Object, 1);
 	js_defglobal(J, "Object", JS_DONTENUM);
 }
--- a/jsrun.c
+++ b/jsrun.c
@@ -99,8 +99,14 @@
 int js_isiterator(js_State *J, int idx)
 {
 	const js_Value *v = stackidx(J, idx);
-	if (v->type == JS_TOBJECT)
-		return v->u.object->type == JS_CITERATOR;
+	return v->type == JS_TOBJECT && v->u.object->type == JS_CITERATOR;
+}
+
+int js_isuserdata(js_State *J, const char *tag, int idx)
+{
+	const js_Value *v = stackidx(J, idx);
+	if (v->type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
+		return !strcmp(tag, v->u.object->u.user.tag);
 	return 0;
 }
 
@@ -160,6 +166,15 @@
 js_Value js_toprimitive(js_State *J, int idx, int hint)
 {
 	return jsV_toprimitive(J, stackidx(J, idx), hint);
+}
+
+void *js_touserdata(js_State *J, const char *tag, int idx)
+{
+	const js_Value *v = stackidx(J, idx);
+	if (v->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);
 }
 
 /* Stack manipulation */
--- a/jsstring.c
+++ b/jsstring.c
@@ -112,7 +112,7 @@
 		//jsB_propf(J, "toLowerCase", Sp_toLowerCase, 0);
 		//jsB_propf(J, "toUpperCase", Sp_toUpperCase, 0);
 	}
-	js_newcconstructor(J, jsB_String, jsB_new_String);
+	js_newcconstructor(J, jsB_String, jsB_new_String, 1);
 	{
 		jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
 	}
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -278,7 +278,7 @@
 }
 
 /* prototype -- constructor */
-void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon)
+void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon, int length)
 {
 	js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
 	obj->u.c.function = cfun;
@@ -285,7 +285,7 @@
 	obj->u.c.constructor = ccon;
 	js_pushobject(J, obj); /* proto obj */
 	{
-		js_pushnumber(J, 1);
+		js_pushnumber(J, length);
 		js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTDELETE);
 		js_rot2(J); /* obj proto */
 		js_copy(J, -2); /* obj proto obj */
@@ -292,6 +292,21 @@
 		js_defproperty(J, -2, "constructor", JS_DONTENUM);
 		js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTDELETE);
 	}
+}
+
+void js_newuserdata(js_State *J, const char *tag, void *data)
+{
+	js_Object *prototype = NULL;
+	js_Object *obj;
+
+	if (js_isobject(J, -1))
+		prototype = js_toobject(J, -1);
+	js_pop(J, 1);
+
+	obj = jsV_newobject(J, JS_CUSERDATA, prototype);
+	obj->u.user.tag = tag;
+	obj->u.user.data = data;
+	js_pushobject(J, obj);
 }
 
 /* Non-trivial operations on values. These are implemented using the stack. */
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -30,6 +30,7 @@
 	JS_CDATE,
 	JS_CMATH,
 	JS_CITERATOR,
+	JS_CUSERDATA,
 };
 
 struct js_Value
@@ -61,6 +62,10 @@
 			js_CFunction constructor;
 		} c;
 		js_Iterator *iter;
+		struct {
+			const char *tag;
+			void *data;
+		} user;
 	} u;
 	js_Object *gcnext;
 	int gcmark;