diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index f00c4d58a94..235c4b32a53 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1280,16 +1280,14 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, } /* Get element's character string. */ - if (!(hole || rval->isNullOrUndefined())) { + if (!hole && !rval->isNullOrUndefined()) { if (locale) { /* Work on obj.toLocalString() instead. */ - JSObject *robj; - - if (!js_ValueToObjectOrNull(cx, *rval, &robj)) + JSObject *robj = ToObject(cx, rval); + if (!robj) goto out; - rval->setObjectOrNull(robj); - JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom; - if (!js_TryMethod(cx, robj, atom, 0, NULL, rval)) + jsid id = ATOM_TO_JSID(cx->runtime->atomState.toLocaleStringAtom); + if (!robj->callMethod(cx, id, 0, NULL, rval)) goto out; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index c069b4241ac..ba7437eced8 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5480,6 +5480,14 @@ JSObject::reportNotExtensible(JSContext *cx, uintN report) NULL, NULL, NULL); } +bool +JSObject::callMethod(JSContext *cx, jsid id, uintN argc, Value *argv, Value *vp) +{ + Value fval; + return js_GetMethod(cx, this, id, JSGET_NO_METHOD_BARRIER, &fval) && + ExternalInvoke(cx, ObjectValue(*this), fval, argc, argv, vp); +} + JSBool js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, Value *vp, JSBool strict) @@ -5899,12 +5907,21 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) return true; } - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) + Value fval; + jsid id = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); + if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) return false; - if (!v.isPrimitive()) { - if (!obj->getClass()->convert(cx, obj, hint, &v)) + if (js_IsCallable(fval)) { + if (!ExternalInvoke(cx, ObjectValue(*obj), fval, 0, NULL, &v)) return false; + if (v.isPrimitive()) { + *vp = v; + return true; + } } + + if (!obj->getClass()->convert(cx, obj, hint, &v)) + return false; } else { /* Optimize (new String(...)).valueOf(). */ Class *clasp = obj->getClass(); @@ -5924,8 +5941,18 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) return false; if (v.isObject()) { JS_ASSERT(hint != TypeOfValue(cx, v)); - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) + Value fval; + jsid id = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); + if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) return false; + if (js_IsCallable(fval)) { + if (!ExternalInvoke(cx, ObjectValue(*obj), fval, 0, NULL, &v)) + return false; + if (v.isPrimitive()) { + *vp = v; + return true; + } + } } } if (v.isObject()) { @@ -6243,35 +6270,21 @@ js_ValueToNonNullObject(JSContext *cx, const Value &v) JSBool js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval) { - Value argv[1]; - - argv[0].setString(cx->runtime->atomState.typeAtoms[type]); - return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, - 1, argv, rval); -} - -JSBool -js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN argc, Value *argv, Value *rval) -{ - JS_CHECK_RECURSION(cx, return JS_FALSE); - - /* - * Report failure only if an appropriate method was found, and calling it - * returned failure. We propagate failure in this case to make exceptions - * behave properly. - */ - JSErrorReporter older = JS_SetErrorReporter(cx, NULL); - jsid id = ATOM_TO_JSID(atom); Value fval; - JSBool ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval); - JS_SetErrorReporter(cx, older); - if (!ok) + jsid id = ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom); + if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) return false; - - if (fval.isPrimitive()) - return JS_TRUE; - return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval); + if (js_IsCallable(fval)) { + Value v; + Value argv[] = { StringValue(cx->runtime->atomState.typeAtoms[type]) }; + if (!ExternalInvoke(cx, ObjectValue(*obj), fval, JS_ARRAY_LENGTH(argv), argv, &v)) + return false; + if (v.isPrimitive()) { + *rval = v; + return true; + } + } + return true; } #if JS_HAS_XDR diff --git a/js/src/jsobj.h b/js/src/jsobj.h index b130c1e2c18..aecd2154f29 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1186,6 +1186,14 @@ struct JSObject : js::gc::Cell { bool reportNotConfigurable(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR); bool reportNotExtensible(JSContext *cx, uintN report = JSREPORT_ERROR); + /* + * Get the property with the given id, then call it as a function with the + * given arguments, providing this object as |this|. If the property isn't + * callable a TypeError will be thrown. On success the value returned by + * the call is stored in *vp. + */ + bool callMethod(JSContext *cx, jsid id, uintN argc, js::Value *argv, js::Value *vp); + private: js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::Shape &child); @@ -1884,10 +1892,6 @@ js_ValueToNonNullObject(JSContext *cx, const js::Value &v); extern JSBool js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, js::Value *rval); -extern JSBool -js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN argc, js::Value *argv, js::Value *rval); - extern JSBool js_XDRObject(JSXDRState *xdr, JSObject **objp); diff --git a/js/src/json.cpp b/js/src/json.cpp index 37464197fb4..29a226cefb4 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -170,15 +170,19 @@ js_json_stringify(JSContext *cx, uintN argc, Value *vp) JSBool js_TryJSON(JSContext *cx, Value *vp) { - // Checks whether the return value implements toJSON() - JSBool ok = JS_TRUE; + if (!vp->isObject()) + return true; - if (vp->isObject()) { - JSObject *obj = &vp->toObject(); - ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp); + JSObject *obj = &vp->toObject(); + Value fval; + jsid id = ATOM_TO_JSID(cx->runtime->atomState.toJSONAtom); + if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval)) + return false; + if (js_IsCallable(fval)) { + if (!ExternalInvoke(cx, ObjectValue(*obj), fval, 0, NULL, vp)) + return false; } - - return ok; + return true; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index bb5eff56df2..171fcfdb69a 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3875,11 +3875,17 @@ js_ValueToSource(JSContext *cx, const Value &v) return js_ValueToString(cx, v); } - JSAtom *atom = cx->runtime->atomState.toSourceAtom; - AutoValueRooter tvr(cx); - if (!js_TryMethod(cx, &v.toObject(), atom, 0, NULL, tvr.addr())) - return NULL; - return js_ValueToString(cx, tvr.value()); + Value rval = NullValue(); + Value fval; + jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom); + if (!js_GetMethod(cx, &v.toObject(), id, JSGET_NO_METHOD_BARRIER, &fval)) + return false; + if (js_IsCallable(fval)) { + if (!ExternalInvoke(cx, v, fval, 0, NULL, &rval)) + return false; + } + + return js_ValueToString(cx, rval); } namespace js { diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 00aa585f04f..efd54a9172a 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -4867,7 +4867,11 @@ xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool st JSBool xml_convert(JSContext *cx, JSObject *obj, JSType type, Value *rval) { - return js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, rval); + JSString *str = js_ValueToString(cx, ObjectValue(*obj)); + if (!str) + return false; + *rval = StringValue(str); + return true; } static JSBool diff --git a/js/src/tests/js1_7/regress/regress-351503-01.js b/js/src/tests/js1_7/regress/regress-351503-01.js index 728526b329c..47299c34412 100644 --- a/js/src/tests/js1_7/regress/regress-351503-01.js +++ b/js/src/tests/js1_7/regress/regress-351503-01.js @@ -80,7 +80,7 @@ catch(ex) } reportCompare(expect, actual, summary + ': 3'); -expect = 'TypeError: ({}) is not a function'; +expect = "TypeError: can't convert ({toString:{}}) to primitive type"; try { 3 + ({toString:({}) }) ; diff --git a/js/src/tests/js1_7/regress/regress-351503-02.js b/js/src/tests/js1_7/regress/regress-351503-02.js index 9730bd1e6f4..f6ef79d6d64 100644 --- a/js/src/tests/js1_7/regress/regress-351503-02.js +++ b/js/src/tests/js1_7/regress/regress-351503-02.js @@ -84,7 +84,7 @@ function test() } reportCompare(expect, actual, summary + ': 3'); - expect = 'TypeError: ({}) is not a function'; + expect = "TypeError: can't convert ({toString:{}}) to primitive type"; try { 3 + ({toString:({}) }) ;