Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit of user code calls, part 2 #2657

Merged
merged 9 commits into from
Sep 13, 2023
7 changes: 7 additions & 0 deletions polyfill/lib/calendar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2160,3 +2160,10 @@ impl['indian'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperIndian });
impl['buddhist'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperBuddhist });
impl['japanese'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperJapanese });
impl['gregory'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperGregory });

function calendarFieldsImpl(calendar, fieldNames) {
return impl[calendar].fields(fieldNames);
}
// Probably not what the intrinsics mechanism was intended for, but view this as
// an export of calendarFieldsImpl while avoiding circular dependencies
DefineIntrinsic('calendarFieldsImpl', calendarFieldsImpl);
160 changes: 136 additions & 24 deletions polyfill/lib/duration.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* global __debug__ */

import bigInt from 'big-integer';

import * as ES from './ecmascript.mjs';
import { MakeIntrinsicClass } from './intrinsicclass.mjs';
import {
Expand All @@ -13,6 +15,9 @@ import {
MILLISECONDS,
MICROSECONDS,
NANOSECONDS,
CALENDAR,
INSTANT,
TIME_ZONE,
CreateSlots,
GetSlot,
SetSlot
Expand Down Expand Up @@ -223,7 +228,7 @@ export class Duration {
let microseconds = GetSlot(this, MICROSECONDS);
let nanoseconds = GetSlot(this, NANOSECONDS);

let defaultLargestUnit = ES.DefaultTemporalLargestUnit(
const existingLargestUnit = ES.DefaultTemporalLargestUnit(
years,
months,
weeks,
Expand All @@ -244,7 +249,7 @@ export class Duration {
}

let largestUnit = ES.GetTemporalUnit(roundTo, 'largestUnit', 'datetime', undefined, ['auto']);
let relativeTo = ES.ToRelativeTemporalObject(roundTo);
let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(roundTo);
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
let smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'datetime', undefined);
Expand All @@ -254,7 +259,7 @@ export class Duration {
smallestUnitPresent = false;
smallestUnit = 'nanosecond';
}
defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit);
const defaultLargestUnit = ES.LargerOfTwoTemporalUnits(existingLargestUnit, smallestUnit);
let largestUnitPresent = true;
if (!largestUnit) {
largestUnitPresent = false;
Expand All @@ -279,13 +284,43 @@ export class Duration {
const maximum = maximumIncrements[smallestUnit];
if (maximum !== undefined) ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false);

const roundingGranularityIsNoop = smallestUnit === 'nanosecond' && roundingIncrement === 1;
const balancingRequested = largestUnit !== existingLargestUnit;
const calendarUnitsPresent = years !== 0 || months !== 0 || weeks !== 0;
const timeUnitsOverflowWillOccur =
minutes >= 60 || seconds >= 60 || milliseconds >= 1000 || microseconds >= 1000 || nanoseconds >= 1000;
const hoursToDaysConversionMayOccur = (days !== 0 && zonedRelativeTo) || hours >= 24;
if (
roundingGranularityIsNoop &&
!balancingRequested &&
!calendarUnitsPresent &&
!timeUnitsOverflowWillOccur &&
!hoursToDaysConversionMayOccur
) {
return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}

const plainRelativeToWillBeUsed =
smallestUnit === 'year' ||
smallestUnit === 'month' ||
smallestUnit === 'week' ||
years !== 0 ||
months !== 0 ||
weeks !== 0 ||
days !== 0;
if (zonedRelativeTo && plainRelativeToWillBeUsed) {
// Convert a ZonedDateTime relativeTo to PlainDate only if needed in one
// of the operations below, because the conversion is user visible
plainRelativeTo = ES.ToTemporalDate(zonedRelativeTo);
}

({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(
years,
months,
weeks,
days,
largestUnit,
relativeTo
plainRelativeTo
));
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.RoundDuration(
Expand All @@ -302,9 +337,10 @@ export class Duration {
roundingIncrement,
smallestUnit,
roundingMode,
relativeTo
plainRelativeTo,
zonedRelativeTo
));
if (ES.IsTemporalZonedDateTime(relativeTo)) {
if (zonedRelativeTo) {
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.AdjustRoundedDurationDays(
years,
Expand All @@ -320,7 +356,7 @@ export class Duration {
roundingIncrement,
smallestUnit,
roundingMode,
relativeTo
zonedRelativeTo
));
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDurationRelative(
days,
Expand All @@ -331,7 +367,7 @@ export class Duration {
microseconds,
nanoseconds,
largestUnit,
relativeTo
zonedRelativeTo
));
} else {
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration(
Expand All @@ -351,7 +387,7 @@ export class Duration {
weeks,
days,
largestUnit,
relativeTo
plainRelativeTo
));

return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
Expand All @@ -377,15 +413,30 @@ export class Duration {
} else {
totalOf = ES.GetOptionsObject(totalOf);
}
const relativeTo = ES.ToRelativeTemporalObject(totalOf);
let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(totalOf);
const unit = ES.GetTemporalUnit(totalOf, 'unit', 'datetime', ES.REQUIRED);

const plainRelativeToWillBeUsed =
unit === 'year' || unit === 'month' || unit === 'week' || years !== 0 || months !== 0 || weeks !== 0;
if (zonedRelativeTo !== undefined && plainRelativeToWillBeUsed) {
// Convert a ZonedDateTime relativeTo to PlainDate only if needed in one
// of the operations below, because the conversion is user visible
plainRelativeTo = ES.ToTemporalDate(zonedRelativeTo);
}

// Convert larger units down to days
({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(years, months, weeks, days, unit, relativeTo));
({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(
years,
months,
weeks,
days,
unit,
plainRelativeTo
));
// If the unit we're totalling is smaller than `days`, convert days down to that unit.
let balanceResult;
if (ES.IsTemporalZonedDateTime(relativeTo)) {
const intermediate = ES.MoveRelativeZonedDateTime(relativeTo, years, months, weeks, 0);
if (zonedRelativeTo) {
const intermediate = ES.MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, 0);
balanceResult = ES.BalancePossiblyInfiniteTimeDurationRelative(
days,
hours,
Expand Down Expand Up @@ -430,7 +481,8 @@ export class Duration {
1,
unit,
'trunc',
relativeTo
plainRelativeTo,
zonedRelativeTo
);
return total;
}
Expand Down Expand Up @@ -532,12 +584,11 @@ export class Duration {
one = ES.ToTemporalDuration(one);
two = ES.ToTemporalDuration(two);
options = ES.GetOptionsObject(options);
const relativeTo = ES.ToRelativeTemporalObject(options);
const y1 = GetSlot(one, YEARS);
const mon1 = GetSlot(one, MONTHS);
const w1 = GetSlot(one, WEEKS);
let d1 = GetSlot(one, DAYS);
const h1 = GetSlot(one, HOURS);
let h1 = GetSlot(one, HOURS);
const min1 = GetSlot(one, MINUTES);
const s1 = GetSlot(one, SECONDS);
const ms1 = GetSlot(one, MILLISECONDS);
Expand All @@ -547,20 +598,81 @@ export class Duration {
const mon2 = GetSlot(two, MONTHS);
const w2 = GetSlot(two, WEEKS);
let d2 = GetSlot(two, DAYS);
const h2 = GetSlot(two, HOURS);
let h2 = GetSlot(two, HOURS);
const min2 = GetSlot(two, MINUTES);
const s2 = GetSlot(two, SECONDS);
const ms2 = GetSlot(two, MILLISECONDS);
const µs2 = GetSlot(two, MICROSECONDS);
let ns2 = GetSlot(two, NANOSECONDS);
const shift1 = ES.CalculateOffsetShift(relativeTo, y1, mon1, w1, d1);
const shift2 = ES.CalculateOffsetShift(relativeTo, y2, mon2, w2, d2);
if (y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0) {
({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', relativeTo));
({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', relativeTo));

if (
y1 === y2 &&
mon1 === mon2 &&
w1 === w2 &&
d1 === d2 &&
h1 === h2 &&
min1 === min2 &&
s1 === s2 &&
ms1 === ms2 &&
µs1 === µs2 &&
ns1 === ns2
) {
return 0;
}
const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options);

const calendarUnitsPresent = y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0;

if (zonedRelativeTo && (calendarUnitsPresent || d1 != 0 || d2 !== 0)) {
const instant = GetSlot(zonedRelativeTo, INSTANT);
const timeZone = GetSlot(zonedRelativeTo, TIME_ZONE);
const calendar = GetSlot(zonedRelativeTo, CALENDAR);
const precalculatedPlainDateTime = ES.GetPlainDateTimeFor(timeZone, instant, calendar);

const after1 = ES.AddZonedDateTime(
instant,
timeZone,
calendar,
y1,
mon1,
w1,
d1,
h1,
min1,
s1,
ms1,
µs1,
ns1,
precalculatedPlainDateTime
);
const after2 = ES.AddZonedDateTime(
instant,
timeZone,
calendar,
y2,
mon2,
w2,
d2,
h2,
min2,
s2,
ms2,
µs2,
ns2,
precalculatedPlainDateTime
);
return ES.ComparisonResult(after1.minus(after2).toJSNumber());
}

if (calendarUnitsPresent) {
// plainRelativeTo may be undefined, and if so Unbalance will throw
({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', plainRelativeTo));
({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', plainRelativeTo));
}
ns1 = ES.TotalDurationNanoseconds(d1, h1, min1, s1, ms1, µs1, ns1, shift1);
ns2 = ES.TotalDurationNanoseconds(d2, h2, min2, s2, ms2, µs2, ns2, shift2);
h1 = bigInt(h1).add(bigInt(d1).multiply(24));
h2 = bigInt(h2).add(bigInt(d2).multiply(24));
ns1 = ES.TotalDurationNanoseconds(h1, min1, s1, ms1, µs1, ns1);
ns2 = ES.TotalDurationNanoseconds(h2, min2, s2, ms2, µs2, ns2);
return ES.ComparisonResult(ns1.minus(ns2).toJSNumber());
}
}
Expand Down
Loading