diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-02-03 21:00:46 -0500 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-02-04 13:47:50 +0000 |
commit | fd7d0a31d9966e74c3af51ac28f6397735b990e8 (patch) | |
tree | 91cfa614a84336976574db5ed62a6c949994ae17 /Userland/Libraries | |
parent | cbdbe0c5a2b1153890209b6942d8902226f56bd2 (diff) | |
download | serenity-fd7d0a31d9966e74c3af51ac28f6397735b990e8.zip |
LibJS: Explicitly handle invalid Date objects in local time setters
This is a normative change in the ECMA-262 spec:
https://github.com/tc39/ecma262/commit/ca53334
Diffstat (limited to 'Userland/Libraries')
9 files changed, 179 insertions, 57 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp index 7ea61a0e48..7163cadad8 100644 --- a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp @@ -378,31 +378,47 @@ static ThrowCompletionOr<Value> argument_or_value(GlobalObject& global_object, s return Value(fallback); } +static ThrowCompletionOr<Optional<Value>> argument_or_empty(GlobalObject& global_object, size_t index) +{ + auto& vm = global_object.vm(); + + if (vm.argument_count() > index) + return TRY(vm.argument(index).to_number(global_object)); + + return Optional<Value> {}; +} + // 21.4.4.20 Date.prototype.setDate ( date ), https://tc39.es/ecma262/#sec-date.prototype.setdate JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_date) { - // 1. Let t be LocalTime(? thisTimeValue(this value)). + // 1. Let t be ? thisTimeValue(this value). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - auto time = local_time(this_time.as_double()); // 2. Let dt be ? ToNumber(date). auto date = TRY(vm.argument(0).to_number(global_object)); - // 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). + // 3. If t is NaN, return NaN. + if (this_time.is_nan()) + return js_nan(); + + // 4. Set t to LocalTime(t). + auto time = local_time(this_time.as_double()); + + // 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). auto year = Value(year_from_time(time)); auto month = Value(month_from_time(time)); auto day = make_day(global_object, year, month, date); auto new_date = make_date(day, Value(time_within_day(time))); - // 4. Let u be TimeClip(UTC(newDate)). + // 6. Let u be TimeClip(UTC(newDate)). new_date = time_clip(global_object, Value(utc_time(new_date.as_double()))); - // 5. Set the [[DateValue]] internal slot of this Date object to u. + // 7. Set the [[DateValue]] internal slot of this Date object to u. auto* this_object = MUST(typed_this_object(global_object)); this_object->set_date_value(new_date.as_double()); - // 6. Return u. + // 8. Return u. return new_date; } @@ -412,14 +428,14 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_full_year) // 1. Let t be ? thisTimeValue(this value). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - // 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). + // 2. Let y be ? ToNumber(year). + auto year = TRY(vm.argument(0).to_number(global_object)); + + // 3. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). double time = 0; if (!this_time.is_nan()) time = local_time(this_time.as_double()); - // 3. Let y be ? ToNumber(year). - auto year = TRY(vm.argument(0).to_number(global_object)); - // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month). auto month = TRY(argument_or_value(global_object, 1, month_from_time(time))); @@ -446,155 +462,213 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_hours) { // 1. Let t be LocalTime(? thisTimeValue(this value)). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - auto time = local_time(this_time.as_double()); // 2. Let h be ? ToNumber(hour). auto hour = TRY(vm.argument(0).to_number(global_object)); - // 3. If min is not present, let m be MinFromTime(t); otherwise, let m be ? ToNumber(min). - auto minute = TRY(argument_or_value(global_object, 1, min_from_time(time))); + // 3. If min is present, let m be ? ToNumber(min). + auto minute = TRY(argument_or_empty(global_object, 1)); - // 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec). - auto second = TRY(argument_or_value(global_object, 2, sec_from_time(time))); + // 4. If sec is present, let s be ? ToNumber(sec). + auto second = TRY(argument_or_empty(global_object, 2)); - // 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - auto millisecond = TRY(argument_or_value(global_object, 3, ms_from_time(time))); + // 5. If ms is present, let milli be ? ToNumber(ms). + auto millisecond = TRY(argument_or_empty(global_object, 3)); - // 6. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)). - auto new_time = make_time(global_object, hour, minute, second, millisecond); + // 6. If t is NaN, return NaN. + if (this_time.is_nan()) + return js_nan(); + + // 7. Set t to LocalTime(t). + auto time = local_time(this_time.as_double()); + + // 8. If min is not present, let m be MinFromTime(t). + if (!minute.has_value()) + minute = Value(min_from_time(time)); + + // 9. If sec is not present, let s be SecFromTime(t). + if (!second.has_value()) + second = Value(sec_from_time(time)); + + // 10. If ms is not present, let milli be msFromTime(t). + if (!millisecond.has_value()) + millisecond = Value(ms_from_time(time)); + + // 11. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)). + auto new_time = make_time(global_object, hour, *minute, *second, *millisecond); auto date = make_date(Value(day(time)), new_time); - // 7. Let u be TimeClip(UTC(date)). + // 12. Let u be TimeClip(UTC(date)). date = time_clip(global_object, Value(utc_time(date.as_double()))); - // 8. Set the [[DateValue]] internal slot of this Date object to u. + // 13. Set the [[DateValue]] internal slot of this Date object to u. auto* this_object = MUST(typed_this_object(global_object)); this_object->set_date_value(date.as_double()); - // 9. Return u. + // 14. Return u. return date; } // 21.4.4.23 Date.prototype.setMilliseconds ( ms ), https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_milliseconds) { - // 1. Let t be LocalTime(? thisTimeValue(this value)). + // 1. Let t be ? thisTimeValue(this value). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - auto time = local_time(this_time.as_double()); // 2. Set ms to ? ToNumber(ms). auto millisecond = TRY(vm.argument(0).to_number(global_object)); - // 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms). + // 3. If t is NaN, return NaN. + if (this_time.is_nan()) + return js_nan(); + + // 4. Set t to LocalTime(t). + auto time = local_time(this_time.as_double()); + + // 5. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms). auto hour = Value(hour_from_time(time)); auto minute = Value(min_from_time(time)); auto second = Value(sec_from_time(time)); auto new_time = make_time(global_object, hour, minute, second, millisecond); - // 4. Let u be TimeClip(UTC(MakeDate(Day(t), time))). + // 6. Let u be TimeClip(UTC(MakeDate(Day(t), time))). auto date = make_date(Value(day(time)), new_time); date = time_clip(global_object, Value(utc_time(date.as_double()))); - // 5. Set the [[DateValue]] internal slot of this Date object to u. + // 7. Set the [[DateValue]] internal slot of this Date object to u. auto* this_object = MUST(typed_this_object(global_object)); this_object->set_date_value(date.as_double()); - // 6. Return u. + // 8. Return u. return date; } // 21.4.4.24 Date.prototype.setMinutes ( min [ , sec [ , ms ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setminutes JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_minutes) { - // 1. Let t be LocalTime(? thisTimeValue(this value)). + // 1. Let t be ? thisTimeValue(this value). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - auto time = local_time(this_time.as_double()); // 2. Let m be ? ToNumber(min). auto minute = TRY(vm.argument(0).to_number(global_object)); - // 3. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec). - auto second = TRY(argument_or_value(global_object, 1, sec_from_time(time))); + // 3. If sec is present, let s be ? ToNumber(sec). + auto second = TRY(argument_or_empty(global_object, 1)); - // 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - auto millisecond = TRY(argument_or_value(global_object, 2, ms_from_time(time))); + // 4. If ms is present, let milli be ? ToNumber(ms). + auto millisecond = TRY(argument_or_empty(global_object, 2)); + + // 5. If t is NaN, return NaN. + if (this_time.is_nan()) + return js_nan(); + + // 6. Set t to LocalTime(t). + auto time = local_time(this_time.as_double()); + + // 7. If sec is not present, let s be SecFromTime(t). + if (!second.has_value()) + second = Value(sec_from_time(time)); + + // 8. If ms is not present, let milli be msFromTime(t). + if (!millisecond.has_value()) + millisecond = Value(ms_from_time(time)); - // 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). + // 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). auto hour = Value(hour_from_time(time)); - auto new_time = make_time(global_object, hour, minute, second, millisecond); + auto new_time = make_time(global_object, hour, minute, *second, *millisecond); auto date = make_date(Value(day(time)), new_time); - // 6. Let u be TimeClip(UTC(date)). + // 10. Let u be TimeClip(UTC(date)). date = time_clip(global_object, Value(utc_time(date.as_double()))); - // 7. Set the [[DateValue]] internal slot of this Date object to u. + // 11. Set the [[DateValue]] internal slot of this Date object to u. auto* this_object = MUST(typed_this_object(global_object)); this_object->set_date_value(date.as_double()); - // 8. Return u. + // 12. Return u. return date; } // 21.4.4.25 Date.prototype.setMonth ( month [ , date ] ), https://tc39.es/ecma262/#sec-date.prototype.setmonth JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_month) { - // 1. Let t be LocalTime(? thisTimeValue(this value)). + // 1. Let t be ? thisTimeValue(this value). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - auto time = local_time(this_time.as_double()); // 2. Let m be ? ToNumber(month). auto month = TRY(vm.argument(0).to_number(global_object)); - // 3. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date). - auto date = TRY(argument_or_value(global_object, 1, date_from_time(time))); + // 3. If date is present, let dt be ? ToNumber(date). + auto date = TRY(argument_or_empty(global_object, 1)); - // 4. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). + // 4. If t is NaN, return NaN. + if (this_time.is_nan()) + return js_nan(); + + // 5. Set t to LocalTime(t). + auto time = local_time(this_time.as_double()); + + // 6. If date is not present, let dt be DateFromTime(t). + if (!date.has_value()) + date = Value(date_from_time(time)); + + // 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). auto year = Value(year_from_time(time)); - auto day = make_day(global_object, year, month, date); + auto day = make_day(global_object, year, month, *date); auto new_date = make_date(day, Value(time_within_day(time))); - // 5. Let u be TimeClip(UTC(newDate)). + // 8. Let u be TimeClip(UTC(newDate)). new_date = time_clip(global_object, Value(utc_time(new_date.as_double()))); - // 6. Set the [[DateValue]] internal slot of this Date object to u. + // 9. Set the [[DateValue]] internal slot of this Date object to u. auto* this_object = MUST(typed_this_object(global_object)); this_object->set_date_value(new_date.as_double()); - // 7. Return u. + // 10. Return u. return new_date; } // 21.4.4.26 Date.prototype.setSeconds ( sec [ , ms ] ), https://tc39.es/ecma262/#sec-date.prototype.setseconds JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_seconds) { - // 1. Let t be LocalTime(? thisTimeValue(this value)). + // 1. Let t be ? thisTimeValue(this value). auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); - auto time = local_time(this_time.as_double()); // 2. Let s be ? ToNumber(sec). auto second = TRY(vm.argument(0).to_number(global_object)); - // 3. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - auto millisecond = TRY(argument_or_value(global_object, 1, ms_from_time(time))); + // 3. If ms is present, let milli be ? ToNumber(ms). + auto millisecond = TRY(argument_or_empty(global_object, 1)); + + // 4. If t is NaN, return NaN. + if (this_time.is_nan()) + return js_nan(); + + // 5. Set t to LocalTime(t). + auto time = local_time(this_time.as_double()); - // 4. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). + // 6. If ms is not present, let milli be msFromTime(t). + if (!millisecond.has_value()) + millisecond = Value(ms_from_time(time)); + + // 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). auto hour = Value(hour_from_time(time)); auto minute = Value(min_from_time(time)); - auto new_time = make_time(global_object, hour, minute, second, millisecond); + auto new_time = make_time(global_object, hour, minute, second, *millisecond); auto new_date = make_date(Value(day(time)), new_time); - // 5. Let u be TimeClip(UTC(date)). + // 8. Let u be TimeClip(UTC(date)). new_date = time_clip(global_object, Value(utc_time(new_date.as_double()))); - // 6. Set the [[DateValue]] internal slot of this Date object to u. + // 9. Set the [[DateValue]] internal slot of this Date object to u. auto* this_object = MUST(typed_this_object(global_object)); this_object->set_date_value(new_date.as_double()); - // 7. Return u. + // 10. Return u. return new_date; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js index fc7807eaa4..12cc7b6b49 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js @@ -30,3 +30,9 @@ test("Day as argument", () => { expect(date.getSeconds()).toBe(0); expect(date.getMilliseconds()).toBe(0); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setDate(15)).toBeNaN(); + expect(date.getDate()).toBeNaN(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setFullYear.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setFullYear.js index 9c5dec7de9..48976eaa9a 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setFullYear.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setFullYear.js @@ -110,3 +110,9 @@ test("Make Invalid Date valid again", () => { expect(date.getSeconds()).toBe(0); expect(date.getMilliseconds()).toBe(0); }); + +test("invalid date", () => { + let date = new Date(NaN); + date.setFullYear(2022); + expect(date.getFullYear()).toBe(2022); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setHours.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setHours.js index df718dd3b4..cd1a53844b 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setHours.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setHours.js @@ -25,3 +25,9 @@ test("basic functionality", () => { d.setHours("a"); expect(d.getHours()).toBe(NaN); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setHours(2)).toBeNaN(); + expect(date.getHours()).toBeNaN(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMilliseconds.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMilliseconds.js index 67b71cfcc7..daa5e77790 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMilliseconds.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMilliseconds.js @@ -10,3 +10,9 @@ test("basic functionality", () => { d.setMilliseconds("a"); expect(d.getMilliseconds()).toBe(NaN); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setMilliseconds(2)).toBeNaN(); + expect(date.getMilliseconds()).toBeNaN(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMinutes.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMinutes.js index d5243ecd08..f6f17b5143 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMinutes.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMinutes.js @@ -19,3 +19,9 @@ test("basic functionality", () => { d.setMinutes("a"); expect(d.getMinutes()).toBe(NaN); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setMinutes(2)).toBeNaN(); + expect(date.getMinutes()).toBeNaN(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js index 1ae040d833..f6df479f9e 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js @@ -61,3 +61,9 @@ test("NaN or undefined in any arguments", () => { date.setMonth(2021, undefined); expect(date.getTime()).toBe(NaN); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setMonth(2)).toBeNaN(); + expect(date.getMonth()).toBeNaN(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setSeconds.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setSeconds.js index 06f7521a1e..9e5f82f508 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setSeconds.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setSeconds.js @@ -14,3 +14,9 @@ test("basic functionality", () => { d.setSeconds("a"); expect(d.getSeconds()).toBe(NaN); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setSeconds(2)).toBeNaN(); + expect(date.getSeconds()).toBeNaN(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setTime.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setTime.js index c23267df4a..a26de088e7 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setTime.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setTime.js @@ -45,3 +45,9 @@ test("Make Invalid Date valid again", () => { expect(date.getUTCSeconds()).toBe(46); expect(date.getUTCMilliseconds()).toBe(0); }); + +test("invalid date", () => { + let date = new Date(NaN); + expect(date.setTime(1622993746000)).toBe(1622993746000); + expect(date.getTime()).toBe(1622993746000); +}); |