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.
This commit is contained in:
Tor Andersson
2014-01-24 17:11:49 +01:00
parent 4595872bb3
commit 90b54a9eec
12 changed files with 62 additions and 14 deletions
+4 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
}
+3
View File
@@ -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;
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
+9 -2
View File
@@ -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);
}
+17 -2
View File
@@ -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)
+1 -1
View File
@@ -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);
}
+17 -2
View File
@@ -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)
+5
View File
@@ -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;