mirror of
https://github.com/nodejs/node.git
synced 2025-05-17 22:02:33 +00:00

I added a new custom ESLint rule to fix these problems. We have a lot of replaceable codes with primordials. Accessing built-in objects is restricted by existing rule (no-restricted-globals), but accessing property in the built-in objects is not restricted right now. We manually review codes that can be replaced by primordials, but there's a lot of code that actually needs to be fixed. We have often made pull requests to replace the primordials with. Restrict accessing global built-in objects such as `Promise`. Restrict calling static methods such as `Array.from` or `Symbol.for`. Don't restrict prototype methods to prevent false-positive. PR-URL: https://github.com/nodejs/node/pull/35448 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com> Reviewed-By: Ben Coe <bencoe@gmail.com>
180 lines
4.6 KiB
JavaScript
180 lines
4.6 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.
|
|
|
|
// TODO(joyeecheung): we can restrict access to these globals in builtin
|
|
// modules through the JS linter, for example: ban access such as `Object`
|
|
// (which falls back to a lookup in the global proxy) in favor of
|
|
// `primordials.Object` where `primordials` is a lexical variable passed
|
|
// by the native module compiler.
|
|
|
|
const ReflectApply = Reflect.apply;
|
|
|
|
// This function is borrowed from the function with the same name on V8 Extras'
|
|
// `utils` object. V8 implements Reflect.apply very efficiently in conjunction
|
|
// with the spread syntax, such that no additional special case is needed for
|
|
// function calls w/o arguments.
|
|
// Refs: https://github.com/v8/v8/blob/d6ead37d265d7215cf9c5f768f279e21bd170212/src/js/prologue.js#L152-L156
|
|
function uncurryThis(func) {
|
|
return (thisArg, ...args) => ReflectApply(func, thisArg, args);
|
|
}
|
|
|
|
primordials.uncurryThis = uncurryThis;
|
|
|
|
function copyProps(src, dest) {
|
|
for (const key of Reflect.ownKeys(src)) {
|
|
if (!Reflect.getOwnPropertyDescriptor(dest, key)) {
|
|
Reflect.defineProperty(
|
|
dest,
|
|
key,
|
|
Reflect.getOwnPropertyDescriptor(src, key));
|
|
}
|
|
}
|
|
}
|
|
|
|
function copyPropsRenamed(src, dest, prefix) {
|
|
for (const key of Reflect.ownKeys(src)) {
|
|
if (typeof key === 'string') {
|
|
Reflect.defineProperty(
|
|
dest,
|
|
`${prefix}${key[0].toUpperCase()}${key.slice(1)}`,
|
|
Reflect.getOwnPropertyDescriptor(src, key));
|
|
}
|
|
}
|
|
}
|
|
|
|
function copyPropsRenamedBound(src, dest, prefix) {
|
|
for (const key of Reflect.ownKeys(src)) {
|
|
if (typeof key === 'string') {
|
|
const desc = Reflect.getOwnPropertyDescriptor(src, key);
|
|
if (typeof desc.value === 'function') {
|
|
desc.value = desc.value.bind(src);
|
|
}
|
|
Reflect.defineProperty(
|
|
dest,
|
|
`${prefix}${key[0].toUpperCase()}${key.slice(1)}`,
|
|
desc
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function copyPrototype(src, dest, prefix) {
|
|
for (const key of Reflect.ownKeys(src)) {
|
|
if (typeof key === 'string') {
|
|
const desc = Reflect.getOwnPropertyDescriptor(src, key);
|
|
if (typeof desc.value === 'function') {
|
|
desc.value = uncurryThis(desc.value);
|
|
}
|
|
Reflect.defineProperty(
|
|
dest,
|
|
`${prefix}${key[0].toUpperCase()}${key.slice(1)}`,
|
|
desc);
|
|
}
|
|
}
|
|
}
|
|
|
|
function makeSafe(unsafe, safe) {
|
|
copyProps(unsafe.prototype, safe.prototype);
|
|
copyProps(unsafe, safe);
|
|
Object.setPrototypeOf(safe.prototype, null);
|
|
Object.freeze(safe.prototype);
|
|
Object.freeze(safe);
|
|
return safe;
|
|
}
|
|
primordials.makeSafe = makeSafe;
|
|
|
|
// Subclass the constructors because we need to use their prototype
|
|
// methods later.
|
|
primordials.SafeMap = makeSafe(
|
|
Map,
|
|
class SafeMap extends Map {}
|
|
);
|
|
primordials.SafeWeakMap = makeSafe(
|
|
WeakMap,
|
|
class SafeWeakMap extends WeakMap {}
|
|
);
|
|
primordials.SafeSet = makeSafe(
|
|
Set,
|
|
class SafeSet extends Set {}
|
|
);
|
|
primordials.SafeWeakSet = makeSafe(
|
|
WeakSet,
|
|
class SafeWeakSet extends WeakSet {}
|
|
);
|
|
primordials.SafePromise = makeSafe(
|
|
Promise,
|
|
class SafePromise extends Promise {}
|
|
);
|
|
|
|
// Create copies of the namespace objects
|
|
[
|
|
'JSON',
|
|
'Math',
|
|
'Reflect'
|
|
].forEach((name) => {
|
|
copyPropsRenamed(global[name], primordials, name);
|
|
});
|
|
|
|
// Create copies of intrinsic objects
|
|
[
|
|
'Array',
|
|
'ArrayBuffer',
|
|
'BigInt',
|
|
'BigInt64Array',
|
|
'BigUint64Array',
|
|
'Boolean',
|
|
'DataView',
|
|
'Date',
|
|
'Error',
|
|
'EvalError',
|
|
'Float32Array',
|
|
'Float64Array',
|
|
'Function',
|
|
'Int16Array',
|
|
'Int32Array',
|
|
'Int8Array',
|
|
'Map',
|
|
'Number',
|
|
'Object',
|
|
'RangeError',
|
|
'ReferenceError',
|
|
'RegExp',
|
|
'Set',
|
|
'String',
|
|
'Symbol',
|
|
'SyntaxError',
|
|
'TypeError',
|
|
'URIError',
|
|
'Uint16Array',
|
|
'Uint32Array',
|
|
'Uint8Array',
|
|
'Uint8ClampedArray',
|
|
'WeakMap',
|
|
'WeakSet',
|
|
].forEach((name) => {
|
|
const original = global[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) => {
|
|
const original = global[name];
|
|
primordials[name] = original;
|
|
copyPropsRenamedBound(original, primordials, name);
|
|
copyPrototype(original.prototype, primordials, `${name}Prototype`);
|
|
});
|
|
|
|
Object.setPrototypeOf(primordials, null);
|
|
Object.freeze(primordials);
|