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;