diff --git a/js.h b/js.h index bb8466d..56ec047 100644 --- a/js.h +++ b/js.h @@ -111,7 +111,8 @@ 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); +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_isstring(js_State *J, int idx); 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); diff --git a/jsarray.c b/jsarray.c index e38d495..77a8bc9 100644 --- a/jsarray.c +++ b/jsarray.c @@ -19,7 +19,7 @@ static int A_isArray(js_State *J, int argc) 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); diff --git a/jsboolean.c b/jsboolean.c index c522c55..5aabacd 100644 --- a/jsboolean.c +++ b/jsboolean.c @@ -39,6 +39,6 @@ void jsB_initboolean(js_State *J) 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); } diff --git a/jsdump.c b/jsdump.c index 6e1d55f..5f84715 100644 --- a/jsdump.c +++ b/jsdump.c @@ -711,6 +711,9 @@ void js_dumpvalue(js_State *J, js_Value v) 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; diff --git a/jserror.c b/jserror.c index 9a1d086..cde7222 100644 --- a/jserror.c +++ b/jserror.c @@ -89,13 +89,13 @@ void jsB_initerror(js_State *J) 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); diff --git a/jsfunction.c b/jsfunction.c index e13ac06..bd6b3d9 100644 --- a/jsfunction.c +++ b/jsfunction.c @@ -98,6 +98,6 @@ void jsB_initfunction(js_State *J) 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); } diff --git a/jsnumber.c b/jsnumber.c index e587faa..90b02f2 100644 --- a/jsnumber.c +++ b/jsnumber.c @@ -76,7 +76,7 @@ void jsB_initnumber(js_State *J) 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); diff --git a/jsobject.c b/jsobject.c index 1da4f9d..cd15aaf 100644 --- a/jsobject.c +++ b/jsobject.c @@ -37,7 +37,14 @@ static int Op_toString(js_State *J, int argc) 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 @@ void jsB_initobject(js_State *J) 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); } diff --git a/jsrun.c b/jsrun.c index 1f96a73..cf6b191 100644 --- a/jsrun.c +++ b/jsrun.c @@ -99,8 +99,14 @@ int js_iscallable(js_State *J, int idx) 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; } @@ -162,6 +168,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 */ void js_pop(js_State *J, int n) diff --git a/jsstring.c b/jsstring.c index f8069ad..a7573f5 100644 --- a/jsstring.c +++ b/jsstring.c @@ -112,7 +112,7 @@ void jsB_initstring(js_State *J) //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); } diff --git a/jsvalue.c b/jsvalue.c index 0c043b8..e5b9ce8 100644 --- a/jsvalue.c +++ b/jsvalue.c @@ -278,14 +278,14 @@ void js_newcfunction(js_State *J, js_CFunction cfun, int length) } /* 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; 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 */ @@ -294,6 +294,21 @@ void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon) } } +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. */ int js_instanceof(js_State *J) diff --git a/jsvalue.h b/jsvalue.h index 6bfa95d..21bf520 100644 --- a/jsvalue.h +++ b/jsvalue.h @@ -30,6 +30,7 @@ enum js_Class { JS_CDATE, JS_CMATH, JS_CITERATOR, + JS_CUSERDATA, }; struct js_Value @@ -61,6 +62,10 @@ struct js_Object js_CFunction constructor; } c; js_Iterator *iter; + struct { + const char *tag; + void *data; + } user; } u; js_Object *gcnext; int gcmark;