ses
Version:
Hardened JavaScript for Fearless Cooperation
128 lines (119 loc) • 3.5 kB
JavaScript
// @ts-check
import {
Date,
TypeError,
apply,
construct,
defineProperties,
} from './commons.js';
export default function tameDateConstructor() {
const OriginalDate = Date;
const DatePrototype = OriginalDate.prototype;
// Use concise methods to obtain named functions without constructors.
const tamedMethods = {
/**
* `%SharedDate%.now()` throw a `TypeError` starting with "secure mode".
* See https://github.com/endojs/endo/issues/910#issuecomment-1581855420
*/
now() {
throw TypeError('secure mode Calling %SharedDate%.now() throws');
},
};
/**
* Tame the Date constructor.
* See https://github.com/endojs/endo/issues/910#issuecomment-1581855420
*
* Common behavior
* * `new Date(x)` coerces x into a number and then returns a Date
* for that number of millis since the epoch
* * `new Date(NaN)` returns a Date object which stringifies to
* 'Invalid Date'
* * `new Date(undefined)` returns a Date object which stringifies to
* 'Invalid Date'
*
* OriginalDate (normal standard) behavior preserved by
* `%InitialDate%`.
* * `Date(anything)` gives a string with the current time
* * `new Date()` returns the current time, as a Date object
*
* `%SharedDate%` behavior
* * `Date(anything)` throws a TypeError starting with "secure mode"
* * `new Date()` throws a TypeError starting with "secure mode"
*
* @param {{powers?: string}} [opts]
*/
const makeDateConstructor = ({ powers = 'none' } = {}) => {
let ResultDate;
if (powers === 'original') {
// eslint-disable-next-line no-shadow
ResultDate = function Date(...rest) {
if (new.target === undefined) {
return apply(OriginalDate, undefined, rest);
}
return construct(OriginalDate, rest, new.target);
};
} else {
// eslint-disable-next-line no-shadow
ResultDate = function Date(...rest) {
if (new.target === undefined) {
throw TypeError(
'secure mode Calling %SharedDate% constructor as a function throws',
);
}
if (rest.length === 0) {
throw TypeError(
'secure mode Calling new %SharedDate%() with no arguments throws',
);
}
return construct(OriginalDate, rest, new.target);
};
}
defineProperties(ResultDate, {
length: { value: 7 },
prototype: {
value: DatePrototype,
writable: false,
enumerable: false,
configurable: false,
},
parse: {
value: OriginalDate.parse,
writable: true,
enumerable: false,
configurable: true,
},
UTC: {
value: OriginalDate.UTC,
writable: true,
enumerable: false,
configurable: true,
},
});
return ResultDate;
};
const InitialDate = makeDateConstructor({ powers: 'original' });
const SharedDate = makeDateConstructor({ powers: 'none' });
defineProperties(InitialDate, {
now: {
value: OriginalDate.now,
writable: true,
enumerable: false,
configurable: true,
},
});
defineProperties(SharedDate, {
now: {
value: tamedMethods.now,
writable: true,
enumerable: false,
configurable: true,
},
});
defineProperties(DatePrototype, {
constructor: { value: SharedDate },
});
return {
'%InitialDate%': InitialDate,
'%SharedDate%': SharedDate,
};
}