Bug 584811 - Date.prototype.toJSON isn't to spec. r=sayrer

This commit is contained in:
Jeff Walden 2010-08-13 10:42:31 -07:00
Родитель 9878311f34
Коммит d87ea1a7e8
6 изменённых файлов: 299 добавлений и 1 удалений

Просмотреть файл

@ -332,3 +332,4 @@ MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed wh
MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument")
MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 251, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}")
MSG_DEF(JSMSG_THROW_TYPE_ERROR, 252, 0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP, 253, 0, JSEXN_TYPEERR, "toISOString property is not callable")

Просмотреть файл

@ -148,6 +148,7 @@ const char *const js_common_atom_names[] = {
js_ignoreCase_str, /* ignoreCaseAtom */
js_index_str, /* indexAtom */
js_input_str, /* inputAtom */
"toISOString", /* toISOStringAtom */
js_iterator_str, /* iteratorAtom */
js_join_str, /* joinAtom */
js_lastIndex_str, /* lastIndexAtom */

Просмотреть файл

@ -340,6 +340,7 @@ struct JSAtomState
JSAtom *ignoreCaseAtom;
JSAtom *indexAtom;
JSAtom *inputAtom;
JSAtom *toISOStringAtom;
JSAtom *iteratorAtom;
JSAtom *joinAtom;
JSAtom *lastIndexAtom;

Просмотреть файл

@ -2012,6 +2012,58 @@ date_toISOString(JSContext *cx, uintN argc, Value *vp)
return date_utc_format(cx, vp, print_iso_string);
}
namespace {
/* ES5 15.9.5.44. */
JSBool
date_toJSON(JSContext *cx, uintN argc, Value *vp)
{
/* Step 1. */
JSObject *obj = ComputeThisFromVp(cx, vp);
if (!obj)
return false;
/* Step 2. */
Value &tv = vp[0];
if (!DefaultValue(cx, obj, JSTYPE_NUMBER, &tv))
return false;
/* Step 3. */
if (tv.isDouble() && !JSDOUBLE_IS_FINITE(tv.toDouble())) {
vp->setNull();
return true;
}
/* Step 4. */
Value &toISO = vp[0];
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.toISOStringAtom), &toISO))
return false;
/* Step 5. */
if (!js_IsCallable(toISO)) {
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
JSMSG_BAD_TOISOSTRING_PROP);
return false;
}
/* Step 6. */
LeaveTrace(cx);
InvokeArgsGuard args;
if (!cx->stack().pushInvokeArgs(cx, 0, args))
return false;
args.callee() = toISO;
args.thisv().setObject(*obj);
if (!Invoke(cx, args, 0))
return false;
*vp = args.rval();
return true;
}
}
/* for Date.toLocaleString; interface to PRMJTime date struct.
*/
static void
@ -2407,7 +2459,7 @@ static JSFunctionSpec date_methods[] = {
JS_FN("toDateString", date_toDateString, 0,0),
JS_FN("toTimeString", date_toTimeString, 0,0),
JS_FN("toISOString", date_toISOString, 0,0),
JS_FN(js_toJSON_str, date_toISOString, 0,0),
JS_FN(js_toJSON_str, date_toJSON, 1,0),
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, date_toSource, 0,0),
#endif

Просмотреть файл

@ -1,2 +1,3 @@
url-prefix ../../jsreftest.html?test=ecma_5/Date/
script 15.9.4.2.js
script toJSON-01.js

Просмотреть файл

@ -0,0 +1,242 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var gTestfile = 'toJSON-01.js';
//-----------------------------------------------------------------------------
var BUGNUMBER = 584811;
var summary = "Date.prototype.toJSON isn't to spec";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var called;
var dateToJSON = Date.prototype.toJSON;
assertEq(Date.prototype.hasOwnProperty("toJSON"), true);
assertEq(typeof dateToJSON, "function");
// brief test to exercise this outside of isolation, just for sanity
var invalidDate = new Date();
invalidDate.setTime(NaN);
assertEq(JSON.stringify({ p: invalidDate }), '{"p":null}');
/* 15.9.5.44 Date.prototype.toJSON ( key ) */
assertEq(dateToJSON.length, 1);
/*
* 1. Let O be the result of calling ToObject, giving it the this value as its
* argument.
*/
function strictThis() { "use strict"; return this; }
if (strictThis.call(null) === null)
{
try
{
dateToJSON.call(null);
throw new Error("should have thrown a TypeError");
}
catch (e)
{
assertEq(e instanceof TypeError, true,
"ToObject throws TypeError for null/undefined");
}
try
{
dateToJSON.call(undefined);
throw new Error("should have thrown a TypeError");
}
catch (e)
{
assertEq(e instanceof TypeError, true,
"ToObject throws TypeError for null/undefined");
}
}
/*
* 2. Let tv be ToPrimitive(O, hint Number).
* ...expands to:
* 1. Let valueOf be the result of calling the [[Get]] internal method of object O with argument "valueOf".
* 2. If IsCallable(valueOf) is true then,
* a. Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and
* an empty argument list.
* b. If val is a primitive value, return val.
* 3. Let toString be the result of calling the [[Get]] internal method of object O with argument "toString".
* 4. If IsCallable(toString) is true then,
* a. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and
* an empty argument list.
* b. If str is a primitive value, return str.
* 5. Throw a TypeError exception.
*/
try
{
var r = dateToJSON.call({ get valueOf() { throw 17; } });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e, 17, "bad exception: " + e);
}
called = false;
assertEq(dateToJSON.call({ valueOf: null,
toString: function() { called = true; return 12; },
toISOString: function() { return "ohai"; } }),
"ohai");
assertEq(called, true);
called = false;
assertEq(dateToJSON.call({ valueOf: function() { called = true; return 42; },
toISOString: function() { return null; } }),
null);
assertEq(called, true);
try
{
called = false;
dateToJSON.call({ valueOf: function() { called = true; return {}; },
get toString() { throw 42; } });
}
catch (e)
{
assertEq(called, true);
assertEq(e, 42, "bad exception: " + e);
}
called = false;
assertEq(dateToJSON.call({ valueOf: function() { called = true; return {}; },
get toString() { return function() { return 8675309; }; },
toISOString: function() { return true; } }),
true);
assertEq(called, true);
var asserted = false;
called = false;
assertEq(dateToJSON.call({ valueOf: function() { called = true; return {}; },
get toString()
{
assertEq(called, true);
asserted = true;
return function() { return 8675309; };
},
toISOString: function() { return NaN; } }),
NaN);
assertEq(asserted, true);
try
{
var r = dateToJSON.call({ valueOf: null, toString: null,
get toISOString()
{
throw new Error("shouldn't have been gotten");
} });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e instanceof TypeError, true, "bad exception: " + e);
}
/* 3. If tv is a Number and is not finite, return null. */
assertEq(dateToJSON.call({ valueOf: function() { return Infinity; } }), null);
assertEq(dateToJSON.call({ valueOf: function() { return -Infinity; } }), null);
assertEq(dateToJSON.call({ valueOf: function() { return NaN; } }), null);
assertEq(dateToJSON.call({ valueOf: function() { return Infinity; },
toISOString: function() { return {}; } }), null);
assertEq(dateToJSON.call({ valueOf: function() { return -Infinity; },
toISOString: function() { return []; } }), null);
assertEq(dateToJSON.call({ valueOf: function() { return NaN; },
toISOString: function() { return undefined; } }), null);
/*
* 4. Let toISO be the result of calling the [[Get]] internal method of O with
* argument "toISOString".
*/
try
{
var r = dateToJSON.call({ get toISOString() { throw 42; } });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e, 42, "bad exception: " + e);
}
/* 5. If IsCallable(toISO) is false, throw a TypeError exception. */
try
{
var r = dateToJSON.call({ toISOString: null });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e instanceof TypeError, true, "bad exception: " + e);
}
try
{
var r = dateToJSON.call({ toISOString: undefined });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e instanceof TypeError, true, "bad exception: " + e);
}
try
{
var r = dateToJSON.call({ toISOString: "oogabooga" });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e instanceof TypeError, true, "bad exception: " + e);
}
try
{
var r = dateToJSON.call({ toISOString: Math.PI });
throw new Error("didn't throw, returned: " + r);
}
catch (e)
{
assertEq(e instanceof TypeError, true, "bad exception: " + e);
}
/*
* 6. Return the result of calling the [[Call]] internal method of toISO with O
* as the this value and an empty argument list.
*/
var o =
{
toISOString: function(a)
{
called = true;
assertEq(this, o);
assertEq(a, undefined);
assertEq(arguments.length, 0);
return obj;
}
};
var obj = {};
called = false;
assertEq(dateToJSON.call(o), obj, "should have gotten obj back");
assertEq(called, true);
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");