Bug 384151: refactoring boxing of primitive values, r=brendan

This commit is contained in:
igor@mir2.org 2007-06-14 00:07:01 -07:00
parent 11dc4a0e47
commit 52a3248e98
13 changed files with 127 additions and 189 deletions

View File

@ -4168,10 +4168,9 @@ js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj,
* the 'this' param if no args.
*/
JS_ASSERT(cx->fp->argv == argv);
tmp = js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), argv);
if (!tmp)
if (!js_ComputeThis(cx, argv))
return JS_FALSE;
cx->fp->thisp = tmp;
cx->fp->thisp = JSVAL_TO_OBJECT(argv[-1]);
/*
* Protect against argc - 1 underflowing below. By calling js_ComputeThis,

View File

@ -174,18 +174,6 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj)
return proto;
}
JSObject *
js_BooleanToObject(JSContext *cx, JSBool b)
{
JSObject *obj;
obj = js_NewObject(cx, &js_BooleanClass, NULL, NULL);
if (!obj)
return NULL;
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b));
return obj;
}
JSString *
js_BooleanToString(JSContext *cx, JSBool b)
{

View File

@ -62,9 +62,6 @@ extern JSClass js_BooleanClass;
extern JSObject *
js_InitBooleanClass(JSContext *cx, JSObject *obj);
extern JSObject *
js_BooleanToObject(JSContext *cx, JSBool b);
extern JSString *
js_BooleanToString(JSContext *cx, JSBool b);

View File

@ -173,8 +173,7 @@ ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
}
JSBool
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,
JSObject **objp, jsval *vp)
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
{
jsval val;
JSObject *obj;
@ -195,11 +194,9 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,
} else {
obj = JSVAL_TO_OBJECT(val);
}
*objp = obj;
return OBJ_GET_PROPERTY(cx, obj, id, vp);
}
*objp = NULL;
*vp = JSVAL_VOID;
if (JSID_IS_INT(id)) {
slot = (uintN) JSID_TO_INT(id);

View File

@ -151,8 +151,7 @@ extern JSBool
js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp);
extern JSBool
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,
JSObject **objp, jsval *vp);
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp);
extern JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);

View File

@ -230,25 +230,6 @@
sp--; \
JS_END_MACRO
/*
* Convert a primitive string, number or boolean to a corresponding object.
* v must not be an object, null or undefined when using this macro.
*/
#define PRIMITIVE_TO_OBJECT(cx, v, obj) \
JS_BEGIN_MACRO \
SAVE_SP(fp); \
if (JSVAL_IS_STRING(v)) { \
obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \
} else if (JSVAL_IS_INT(v)) { \
obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \
} else if (JSVAL_IS_DOUBLE(v)) { \
obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \
} else { \
JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \
obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \
} \
JS_END_MACRO
/* SAVE_SP_AND_PC must be already called. */
#define VALUE_TO_OBJECT(cx, n, v, obj) \
JS_BEGIN_MACRO \
@ -524,14 +505,23 @@ PutBlockObjects(JSContext *cx, JSStackFrame *fp)
return ok;
}
JSObject *
js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv)
JSBool
js_ComputeThis(JSContext *cx, jsval *argv)
{
JSObject *thisp;
if (!JSVAL_IS_OBJECT(argv[-1]))
return js_PrimitiveToObject(cx, &argv[-1]);
thisp = JSVAL_TO_OBJECT(argv[-1]);
if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
if (!thisp->map->ops->thisObject)
return JS_TRUE;
/* Some objects (e.g., With) delegate 'this' to another object. */
thisp = OBJ_THIS_OBJECT(cx, thisp);
if (!thisp)
return NULL;
return JS_FALSE;
} else {
/*
* ECMA requires "the global object", but in the presence of multiple
@ -563,7 +553,7 @@ js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv)
id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
for (;;) {
if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs))
return NULL;
return JS_FALSE;
parent = JSVAL_IS_VOID(v)
? OBJ_GET_PARENT(cx, thisp)
: JSVAL_TO_OBJECT(v);
@ -574,7 +564,7 @@ js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv)
}
}
argv[-1] = OBJECT_TO_JSVAL(thisp);
return thisp;
return JS_TRUE;
}
#if JS_HAS_NO_SUCH_METHOD
@ -605,25 +595,17 @@ NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags,
* such defaulting of 'this' to callee (v, *vp) ancestor.
*/
JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0]));
RESTORE_SP(fp);
if (JSVAL_IS_OBJECT(vp[1])) {
thisp = JSVAL_TO_OBJECT(vp[1]);
} else {
PRIMITIVE_TO_OBJECT(cx, vp[1], thisp);
if (!thisp)
return JS_FALSE;
vp[1] = OBJECT_TO_JSVAL(thisp);
}
thisp = js_ComputeThis(cx, thisp, vp + 2);
if (!thisp)
if (!js_ComputeThis(cx, vp + 2))
return JS_FALSE;
vp[1] = OBJECT_TO_JSVAL(thisp);
RESTORE_SP(fp);
/* From here on, control must flow through label out: to return. */
memset(roots, 0, sizeof roots);
JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
thisp = JSVAL_TO_OBJECT(vp[1]);
#if JS_HAS_XML_SUPPORT
if (OBJECT_IS_XML(cx, thisp)) {
JSXMLObjectOps *ops;
@ -1038,8 +1020,8 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
void *mark;
JSStackFrame *fp, frame;
jsval *sp, *newsp, *limit;
jsval *vp, v, thisv;
JSObject *funobj, *parent, *thisp;
jsval *vp, v;
JSObject *funobj, *parent;
JSBool ok;
JSClass *clasp;
JSObjectOps *ops;
@ -1070,7 +1052,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
* implements the __noSuchMethod__ method, in which case that method will
* be called like so:
*
* thisp.__noSuchMethod__(id, args)
* this.__noSuchMethod__(id, args)
*
* where id is the name of the method that this invocation attempted to
* call by name, and args is an Array containing this invocation's actual
@ -1088,9 +1070,6 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
goto bad;
}
/* Load thisv after potentially calling NoSuchMethod, which may set it. */
thisv = vp[1];
funobj = JSVAL_TO_OBJECT(v);
parent = OBJ_GET_PARENT(cx, funobj);
clasp = OBJ_GET_CLASS(cx, funobj);
@ -1125,18 +1104,18 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
nslots = nvars = 0;
/* Try a call or construct native object op. */
native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call;
if (flags & JSINVOKE_CONSTRUCT) {
if (!JSVAL_IS_OBJECT(vp[1])) {
ok = js_PrimitiveToObject(cx, &vp[1]);
if (!ok)
goto out2;
}
native = ops->construct;
} else {
native = ops->call;
}
if (!native)
goto bad;
if (JSVAL_IS_OBJECT(thisv)) {
thisp = JSVAL_TO_OBJECT(thisv);
} else {
PRIMITIVE_TO_OBJECT(cx, thisv, thisp);
if (!thisp)
goto out2;
vp[1] = thisv = OBJECT_TO_JSVAL(thisp);
}
} else {
have_fun:
/* Get private data and set derived locals from it. */
@ -1155,61 +1134,56 @@ have_fun:
if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
/* Handle bound method special case. */
thisp = parent;
} else if (JSVAL_IS_OBJECT(thisv)) {
thisp = JSVAL_TO_OBJECT(thisv);
} else {
uintN thispflags = JSFUN_THISP_FLAGS(fun->flags);
vp[1] = OBJECT_TO_JSVAL(parent);
} else if (!JSVAL_IS_OBJECT(vp[1])) {
/*
* We check if the function accepts a primitive value as |this|.
* For that we use a table that maps value's tag into the
* corresponding function flag.
*/
uintN flagToTest;
JS_STATIC_ASSERT(JSVAL_INT == 1);
JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
JS_STATIC_ASSERT(JSVAL_STRING == 4);
JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
static const uint16 PrimitiveTestFlags[] = {
JSFUN_THISP_NUMBER, /* INT */
JSFUN_THISP_NUMBER, /* DOUBLE */
JSFUN_THISP_NUMBER, /* INT */
JSFUN_THISP_STRING, /* STRING */
JSFUN_THISP_NUMBER, /* INT */
JSFUN_THISP_BOOLEAN, /* BOOLEAN */
JSFUN_THISP_NUMBER /* INT */
};
JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
if (JSVAL_IS_STRING(thisv)) {
if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_STRING)) {
thisp = (JSObject *) thisv;
goto init_frame;
}
thisp = js_StringToObject(cx, JSVAL_TO_STRING(thisv));
} else if (JSVAL_IS_INT(thisv)) {
if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) {
thisp = (JSObject *) thisv;
goto init_frame;
}
thisp = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(thisv));
} else if (JSVAL_IS_DOUBLE(thisv)) {
if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) {
thisp = (JSObject *) thisv;
goto init_frame;
}
thisp = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(thisv));
} else {
JS_ASSERT(JSVAL_IS_BOOLEAN(thisv));
if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_BOOLEAN)) {
thisp = (JSObject *) thisv;
goto init_frame;
}
thisp = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(thisv));
}
if (!thisp) {
ok = JS_FALSE;
goto out2;
}
goto init_frame;
JS_ASSERT(vp[1] != JSVAL_VOID);
flagToTest = PrimitiveTestFlags[JSVAL_TAG(vp[1]) - 1];
if (JSFUN_THISP_TEST(JSFUN_THISP_FLAGS(fun->flags), flagToTest))
goto init_frame;
}
}
if (flags & JSINVOKE_CONSTRUCT) {
/* Default return value for a constructor is the new object. */
frame.rval = OBJECT_TO_JSVAL(thisp);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
frame.rval = vp[1];
} else {
thisp = js_ComputeThis(cx, thisp, vp + 2);
if (!thisp) {
ok = JS_FALSE;
ok = js_ComputeThis(cx, vp + 2);
if (!ok)
goto out2;
}
}
init_frame:
/* Initialize the rest of frame, except for sp (set by SAVE_SP later). */
frame.thisp = thisp;
/*
* Initialize the rest of frame, except for sp (set by SAVE_SP later).
*
* To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
* can be a primitive value here for native functions specified with
* JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
*/
frame.thisp = (JSObject *)vp[1];
frame.varobj = NULL;
frame.callobj = frame.argsobj = NULL;
frame.script = script;
@ -3947,20 +3921,11 @@ interrupt:
newifp->mark = newmark;
/* Compute the 'this' parameter now that argv is set. */
if (!JSVAL_IS_OBJECT(vp[1])) {
PRIMITIVE_TO_OBJECT(cx, vp[1], obj2);
if (!obj2)
goto bad_inline_call;
vp[1] = OBJECT_TO_JSVAL(obj2);
}
newifp->frame.thisp =
js_ComputeThis(cx,
JSFUN_BOUND_METHOD_TEST(fun->flags)
? parent
: JSVAL_TO_OBJECT(vp[1]),
newifp->frame.argv);
if (!newifp->frame.thisp)
if (JSFUN_BOUND_METHOD_TEST(fun->flags))
vp[1] = OBJECT_TO_JSVAL(parent);
if (!js_ComputeThis(cx, vp + 2))
goto bad_inline_call;
newifp->frame.thisp = JSVAL_TO_OBJECT(vp[1]);
#ifdef DUMP_CALL_TABLE
LogCall(cx, *vp, argc, vp + 2);
#endif
@ -4540,7 +4505,7 @@ interrupt:
BEGIN_CASE(JSOP_ARGSUB)
id = INT_TO_JSID(GET_ARGNO(pc));
SAVE_SP_AND_PC(fp);
ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
ok = js_GetArgsProperty(cx, fp, id, &rval);
if (!ok)
goto out;
PUSH_OPND(rval);
@ -4549,7 +4514,7 @@ interrupt:
BEGIN_CASE(JSOP_ARGCNT)
id = ATOM_TO_JSID(rt->atomState.lengthAtom);
SAVE_SP_AND_PC(fp);
ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
ok = js_GetArgsProperty(cx, fp, id, &rval);
if (!ok)
goto out;
PUSH_OPND(rval);

View File

@ -152,14 +152,14 @@ extern JSObject *
js_GetScopeChain(JSContext *cx, JSStackFrame *fp);
/*
* Compute the 'this' parameter for a call with nominal 'this' given by thisp
* and arguments including argv[-1] (nominal 'this') and argv[-2] (callee).
* Activation objects ("Call" objects not created with "new Call()", i.e.,
* "Call" objects that have private data) may not be referred to by 'this',
* per ECMA-262, so js_ComputeThis censors them.
* For a call with arguments argv including argv[-1] (nominal |this|) and
* argv[-2] (callee) replace null |this| with callee's parent, replace
* primitive values with the equivalent wrapper objects and censor activation
* objects as, per ECMA-262, they may not be referred to by |this|. argv[-1]
* must not be a JSVAL_VOID.
*/
extern JSObject *
js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv);
extern JSBool
js_ComputeThis(JSContext *cx, jsval *argv);
/*
* NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp

View File

@ -686,23 +686,6 @@ js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
return JS_TRUE;
}
JSObject *
js_NumberToObject(JSContext *cx, jsdouble d)
{
JSObject *obj;
jsval v;
obj = js_NewObject(cx, &js_NumberClass, NULL, NULL);
if (!obj)
return NULL;
if (!js_NewNumberValue(cx, d, &v)) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
return NULL;
}
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
return obj;
}
JSString *
js_NumberToString(JSContext *cx, jsdouble d)
{

View File

@ -178,10 +178,6 @@ js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval);
extern JSBool
js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);
/* Construct a Number instance that wraps around d. */
extern JSObject *
js_NumberToObject(JSContext *cx, jsdouble d);
/* Convert a number to a GC'ed string. */
extern JSString *
js_NumberToString(JSContext *cx, jsdouble d);

View File

@ -4510,6 +4510,38 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
0, NULL);
}
JSBool
js_PrimitiveToObject(JSContext *cx, jsval *vp)
{
JSClass *clasp;
JSObject *obj;
/* Table to map primitive value's tag into the corresponding class. */
JS_STATIC_ASSERT(JSVAL_INT == 1);
JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
JS_STATIC_ASSERT(JSVAL_STRING == 4);
JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
static JSClass *const PrimitiveClasses[] = {
&js_NumberClass, /* INT */
&js_NumberClass, /* DOUBLE */
&js_NumberClass, /* INT */
&js_StringClass, /* STRING */
&js_NumberClass, /* INT */
&js_BooleanClass, /* BOOLEAN */
&js_NumberClass /* INT */
};
JS_ASSERT(!JSVAL_IS_OBJECT(*vp));
JS_ASSERT(*vp != JSVAL_VOID);
clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1];
obj = js_NewObject(cx, clasp, NULL, NULL);
if (!obj)
return JS_FALSE;
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, *vp);
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JSBool
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
{
@ -4524,18 +4556,9 @@ js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
if (JSVAL_IS_OBJECT(v))
obj = JSVAL_TO_OBJECT(v);
} else {
if (JSVAL_IS_STRING(v)) {
obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
} else if (JSVAL_IS_INT(v)) {
obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
} else if (JSVAL_IS_DOUBLE(v)) {
obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
} else {
JS_ASSERT(JSVAL_IS_BOOLEAN(v));
obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
}
if (!obj)
if (!js_PrimitiveToObject(cx, &v))
return JS_FALSE;
obj = JSVAL_TO_OBJECT(v);
}
*objp = obj;
return JS_TRUE;

View File

@ -597,6 +597,13 @@ extern JSBool
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
uintN attrs);
/*
* Wrap boolean, number or string as Boolean, Number or String object.
* *vp must not be an object, null or undefined.
*/
extern JSBool
js_PrimitiveToObject(JSContext *cx, jsval *vp);
extern JSBool
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp);

View File

@ -2629,18 +2629,6 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str)
str->length = 0;
}
JSObject *
js_StringToObject(JSContext *cx, JSString *str)
{
JSObject *obj;
obj = js_NewObject(cx, &js_StringClass, NULL, NULL);
if (!obj)
return NULL;
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));
return obj;
}
JS_FRIEND_API(const char *)
js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun)
{

View File

@ -340,10 +340,6 @@ js_FinalizeString(JSContext *cx, JSString *str);
extern void
js_FinalizeStringRT(JSRuntime *rt, JSString *str);
/* Wrap a string value in a String object. */
extern JSObject *
js_StringToObject(JSContext *cx, JSString *str);
/*
* Convert a value to a printable C string.
*/