Introduce ObjectOps for typeOf and make trace a mandatory ObjectOp (547314, r=brendan).

This commit is contained in:
Andreas Gal 2010-02-22 16:30:22 -08:00
parent bfa1d960a9
commit 7fc1c8efb0
11 changed files with 105 additions and 58 deletions

View File

@ -496,49 +496,13 @@ JS_TypeOfValue(JSContext *cx, jsval v)
{
JSType type;
JSObject *obj;
const JSObjectOps *ops;
JSClass *clasp;
CHECK_REQUEST(cx);
if (JSVAL_IS_OBJECT(v)) {
type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */
obj = JSVAL_TO_OBJECT(v);
if (obj) {
obj = js_GetWrappedObject(cx, obj);
ops = obj->map->ops;
#if JS_HAS_XML_SUPPORT
if (ops == &js_XMLObjectOps) {
type = JSTYPE_XML;
} else
#endif
{
/*
* ECMA 262, 11.4.3 says that any native object that implements
* [[Call]] should be of type "function". However, RegExp is of
* type "object", not "function", for Web compatibility.
*/
clasp = OBJ_GET_CLASS(cx, obj);
if ((ops == &js_ObjectOps)
? (clasp->call
? clasp == &js_ScriptClass
: clasp == &js_FunctionClass)
: ops->call != NULL) {
type = JSTYPE_FUNCTION;
} else {
#ifdef NARCISSUS
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__call__Atom),
&v)) {
JS_ClearPendingException(cx);
} else if (VALUE_IS_FUNCTION(cx, v)) {
type = JSTYPE_FUNCTION;
}
#endif
}
}
}
if (obj)
return obj->map->ops->typeOf(cx, obj);
return JSTYPE_OBJECT;
} else if (JSVAL_IS_NUMBER(v)) {
type = JSTYPE_NUMBER;
} else if (JSVAL_IS_STRING(v)) {

View File

@ -828,6 +828,12 @@ static JSBool
slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp);
static JSType
array_typeOf(JSContext *cx, JSObject *obj)
{
return JSTYPE_OBJECT;
}
/* The same as js_ObjectOps except for the .enumerate and .call hooks. */
static JSObjectOps js_SlowArrayObjectOps = {
NULL,
@ -836,10 +842,10 @@ static JSObjectOps js_SlowArrayObjectOps = {
js_GetAttributes, js_SetAttributes,
js_DeleteProperty, js_DefaultValue,
slowarray_enumerate, js_CheckAccess,
array_typeOf, js_TraceObject,
NULL, NATIVE_DROP_PROPERTY,
NULL, js_Construct,
js_HasInstance, js_TraceObject,
js_Clear
js_HasInstance, js_Clear
};
static JSObjectOps *
@ -1248,10 +1254,10 @@ JSObjectOps js_ArrayObjectOps = {
array_getAttributes, array_setAttributes,
array_deleteProperty, js_DefaultValue,
array_enumerate, js_CheckAccess,
array_typeOf, array_trace,
NULL, array_dropProperty,
NULL, NULL,
js_HasInstance, array_trace,
NULL
js_HasInstance, NULL
};
static JSObjectOps *

View File

@ -329,8 +329,7 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, IN
JSString* FASTCALL
js_TypeOfObject(JSContext* cx, JSObject* obj)
{
JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj));
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[obj->typeOf(cx)]);
}
JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, 1)

View File

@ -111,10 +111,10 @@ JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
js_GetAttributes, js_SetAttributes,
js_DeleteProperty, js_DefaultValue,
js_Enumerate, js_CheckAccess,
js_TypeOf, js_TraceObject,
NULL, NATIVE_DROP_PROPERTY,
js_Call, js_Construct,
js_HasInstance, js_TraceObject,
js_Clear
js_HasInstance, js_Clear
};
JSClass js_ObjectClass = {
@ -1821,7 +1821,7 @@ js_obj_defineGetter(JSContext *cx, uintN argc, jsval *vp)
JSObject *obj;
uintN attrs;
if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
if (argc <= 1 || !js_IsCallable(cx, vp[3])) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_GETTER_OR_SETTER,
js_getter_str);
@ -1854,7 +1854,7 @@ js_obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
JSObject *obj;
uintN attrs;
if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
if (argc <= 1 || !js_IsCallable(cx, vp[3])) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_GETTER_OR_SETTER,
js_setter_str);
@ -3319,6 +3319,12 @@ with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
return proto->checkAccess(cx, id, mode, vp, attrsp);
}
static JSType
with_TypeOf(JSContext *cx, JSObject *obj)
{
return JSTYPE_OBJECT;
}
static JSObject *
with_ThisObject(JSContext *cx, JSObject *obj)
{
@ -3335,10 +3341,10 @@ JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
with_GetAttributes, with_SetAttributes,
with_DeleteProperty, with_DefaultValue,
with_Enumerate, with_CheckAccess,
with_TypeOf, js_TraceObject,
with_ThisObject, NATIVE_DROP_PROPERTY,
NULL, NULL,
NULL, js_TraceObject,
js_Clear
NULL, js_Clear
};
static JSObjectOps *
@ -6017,6 +6023,38 @@ js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp);
}
JSType
js_TypeOf(JSContext *cx, JSObject *obj)
{
/*
* Wrappers should also intercept js_TypeOf and answer accordingly.
*/
JS_ASSERT(js_GetWrappedObject(cx, obj) == obj);
/*
* ECMA 262, 11.4.3 says that any native object that implements
* [[Call]] should be of type "function". However, RegExp is of
* type "object", not "function", for Web compatibility.
*/
if (obj->isCallable(cx)) {
return (obj->getClass() != &js_RegExpClass)
? JSTYPE_FUNCTION
: JSTYPE_OBJECT;
}
#ifdef NARCISSUS
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__call__Atom), &v)) {
JS_ClearPendingException(cx);
} else if (VALUE_IS_FUNCTION(cx, v)) {
return JSTYPE_FUNCTION;
}
#endif
return JSTYPE_OBJECT;
}
#ifdef JS_THREADSAFE
void
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)

View File

@ -154,6 +154,8 @@ struct JSObjectOps {
JSConvertOp defaultValue;
JSNewEnumerateOp enumerate;
JSCheckAccessIdOp checkAccess;
JSTypeOfOp typeOf;
JSTraceOp trace;
/* Optionally non-null members start here. */
JSObjectOp thisObject;
@ -161,7 +163,6 @@ struct JSObjectOps {
JSNative call;
JSNative construct;
JSHasInstanceOp hasInstance;
JSTraceOp trace;
JSFinalizeOp clear;
bool inline isNative() const;
@ -414,6 +415,10 @@ struct JSObject {
return map->ops->checkAccess(cx, this, id, mode, vp, attrsp);
}
JSType typeOf(JSContext *cx) {
return map->ops->typeOf(cx, this);
}
/* These four are time-optimized to avoid stub calls. */
JSObject *thisObject(JSContext *cx) {
return map->ops->thisObject ? map->ops->thisObject(cx, this) : this;
@ -1002,6 +1007,9 @@ extern JSBool
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
jsval *vp, uintN *attrsp);
extern JSType
js_TypeOf(JSContext *cx, JSObject *obj);
extern JSBool
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

View File

@ -3292,7 +3292,7 @@ BEGIN_CASE(JSOP_SETTER)
if (id == 0)
FETCH_ELEMENT_ID(obj, i, id);
if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
if (!js_IsCallable(cx, rval)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_GETTER_OR_SETTER,
(op == JSOP_GETTER)

View File

@ -260,6 +260,12 @@ typedef JSBool
typedef JSBool
(* JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
/*
* Delegate typeof to an object so it can cloak a primitive or another object.
*/
typedef JSType
(* JSTypeOfOp)(JSContext *cx, JSObject *obj);
/*
* Finalize obj, which the garbage collector has determined to be unreachable
* from other live objects or from GC roots. Obviously, finalizers must never

View File

@ -1953,7 +1953,7 @@ str_replace(JSContext *cx, uintN argc, jsval *vp)
NORMALIZE_THIS(cx, vp, rdata.str);
/* Extract replacement string/function. */
if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {
if (argc >= 2 && js_IsCallable(cx, vp[3])) {
rdata.lambda = JSVAL_TO_OBJECT(vp[3]);
rdata.repstr = NULL;
rdata.dollar = rdata.dollarEnd = NULL;

View File

@ -599,6 +599,12 @@ class TypedArrayTemplate
return true;
}
static JSType
obj_typeOf(JSContext *cx, JSObject *obj)
{
return JSTYPE_OBJECT;
}
/*
* new [Type]Array(length)
* new [Type]Array(otherTypedArray)
@ -1186,10 +1192,11 @@ template<> JSObjectOps _typedArray::fastObjectOps = { \
js_DefaultValue, \
_typedArray::obj_enumerate, \
js_CheckAccess, \
_typedArray::obj_typeOf, \
_typedArray::obj_trace, \
NULL, \
_typedArray::obj_dropProperty, \
NULL, NULL, NULL, \
TypedArray::obj_trace, \
NULL \
}; \
template<> JSFunctionSpec _typedArray::jsfuncs[] = { \

View File

@ -84,7 +84,6 @@
* - Fuse objects and their JSXML* private data into single GC-things
* - fix function::foo vs. x.(foo == 42) collision using proper namespacing
* - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
* - JS_TypeOfValue sure could use a cleaner interface to "types"
*/
#ifdef XML_METERING
@ -5045,6 +5044,12 @@ xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
return JS_TRUE;
}
static JSType
xml_typeOf(JSContext *cx, JSObject *obj)
{
return JSTYPE_XML;
}
static JSBool
xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
{
@ -5314,10 +5319,10 @@ JS_FRIEND_DATA(JSObjectOps) js_XMLObjectOps = {
xml_getAttributes, xml_setAttributes,
xml_deleteProperty, xml_defaultValue,
xml_enumerate, js_CheckAccess,
xml_typeOf, js_TraceObject,
NULL, NULL,
NULL, NULL,
xml_hasInstance, js_TraceObject,
xml_clear
xml_hasInstance, xml_clear
};
static JSObjectOps *

View File

@ -1405,6 +1405,18 @@ XPC_WN_JSOp_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
return js_ObjectOps.enumerate(cx, obj, enum_op, statep, idp);
}
static JSType
XPC_WN_JSOp_TypeOf_Object(JSContext *cx, JSObject *obj)
{
return JSTYPE_OBJECT;
}
static JSType
XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSObject *obj)
{
return JSTYPE_FUNCTION;
}
static void
XPC_WN_JSOp_Clear(JSContext *cx, JSObject *obj)
{
@ -1554,11 +1566,13 @@ JSBool xpc_InitWrappedNativeJSOps()
XPC_WN_NoCall_JSOps.enumerate = XPC_WN_JSOp_Enumerate;
XPC_WN_NoCall_JSOps.call = nsnull;
XPC_WN_NoCall_JSOps.construct = nsnull;
XPC_WN_NoCall_JSOps.typeOf = XPC_WN_JSOp_TypeOf_Object;
XPC_WN_NoCall_JSOps.clear = XPC_WN_JSOp_Clear;
XPC_WN_NoCall_JSOps.thisObject = XPC_WN_JSOp_ThisObject;
memcpy(&XPC_WN_WithCall_JSOps, &js_ObjectOps, sizeof(JSObjectOps));
XPC_WN_WithCall_JSOps.enumerate = XPC_WN_JSOp_Enumerate;
XPC_WN_WithCall_JSOps.typeOf = XPC_WN_JSOp_TypeOf_Function;
XPC_WN_WithCall_JSOps.clear = XPC_WN_JSOp_Clear;
XPC_WN_WithCall_JSOps.thisObject = XPC_WN_JSOp_ThisObject;
}