node/lib/internal/per_context/primordials.js
Leko cef144421c tools: add new ESLint rule: prefer-primordials
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>
2020-11-07 18:28:14 +08:00

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);