ses
Version:
Hardened JavaScript for Fearless Cooperation
79 lines (69 loc) • 2.2 kB
JavaScript
import {
Number,
String,
TypeError,
defineProperty,
getOwnPropertyNames,
isPrimitive,
regexpExec,
} from './commons.js';
import { assert } from './error/assert.js';
const { Fail, quote: q } = assert;
const localePattern = /^(\w*[a-z])Locale([A-Z]\w*)$/;
// Use concise methods to obtain named functions without constructor
// behavior or `.prototype` property.
const tamedMethods = {
// See https://tc39.es/ecma262/#sec-string.prototype.localecompare
localeCompare(arg) {
if (this === null || this === undefined) {
throw TypeError(
'Cannot localeCompare with null or undefined "this" value',
);
}
const s = `${this}`;
const that = `${arg}`;
if (s < that) {
return -1;
}
if (s > that) {
return 1;
}
s === that || Fail`expected ${q(s)} and ${q(that)} to compare`;
return 0;
},
toString() {
return `${this}`;
},
};
const nonLocaleCompare = tamedMethods.localeCompare;
const numberToString = tamedMethods.toString;
export default function tameLocaleMethods(intrinsics, localeTaming = 'safe') {
if (localeTaming === 'unsafe') {
return;
}
defineProperty(String.prototype, 'localeCompare', {
value: nonLocaleCompare,
});
for (const intrinsicName of getOwnPropertyNames(intrinsics)) {
const intrinsic = intrinsics[intrinsicName];
if (!isPrimitive(intrinsic)) {
for (const methodName of getOwnPropertyNames(intrinsic)) {
const match = regexpExec(localePattern, methodName);
if (match) {
typeof intrinsic[methodName] === 'function' ||
Fail`expected ${q(methodName)} to be a function`;
const nonLocaleMethodName = `${match[1]}${match[2]}`;
const method = intrinsic[nonLocaleMethodName];
typeof method === 'function' ||
Fail`function ${q(nonLocaleMethodName)} not found`;
defineProperty(intrinsic, methodName, { value: method });
}
}
}
}
// Numbers are special because toString accepts a radix instead of ignoring
// all of the arguments that we would otherwise forward.
defineProperty(Number.prototype, 'toLocaleString', {
value: numberToString,
});
}