mirror of
https://github.com/nodejs/node.git
synced 2025-05-12 09:36:17 +00:00

PR-URL: https://github.com/nodejs/node/pull/46970 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
738 lines
21 KiB
JavaScript
738 lines
21 KiB
JavaScript
'use strict';
|
|
|
|
/* eslint-disable node-core/prefer-primordials */
|
|
|
|
// This file subclasses and stores the JS builtins that come from the VM
|
|
// so that Node.js's builtin modules do not need to later look these up from
|
|
// the global proxy, which can be mutated by users.
|
|
|
|
// Use of primordials have sometimes a dramatic impact on performance, please
|
|
// benchmark all changes made in performance-sensitive areas of the codebase.
|
|
// See: https://github.com/nodejs/node/pull/38248
|
|
|
|
const {
|
|
defineProperty: ReflectDefineProperty,
|
|
getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor,
|
|
ownKeys: ReflectOwnKeys,
|
|
} = Reflect;
|
|
|
|
// `uncurryThis` is equivalent to `func => Function.prototype.call.bind(func)`.
|
|
// It is using `bind.bind(call)` to avoid using `Function.prototype.bind`
|
|
// and `Function.prototype.call` after it may have been mutated by users.
|
|
const { apply, bind, call } = Function.prototype;
|
|
const uncurryThis = bind.bind(call);
|
|
primordials.uncurryThis = uncurryThis;
|
|
|
|
// `applyBind` is equivalent to `func => Function.prototype.apply.bind(func)`.
|
|
// It is using `bind.bind(apply)` to avoid using `Function.prototype.bind`
|
|
// and `Function.prototype.apply` after it may have been mutated by users.
|
|
const applyBind = bind.bind(apply);
|
|
primordials.applyBind = applyBind;
|
|
|
|
// Methods that accept a variable number of arguments, and thus it's useful to
|
|
// also create `${prefix}${key}Apply`, which uses `Function.prototype.apply`,
|
|
// instead of `Function.prototype.call`, and thus doesn't require iterator
|
|
// destructuring.
|
|
const varargsMethods = [
|
|
// 'ArrayPrototypeConcat' is omitted, because it performs the spread
|
|
// on its own for arrays and array-likes with a truthy
|
|
// @@isConcatSpreadable symbol property.
|
|
'ArrayOf',
|
|
'ArrayPrototypePush',
|
|
'ArrayPrototypeUnshift',
|
|
// 'FunctionPrototypeCall' is omitted, since there's 'ReflectApply'
|
|
// and 'FunctionPrototypeApply'.
|
|
'MathHypot',
|
|
'MathMax',
|
|
'MathMin',
|
|
'StringFromCharCode',
|
|
'StringFromCodePoint',
|
|
'StringPrototypeConcat',
|
|
'TypedArrayOf',
|
|
];
|
|
|
|
function getNewKey(key) {
|
|
return typeof key === 'symbol' ?
|
|
`Symbol${key.description[7].toUpperCase()}${key.description.slice(8)}` :
|
|
`${key[0].toUpperCase()}${key.slice(1)}`;
|
|
}
|
|
|
|
function copyAccessor(dest, prefix, key, { enumerable, get, set }) {
|
|
ReflectDefineProperty(dest, `${prefix}Get${key}`, {
|
|
__proto__: null,
|
|
value: uncurryThis(get),
|
|
enumerable,
|
|
});
|
|
if (set !== undefined) {
|
|
ReflectDefineProperty(dest, `${prefix}Set${key}`, {
|
|
__proto__: null,
|
|
value: uncurryThis(set),
|
|
enumerable,
|
|
});
|
|
}
|
|
}
|
|
|
|
function copyPropsRenamed(src, dest, prefix) {
|
|
for (const key of ReflectOwnKeys(src)) {
|
|
const newKey = getNewKey(key);
|
|
const desc = ReflectGetOwnPropertyDescriptor(src, key);
|
|
if ('get' in desc) {
|
|
copyAccessor(dest, prefix, newKey, desc);
|
|
} else {
|
|
const name = `${prefix}${newKey}`;
|
|
ReflectDefineProperty(dest, name, { __proto__: null, ...desc });
|
|
if (varargsMethods.includes(name)) {
|
|
ReflectDefineProperty(dest, `${name}Apply`, {
|
|
__proto__: null,
|
|
// `src` is bound as the `this` so that the static `this` points
|
|
// to the object it was defined on,
|
|
// e.g.: `ArrayOfApply` gets a `this` of `Array`:
|
|
value: applyBind(desc.value, src),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function copyPropsRenamedBound(src, dest, prefix) {
|
|
for (const key of ReflectOwnKeys(src)) {
|
|
const newKey = getNewKey(key);
|
|
const desc = ReflectGetOwnPropertyDescriptor(src, key);
|
|
if ('get' in desc) {
|
|
copyAccessor(dest, prefix, newKey, desc);
|
|
} else {
|
|
const { value } = desc;
|
|
if (typeof value === 'function') {
|
|
desc.value = value.bind(src);
|
|
}
|
|
|
|
const name = `${prefix}${newKey}`;
|
|
ReflectDefineProperty(dest, name, { __proto__: null, ...desc });
|
|
if (varargsMethods.includes(name)) {
|
|
ReflectDefineProperty(dest, `${name}Apply`, {
|
|
__proto__: null,
|
|
value: applyBind(value, src),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function copyPrototype(src, dest, prefix) {
|
|
for (const key of ReflectOwnKeys(src)) {
|
|
const newKey = getNewKey(key);
|
|
const desc = ReflectGetOwnPropertyDescriptor(src, key);
|
|
if ('get' in desc) {
|
|
copyAccessor(dest, prefix, newKey, desc);
|
|
} else {
|
|
const { value } = desc;
|
|
if (typeof value === 'function') {
|
|
desc.value = uncurryThis(value);
|
|
}
|
|
|
|
const name = `${prefix}${newKey}`;
|
|
ReflectDefineProperty(dest, name, { __proto__: null, ...desc });
|
|
if (varargsMethods.includes(name)) {
|
|
ReflectDefineProperty(dest, `${name}Apply`, {
|
|
__proto__: null,
|
|
value: applyBind(value),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create copies of configurable value properties of the global object
|
|
[
|
|
'Proxy',
|
|
'globalThis',
|
|
].forEach((name) => {
|
|
// eslint-disable-next-line no-restricted-globals
|
|
primordials[name] = globalThis[name];
|
|
});
|
|
|
|
// Create copies of URI handling functions
|
|
[
|
|
decodeURI,
|
|
decodeURIComponent,
|
|
encodeURI,
|
|
encodeURIComponent,
|
|
].forEach((fn) => {
|
|
primordials[fn.name] = fn;
|
|
});
|
|
|
|
// Create copies of legacy functions
|
|
[
|
|
escape,
|
|
eval,
|
|
unescape,
|
|
].forEach((fn) => {
|
|
primordials[fn.name] = fn;
|
|
});
|
|
|
|
// Create copies of the namespace objects
|
|
[
|
|
'JSON',
|
|
'Math',
|
|
'Proxy',
|
|
'Reflect',
|
|
].forEach((name) => {
|
|
// eslint-disable-next-line no-restricted-globals
|
|
copyPropsRenamed(globalThis[name], primordials, name);
|
|
});
|
|
|
|
// Create copies of intrinsic objects
|
|
[
|
|
'AggregateError',
|
|
'Array',
|
|
'ArrayBuffer',
|
|
'BigInt',
|
|
'BigInt64Array',
|
|
'BigUint64Array',
|
|
'Boolean',
|
|
'DataView',
|
|
'Date',
|
|
'Error',
|
|
'EvalError',
|
|
'FinalizationRegistry',
|
|
'Float32Array',
|
|
'Float64Array',
|
|
'Function',
|
|
'Int16Array',
|
|
'Int32Array',
|
|
'Int8Array',
|
|
'Map',
|
|
'Number',
|
|
'Object',
|
|
'RangeError',
|
|
'ReferenceError',
|
|
'RegExp',
|
|
'Set',
|
|
'String',
|
|
'Symbol',
|
|
'SyntaxError',
|
|
'TypeError',
|
|
'URIError',
|
|
'Uint16Array',
|
|
'Uint32Array',
|
|
'Uint8Array',
|
|
'Uint8ClampedArray',
|
|
'WeakMap',
|
|
'WeakRef',
|
|
'WeakSet',
|
|
].forEach((name) => {
|
|
// eslint-disable-next-line no-restricted-globals
|
|
const original = globalThis[name];
|
|
primordials[name] = original;
|
|
copyPropsRenamed(original, primordials, name);
|
|
copyPrototype(original.prototype, primordials, `${name}Prototype`);
|
|
});
|
|
|
|
// Create copies of intrinsic objects that require a valid `this` to call
|
|
// static methods.
|
|
// Refs: https://www.ecma-international.org/ecma-262/#sec-promise.all
|
|
[
|
|
'Promise',
|
|
].forEach((name) => {
|
|
// eslint-disable-next-line no-restricted-globals
|
|
const original = globalThis[name];
|
|
primordials[name] = original;
|
|
copyPropsRenamedBound(original, primordials, name);
|
|
copyPrototype(original.prototype, primordials, `${name}Prototype`);
|
|
});
|
|
|
|
// Create copies of abstract intrinsic objects that are not directly exposed
|
|
// on the global object.
|
|
// Refs: https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object
|
|
[
|
|
{ name: 'TypedArray', original: Reflect.getPrototypeOf(Uint8Array) },
|
|
{ name: 'ArrayIterator', original: {
|
|
prototype: Reflect.getPrototypeOf(Array.prototype[Symbol.iterator]()),
|
|
} },
|
|
{ name: 'StringIterator', original: {
|
|
prototype: Reflect.getPrototypeOf(String.prototype[Symbol.iterator]()),
|
|
} },
|
|
].forEach(({ name, original }) => {
|
|
primordials[name] = original;
|
|
// The static %TypedArray% methods require a valid `this`, but can't be bound,
|
|
// as they need a subclass constructor as the receiver:
|
|
copyPrototype(original, primordials, name);
|
|
copyPrototype(original.prototype, primordials, `${name}Prototype`);
|
|
});
|
|
|
|
primordials.IteratorPrototype = Reflect.getPrototypeOf(primordials.ArrayIteratorPrototype);
|
|
|
|
/* eslint-enable node-core/prefer-primordials */
|
|
|
|
const {
|
|
Array: ArrayConstructor,
|
|
ArrayPrototypeForEach,
|
|
ArrayPrototypeMap,
|
|
FinalizationRegistry,
|
|
FunctionPrototypeCall,
|
|
Map,
|
|
ObjectDefineProperties,
|
|
ObjectDefineProperty,
|
|
ObjectFreeze,
|
|
ObjectSetPrototypeOf,
|
|
Promise,
|
|
PromisePrototypeThen,
|
|
PromiseResolve,
|
|
ReflectApply,
|
|
ReflectConstruct,
|
|
ReflectSet,
|
|
ReflectGet,
|
|
RegExp,
|
|
RegExpPrototype,
|
|
RegExpPrototypeExec,
|
|
RegExpPrototypeGetDotAll,
|
|
RegExpPrototypeGetFlags,
|
|
RegExpPrototypeGetGlobal,
|
|
RegExpPrototypeGetHasIndices,
|
|
RegExpPrototypeGetIgnoreCase,
|
|
RegExpPrototypeGetMultiline,
|
|
RegExpPrototypeGetSource,
|
|
RegExpPrototypeGetSticky,
|
|
RegExpPrototypeGetUnicode,
|
|
Set,
|
|
SymbolIterator,
|
|
SymbolMatch,
|
|
SymbolMatchAll,
|
|
SymbolReplace,
|
|
SymbolSearch,
|
|
SymbolSpecies,
|
|
SymbolSplit,
|
|
WeakMap,
|
|
WeakRef,
|
|
WeakSet,
|
|
} = primordials;
|
|
|
|
|
|
/**
|
|
* Creates a class that can be safely iterated over.
|
|
*
|
|
* Because these functions are used by `makeSafe`, which is exposed on the
|
|
* `primordials` object, it's important to use const references to the
|
|
* primordials that they use.
|
|
*
|
|
* @template {Iterable} T
|
|
* @template {*} TReturn
|
|
* @template {*} TNext
|
|
* @param {(self: T) => IterableIterator<T>} factory
|
|
* @param {(...args: [] | [TNext]) => IteratorResult<T, TReturn>} next
|
|
* @returns {Iterator<T, TReturn, TNext>}
|
|
*/
|
|
const createSafeIterator = (factory, next) => {
|
|
class SafeIterator {
|
|
constructor(iterable) {
|
|
this._iterator = factory(iterable);
|
|
}
|
|
next() {
|
|
return next(this._iterator);
|
|
}
|
|
[SymbolIterator]() {
|
|
return this;
|
|
}
|
|
}
|
|
ObjectSetPrototypeOf(SafeIterator.prototype, null);
|
|
ObjectFreeze(SafeIterator.prototype);
|
|
ObjectFreeze(SafeIterator);
|
|
return SafeIterator;
|
|
};
|
|
|
|
primordials.SafeArrayIterator = createSafeIterator(
|
|
primordials.ArrayPrototypeSymbolIterator,
|
|
primordials.ArrayIteratorPrototypeNext,
|
|
);
|
|
primordials.SafeStringIterator = createSafeIterator(
|
|
primordials.StringPrototypeSymbolIterator,
|
|
primordials.StringIteratorPrototypeNext,
|
|
);
|
|
|
|
const copyProps = (src, dest) => {
|
|
ArrayPrototypeForEach(ReflectOwnKeys(src), (key) => {
|
|
if (!ReflectGetOwnPropertyDescriptor(dest, key)) {
|
|
ReflectDefineProperty(
|
|
dest,
|
|
key,
|
|
{ __proto__: null, ...ReflectGetOwnPropertyDescriptor(src, key) });
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @type {typeof primordials.makeSafe}
|
|
*/
|
|
const makeSafe = (unsafe, safe) => {
|
|
if (SymbolIterator in unsafe.prototype) {
|
|
const dummy = new unsafe();
|
|
let next; // We can reuse the same `next` method.
|
|
|
|
ArrayPrototypeForEach(ReflectOwnKeys(unsafe.prototype), (key) => {
|
|
if (!ReflectGetOwnPropertyDescriptor(safe.prototype, key)) {
|
|
const desc = ReflectGetOwnPropertyDescriptor(unsafe.prototype, key);
|
|
if (
|
|
typeof desc.value === 'function' &&
|
|
desc.value.length === 0 &&
|
|
SymbolIterator in (FunctionPrototypeCall(desc.value, dummy) ?? {})
|
|
) {
|
|
const createIterator = uncurryThis(desc.value);
|
|
next ??= uncurryThis(createIterator(dummy).next);
|
|
const SafeIterator = createSafeIterator(createIterator, next);
|
|
desc.value = function() {
|
|
return new SafeIterator(this);
|
|
};
|
|
}
|
|
ReflectDefineProperty(safe.prototype, key, { __proto__: null, ...desc });
|
|
}
|
|
});
|
|
} else {
|
|
copyProps(unsafe.prototype, safe.prototype);
|
|
}
|
|
copyProps(unsafe, safe);
|
|
|
|
ObjectSetPrototypeOf(safe.prototype, null);
|
|
ObjectFreeze(safe.prototype);
|
|
ObjectFreeze(safe);
|
|
return safe;
|
|
};
|
|
primordials.makeSafe = makeSafe;
|
|
|
|
// Subclass the constructors because we need to use their prototype
|
|
// methods later.
|
|
// Defining the `constructor` is necessary here to avoid the default
|
|
// constructor which uses the user-mutable `%ArrayIteratorPrototype%.next`.
|
|
primordials.SafeMap = makeSafe(
|
|
Map,
|
|
class SafeMap extends Map {
|
|
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
|
|
},
|
|
);
|
|
primordials.SafeWeakMap = makeSafe(
|
|
WeakMap,
|
|
class SafeWeakMap extends WeakMap {
|
|
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
|
|
},
|
|
);
|
|
|
|
primordials.SafeSet = makeSafe(
|
|
Set,
|
|
class SafeSet extends Set {
|
|
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
|
|
},
|
|
);
|
|
primordials.SafeWeakSet = makeSafe(
|
|
WeakSet,
|
|
class SafeWeakSet extends WeakSet {
|
|
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
|
|
},
|
|
);
|
|
|
|
primordials.SafeFinalizationRegistry = makeSafe(
|
|
FinalizationRegistry,
|
|
class SafeFinalizationRegistry extends FinalizationRegistry {
|
|
// eslint-disable-next-line no-useless-constructor
|
|
constructor(cleanupCallback) { super(cleanupCallback); }
|
|
},
|
|
);
|
|
primordials.SafeWeakRef = makeSafe(
|
|
WeakRef,
|
|
class SafeWeakRef extends WeakRef {
|
|
// eslint-disable-next-line no-useless-constructor
|
|
constructor(target) { super(target); }
|
|
},
|
|
);
|
|
|
|
const SafePromise = makeSafe(
|
|
Promise,
|
|
class SafePromise extends Promise {
|
|
// eslint-disable-next-line no-useless-constructor
|
|
constructor(executor) { super(executor); }
|
|
},
|
|
);
|
|
|
|
/**
|
|
* Attaches a callback that is invoked when the Promise is settled (fulfilled or
|
|
* rejected). The resolved value cannot be modified from the callback.
|
|
* Prefer using async functions when possible.
|
|
* @param {Promise<any>} thisPromise
|
|
* @param {() => void) | undefined | null} onFinally The callback to execute
|
|
* when the Promise is settled (fulfilled or rejected).
|
|
* @returns {Promise} A Promise for the completion of the callback.
|
|
*/
|
|
primordials.SafePromisePrototypeFinally = (thisPromise, onFinally) =>
|
|
// Wrapping on a new Promise is necessary to not expose the SafePromise
|
|
// prototype to user-land.
|
|
new Promise((a, b) =>
|
|
new SafePromise((a, b) => PromisePrototypeThen(thisPromise, a, b))
|
|
.finally(onFinally)
|
|
.then(a, b),
|
|
);
|
|
|
|
primordials.AsyncIteratorPrototype =
|
|
primordials.ReflectGetPrototypeOf(
|
|
primordials.ReflectGetPrototypeOf(
|
|
async function* () {}).prototype);
|
|
|
|
const arrayToSafePromiseIterable = (promises, mapFn) =>
|
|
new primordials.SafeArrayIterator(
|
|
ArrayPrototypeMap(
|
|
promises,
|
|
(promise, i) =>
|
|
new SafePromise((a, b) => PromisePrototypeThen(mapFn == null ? promise : mapFn(promise, i), a, b)),
|
|
),
|
|
);
|
|
|
|
/**
|
|
* @template T,U
|
|
* @param {Array<T | PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<Awaited<U>[]>}
|
|
*/
|
|
primordials.SafePromiseAll = (promises, mapFn) =>
|
|
// Wrapping on a new Promise is necessary to not expose the SafePromise
|
|
// prototype to user-land.
|
|
new Promise((a, b) =>
|
|
SafePromise.all(arrayToSafePromiseIterable(promises, mapFn)).then(a, b),
|
|
);
|
|
|
|
/**
|
|
* Should only be used for internal functions, this would produce similar
|
|
* results as `Promise.all` but without prototype pollution, and the return
|
|
* value is not a genuine Array but an array-like object.
|
|
* @template T,U
|
|
* @param {ArrayLike<T | PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<ArrayLike<Awaited<U>>>}
|
|
*/
|
|
primordials.SafePromiseAllReturnArrayLike = (promises, mapFn) =>
|
|
new Promise((resolve, reject) => {
|
|
const { length } = promises;
|
|
|
|
const returnVal = ArrayConstructor(length);
|
|
ObjectSetPrototypeOf(returnVal, null);
|
|
if (length === 0) resolve(returnVal);
|
|
|
|
let pendingPromises = length;
|
|
for (let i = 0; i < length; i++) {
|
|
const promise = mapFn != null ? mapFn(promises[i], i) : promises[i];
|
|
PromisePrototypeThen(PromiseResolve(promise), (result) => {
|
|
returnVal[i] = result;
|
|
if (--pendingPromises === 0) resolve(returnVal);
|
|
}, reject);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Should only be used when we only care about waiting for all the promises to
|
|
* resolve, not what value they resolve to.
|
|
* @template T,U
|
|
* @param {ArrayLike<T | PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
primordials.SafePromiseAllReturnVoid = (promises, mapFn) =>
|
|
new Promise((resolve, reject) => {
|
|
let pendingPromises = promises.length;
|
|
if (pendingPromises === 0) resolve();
|
|
for (let i = 0; i < promises.length; i++) {
|
|
const promise = mapFn != null ? mapFn(promises[i], i) : promises[i];
|
|
PromisePrototypeThen(PromiseResolve(promise), () => {
|
|
if (--pendingPromises === 0) resolve();
|
|
}, reject);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @template T,U
|
|
* @param {Array<T|PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<PromiseSettledResult<any>[]>}
|
|
*/
|
|
primordials.SafePromiseAllSettled = (promises, mapFn) =>
|
|
// Wrapping on a new Promise is necessary to not expose the SafePromise
|
|
// prototype to user-land.
|
|
new Promise((a, b) =>
|
|
SafePromise.allSettled(arrayToSafePromiseIterable(promises, mapFn)).then(a, b),
|
|
);
|
|
|
|
/**
|
|
* Should only be used when we only care about waiting for all the promises to
|
|
* settle, not what value they resolve or reject to.
|
|
* @template T,U
|
|
* @param {ArrayLike<T|PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
primordials.SafePromiseAllSettledReturnVoid = async (promises, mapFn) => {
|
|
for (let i = 0; i < promises.length; i++) {
|
|
try {
|
|
await (mapFn != null ? mapFn(promises[i], i) : promises[i]);
|
|
} catch {
|
|
// In all settled, we can ignore errors.
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @template T,U
|
|
* @param {Array<T|PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<Awaited<U>>}
|
|
*/
|
|
primordials.SafePromiseAny = (promises, mapFn) =>
|
|
// Wrapping on a new Promise is necessary to not expose the SafePromise
|
|
// prototype to user-land.
|
|
new Promise((a, b) =>
|
|
SafePromise.any(arrayToSafePromiseIterable(promises, mapFn)).then(a, b),
|
|
);
|
|
|
|
/**
|
|
* @template T,U
|
|
* @param {Array<T|PromiseLike<T>>} promises
|
|
* @param {(v: T|PromiseLike<T>, k: number) => U|PromiseLike<U>} [mapFn]
|
|
* @returns {Promise<Awaited<U>>}
|
|
*/
|
|
primordials.SafePromiseRace = (promises, mapFn) =>
|
|
// Wrapping on a new Promise is necessary to not expose the SafePromise
|
|
// prototype to user-land.
|
|
new Promise((a, b) =>
|
|
SafePromise.race(arrayToSafePromiseIterable(promises, mapFn)).then(a, b),
|
|
);
|
|
|
|
|
|
const {
|
|
exec: OriginalRegExpPrototypeExec,
|
|
[SymbolMatch]: OriginalRegExpPrototypeSymbolMatch,
|
|
[SymbolMatchAll]: OriginalRegExpPrototypeSymbolMatchAll,
|
|
[SymbolReplace]: OriginalRegExpPrototypeSymbolReplace,
|
|
[SymbolSearch]: OriginalRegExpPrototypeSymbolSearch,
|
|
[SymbolSplit]: OriginalRegExpPrototypeSymbolSplit,
|
|
} = RegExpPrototype;
|
|
|
|
class RegExpLikeForStringSplitting {
|
|
#regex;
|
|
constructor() {
|
|
this.#regex = ReflectConstruct(RegExp, arguments);
|
|
}
|
|
|
|
get lastIndex() {
|
|
return ReflectGet(this.#regex, 'lastIndex');
|
|
}
|
|
set lastIndex(value) {
|
|
ReflectSet(this.#regex, 'lastIndex', value);
|
|
}
|
|
|
|
exec() {
|
|
return ReflectApply(OriginalRegExpPrototypeExec, this.#regex, arguments);
|
|
}
|
|
}
|
|
ObjectSetPrototypeOf(RegExpLikeForStringSplitting.prototype, null);
|
|
|
|
/**
|
|
* @param {RegExp} pattern
|
|
* @returns {RegExp}
|
|
*/
|
|
primordials.hardenRegExp = function hardenRegExp(pattern) {
|
|
ObjectDefineProperties(pattern, {
|
|
[SymbolMatch]: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: OriginalRegExpPrototypeSymbolMatch,
|
|
},
|
|
[SymbolMatchAll]: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: OriginalRegExpPrototypeSymbolMatchAll,
|
|
},
|
|
[SymbolReplace]: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: OriginalRegExpPrototypeSymbolReplace,
|
|
},
|
|
[SymbolSearch]: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: OriginalRegExpPrototypeSymbolSearch,
|
|
},
|
|
[SymbolSplit]: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: OriginalRegExpPrototypeSymbolSplit,
|
|
},
|
|
constructor: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: {
|
|
[SymbolSpecies]: RegExpLikeForStringSplitting,
|
|
},
|
|
},
|
|
dotAll: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetDotAll(pattern),
|
|
},
|
|
exec: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: OriginalRegExpPrototypeExec,
|
|
},
|
|
global: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetGlobal(pattern),
|
|
},
|
|
hasIndices: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetHasIndices(pattern),
|
|
},
|
|
ignoreCase: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetIgnoreCase(pattern),
|
|
},
|
|
multiline: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetMultiline(pattern),
|
|
},
|
|
source: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetSource(pattern),
|
|
},
|
|
sticky: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetSticky(pattern),
|
|
},
|
|
unicode: {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetUnicode(pattern),
|
|
},
|
|
});
|
|
ObjectDefineProperty(pattern, 'flags', {
|
|
__proto__: null,
|
|
configurable: true,
|
|
value: RegExpPrototypeGetFlags(pattern),
|
|
});
|
|
return pattern;
|
|
};
|
|
|
|
|
|
/**
|
|
* @param {string} str
|
|
* @param {RegExp} regexp
|
|
* @returns {number}
|
|
*/
|
|
primordials.SafeStringPrototypeSearch = (str, regexp) => {
|
|
regexp.lastIndex = 0;
|
|
const match = RegExpPrototypeExec(regexp, str);
|
|
return match ? match.index : -1;
|
|
};
|
|
|
|
ObjectSetPrototypeOf(primordials, null);
|
|
ObjectFreeze(primordials);
|