From 112cc7c27551254aa2b17098fb774867f05ed0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Fri, 5 Apr 2019 11:11:26 +0200 Subject: [PATCH] lib: use safe methods from primordials This changes the primordials to expose built-in prototypes with their methods already uncurried. The uncurryThis function is therefore moved to the primordials. All uses of uncurryThis on built-ins are changed to import the relevant prototypes from primordials. All uses of Function.call.bind are also changed to use primordials. PR-URL: https://github.com/nodejs/node/pull/27096 Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell Reviewed-By: Joyee Cheung --- lib/_http_outgoing.js | 6 +- lib/internal/async_hooks.js | 6 +- lib/internal/bootstrap/loaders.js | 7 +- lib/internal/bootstrap/primordials.js | 33 ++++++++- lib/internal/cli_table.js | 6 +- lib/internal/console/constructor.js | 6 +- lib/internal/error-serdes.js | 37 ++++------ .../modules/esm/create_dynamic_module.js | 8 +-- lib/internal/modules/esm/loader.js | 12 ++-- lib/internal/modules/esm/translators.js | 16 +++-- lib/internal/policy/manifest.js | 25 +++---- lib/internal/policy/sri.js | 17 ++--- lib/internal/process/per_thread.js | 24 ++++--- lib/internal/process/task_queues.js | 6 +- lib/internal/util.js | 12 ---- lib/internal/util/comparisons.js | 72 ++++++++++--------- lib/internal/util/inspect.js | 71 +++++++++--------- lib/internal/util/types.js | 2 +- lib/util.js | 15 ++-- lib/vm.js | 14 ++-- 20 files changed, 206 insertions(+), 189 deletions(-) diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 34b064a6a311b9..2c2346d21125de 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -21,6 +21,8 @@ 'use strict'; +const { ObjectPrototype } = primordials; + const assert = require('internal/assert'); const Stream = require('stream'); const internalUtil = require('internal/util'); @@ -53,8 +55,6 @@ const { CRLF, debug } = common; const kIsCorked = Symbol('isCorked'); -const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); - const RE_CONN_CLOSE = /(?:^|\W)close(?:$|\W)/i; const RE_TE_CHUNKED = common.chunkExpression; @@ -310,7 +310,7 @@ function _storeHeader(firstLine, headers) { } } else { for (const key in headers) { - if (hasOwnProperty(headers, key)) { + if (ObjectPrototype.hasOwnProperty(headers, key)) { processHeader(this, state, key, headers[key], true); } } diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index a1f3c07defe879..9d30ad133dca6d 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -1,6 +1,6 @@ 'use strict'; -const { Reflect } = primordials; +const { FunctionPrototype, Reflect } = primordials; const { ERR_ASYNC_TYPE, @@ -77,8 +77,6 @@ const { kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve, kCheck, kExecutionAsyncId, kAsyncIdCounter, kTriggerAsyncId, kDefaultTriggerAsyncId, kStackLength } = async_wrap.constants; -const FunctionBind = Function.call.bind(Function.prototype.bind); - // Used in AsyncHook and AsyncResource. const async_id_symbol = Symbol('asyncId'); const trigger_async_id_symbol = Symbol('triggerAsyncId'); @@ -181,7 +179,7 @@ function emitHook(symbol, asyncId) { } function emitHookFactory(symbol, name) { - const fn = FunctionBind(emitHook, undefined, symbol); + const fn = FunctionPrototype.bind(emitHook, undefined, symbol); // Set the name property of the function as it looks good in the stack trace. Object.defineProperty(fn, 'name', { diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index ffcc931334f861..e4d1a09587001e 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -225,7 +225,7 @@ NativeModule.prototype.compileForPublicLoader = function(needToProxify) { }; const getOwn = (target, property, receiver) => { - return Reflect.apply(ObjectPrototype.hasOwnProperty, target, [property]) ? + return ObjectPrototype.hasOwnProperty(target, property) ? Reflect.get(target, property, receiver) : undefined; }; @@ -239,8 +239,7 @@ NativeModule.prototype.proxifyExports = function() { const update = (property, value) => { if (this.reflect !== undefined && - Reflect.apply(ObjectPrototype.hasOwnProperty, - this.reflect.exports, [property])) + ObjectPrototype.hasOwnProperty(this.reflect.exports, property)) this.reflect.exports[property].set(value); }; @@ -254,7 +253,7 @@ NativeModule.prototype.proxifyExports = function() { !Reflect.has(handler, 'get')) { handler.get = (target, prop, receiver) => { const value = Reflect.get(target, prop, receiver); - if (Reflect.apply(ObjectPrototype.hasOwnProperty, target, [prop])) + if (ObjectPrototype.hasOwnProperty(target, prop)) update(prop, value); return value; }; diff --git a/lib/internal/bootstrap/primordials.js b/lib/internal/bootstrap/primordials.js index e1fef7f8860b2d..8d2150b7b7690a 100644 --- a/lib/internal/bootstrap/primordials.js +++ b/lib/internal/bootstrap/primordials.js @@ -12,6 +12,19 @@ // `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)) { @@ -23,6 +36,18 @@ function copyProps(src, dest) { } } +function copyPrototype(src, dest) { + for (const key of Reflect.ownKeys(src)) { + if (!Reflect.getOwnPropertyDescriptor(dest, key)) { + const desc = Reflect.getOwnPropertyDescriptor(src, key); + if (typeof desc.value === 'function') { + desc.value = uncurryThis(desc.value); + } + Reflect.defineProperty(dest, key, desc); + } + } +} + function makeSafe(unsafe, safe) { copyProps(unsafe.prototype, safe.prototype); copyProps(unsafe, safe); @@ -64,17 +89,23 @@ primordials.SafePromise = makeSafe( // Create copies of intrinsic objects [ 'Array', + 'BigInt', + 'Boolean', 'Date', + 'Error', 'Function', + 'Map', + 'Number', 'Object', 'RegExp', + 'Set', 'String', 'Symbol', ].forEach((name) => { const target = primordials[name] = Object.create(null); copyProps(global[name], target); const proto = primordials[name + 'Prototype'] = Object.create(null); - copyProps(global[name].prototype, proto); + copyPrototype(global[name].prototype, proto); }); Object.setPrototypeOf(primordials, null); diff --git a/lib/internal/cli_table.js b/lib/internal/cli_table.js index b2d2779e5f9cdc..6e9a6bdbc9021a 100644 --- a/lib/internal/cli_table.js +++ b/lib/internal/cli_table.js @@ -1,10 +1,9 @@ 'use strict'; -const { Math } = primordials; +const { Math, ObjectPrototype } = primordials; const { Buffer } = require('buffer'); const { removeColors } = require('internal/util'); -const HasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); // The use of Unicode characters below is the only non-comment use of non-ASCII // Unicode characters in Node.js built-in modules. If they are ever removed or @@ -61,7 +60,8 @@ const table = (head, columns) => { for (var j = 0; j < longestColumn; j++) { if (rows[j] === undefined) rows[j] = []; - const value = rows[j][i] = HasOwnProperty(column, j) ? column[j] : ''; + const value = rows[j][i] = + ObjectPrototype.hasOwnProperty(column, j) ? column[j] : ''; const width = columnWidths[i] || 0; const counted = countSymbols(value); columnWidths[i] = Math.max(width, counted); diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 9e75dce3af1bb8..46fd1e9c625082 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -3,7 +3,7 @@ // The Console constructor is not actually used to construct the global // console. It's exported for backwards compatibility. -const { Reflect } = primordials; +const { ObjectPrototype, Reflect } = primordials; const { trace } = internalBinding('trace_events'); const { @@ -36,7 +36,6 @@ const { keys: ObjectKeys, values: ObjectValues, } = Object; -const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); const { isArray: ArrayIsArray, @@ -493,7 +492,8 @@ const consoleMethods = { for (const key of keys) { if (map[key] === undefined) map[key] = []; - if ((primitive && properties) || !hasOwnProperty(item, key)) + if ((primitive && properties) || + !ObjectPrototype.hasOwnProperty(item, key)) map[key][i] = ''; else map[key][i] = _inspect(item[key]); diff --git a/lib/internal/error-serdes.js b/lib/internal/error-serdes.js index b907842c3896ff..8647e3b78d19ec 100644 --- a/lib/internal/error-serdes.js +++ b/lib/internal/error-serdes.js @@ -2,27 +2,17 @@ const Buffer = require('buffer').Buffer; const { - SafeSet, + ArrayPrototype, + FunctionPrototype, Object, ObjectPrototype, - FunctionPrototype, - ArrayPrototype + SafeSet, } = primordials; const kSerializedError = 0; const kSerializedObject = 1; const kInspectedError = 2; -const GetPrototypeOf = Object.getPrototypeOf; -const GetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; -const GetOwnPropertyNames = Object.getOwnPropertyNames; -const DefineProperty = Object.defineProperty; -const Assign = Object.assign; -const ObjectPrototypeToString = - FunctionPrototype.call.bind(ObjectPrototype.toString); -const ForEach = FunctionPrototype.call.bind(ArrayPrototype.forEach); -const Call = FunctionPrototype.call.bind(FunctionPrototype.call); - const errors = { Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError }; @@ -32,17 +22,18 @@ function TryGetAllProperties(object, target = object) { const all = Object.create(null); if (object === null) return all; - Assign(all, TryGetAllProperties(GetPrototypeOf(object), target)); - const keys = GetOwnPropertyNames(object); - ForEach(keys, (key) => { + Object.assign(all, + TryGetAllProperties(Object.getPrototypeOf(object), target)); + const keys = Object.getOwnPropertyNames(object); + ArrayPrototype.forEach(keys, (key) => { let descriptor; try { - descriptor = GetOwnPropertyDescriptor(object, key); + descriptor = Object.getOwnPropertyDescriptor(object, key); } catch { return; } const getter = descriptor.get; if (getter && key !== '__proto__') { try { - descriptor.value = Call(getter, target); + descriptor.value = FunctionPrototype.call(getter, target); } catch {} } if ('value' in descriptor && typeof descriptor.value !== 'function') { @@ -59,10 +50,10 @@ function GetConstructors(object) { for (var current = object; current !== null; - current = GetPrototypeOf(current)) { - const desc = GetOwnPropertyDescriptor(current, 'constructor'); + current = Object.getPrototypeOf(current)) { + const desc = Object.getOwnPropertyDescriptor(current, 'constructor'); if (desc && desc.value) { - DefineProperty(constructors, constructors.length, { + Object.defineProperty(constructors, constructors.length, { value: desc.value, enumerable: true }); } @@ -72,7 +63,7 @@ function GetConstructors(object) { } function GetName(object) { - const desc = GetOwnPropertyDescriptor(object, 'name'); + const desc = Object.getOwnPropertyDescriptor(object, 'name'); return desc && desc.value; } @@ -89,7 +80,7 @@ function serializeError(error) { if (!serialize) serialize = require('v8').serialize; try { if (typeof error === 'object' && - ObjectPrototypeToString(error) === '[object Error]') { + ObjectPrototype.toString(error) === '[object Error]') { const constructors = GetConstructors(error); for (var i = 0; i < constructors.length; i++) { const name = GetName(constructors[i]); diff --git a/lib/internal/modules/esm/create_dynamic_module.js b/lib/internal/modules/esm/create_dynamic_module.js index d4356b4b673ae7..d3e74263c40708 100644 --- a/lib/internal/modules/esm/create_dynamic_module.js +++ b/lib/internal/modules/esm/create_dynamic_module.js @@ -1,16 +1,16 @@ 'use strict'; +const { ArrayPrototype } = primordials; + const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); const debug = require('util').debuglog('esm'); -const ArrayJoin = Function.call.bind(Array.prototype.join); -const ArrayMap = Function.call.bind(Array.prototype.map); const createDynamicModule = (exports, url = '', evaluate) => { debug('creating ESM facade for %s with exports: %j', url, exports); - const names = ArrayMap(exports, (name) => `${name}`); + const names = ArrayPrototype.map(exports, (name) => `${name}`); const source = ` -${ArrayJoin(ArrayMap(names, (name) => +${ArrayPrototype.join(ArrayPrototype.map(names, (name) => `let $${name}; export { $${name} as ${name} }; import.meta.exports.${name} = { diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 88f03119fcfc80..f752550d12c205 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -1,5 +1,7 @@ 'use strict'; +const { FunctionPrototype } = primordials; + const { ERR_INVALID_RETURN_PROPERTY, ERR_INVALID_RETURN_PROPERTY_VALUE, @@ -18,8 +20,6 @@ const createDynamicModule = require( const { translators } = require('internal/modules/esm/translators'); const { ModuleWrap } = internalBinding('module_wrap'); -const FunctionBind = Function.call.bind(Function.prototype.bind); - const debug = require('internal/util/debuglog').debuglog('esm'); const { @@ -132,9 +132,11 @@ class Loader { hook({ resolve, dynamicInstantiate }) { // Use .bind() to avoid giving access to the Loader instance when called. if (resolve !== undefined) - this._resolve = FunctionBind(resolve, null); - if (dynamicInstantiate !== undefined) - this._dynamicInstantiate = FunctionBind(dynamicInstantiate, null); + this._resolve = FunctionPrototype.bind(resolve, null); + if (dynamicInstantiate !== undefined) { + this._dynamicInstantiate = + FunctionPrototype.bind(dynamicInstantiate, null); + } } async getModuleJob(specifier, parentURL) { diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 4b2db024c5848f..8494f5e307bfa9 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -1,5 +1,11 @@ 'use strict'; +const { + SafeMap, + StringPrototype, + JSON +} = primordials; + const { NativeModule } = require('internal/bootstrap/loaders'); const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); const { @@ -11,10 +17,6 @@ const internalURLModule = require('internal/url'); const createDynamicModule = require( 'internal/modules/esm/create_dynamic_module'); const fs = require('fs'); -const { - SafeMap, - JSON -} = primordials; const { fileURLToPath, URL } = require('url'); const { debuglog } = require('internal/util/debuglog'); const { promisify } = require('internal/util'); @@ -23,7 +25,6 @@ const { ERR_UNKNOWN_BUILTIN_MODULE } = require('internal/errors').codes; const readFileAsync = promisify(fs.readFile); -const StringReplace = Function.call.bind(String.prototype.replace); const JsonParse = JSON.parse; const debug = debuglog('esm'); @@ -67,7 +68,8 @@ translators.set('commonjs', async function commonjsStrategy(url, isMain) { return cached; } const module = CJSModule._cache[ - isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname]; + isWindows ? StringPrototype.replace(pathname, winSepRegEx, '\\') : pathname + ]; if (module && module.loaded) { const exports = module.exports; return createDynamicModule(['default'], url, (reflect) => { @@ -110,7 +112,7 @@ translators.set('json', async function jsonStrategy(url) { debug(`Loading JSONModule ${url}`); const pathname = fileURLToPath(url); const modulePath = isWindows ? - StringReplace(pathname, winSepRegEx, '\\') : pathname; + StringPrototype.replace(pathname, winSepRegEx, '\\') : pathname; let module = CJSModule._cache[modulePath]; if (module && module.loaded) { const exports = module.exports; diff --git a/lib/internal/policy/manifest.js b/lib/internal/policy/manifest.js index 1715bcba561fd3..0714b2294b637f 100644 --- a/lib/internal/policy/manifest.js +++ b/lib/internal/policy/manifest.js @@ -1,4 +1,12 @@ 'use strict'; + +const { + SafeWeakMap, + Object, + RegExpPrototype, + uncurryThis +} = primordials; + const { ERR_MANIFEST_ASSERT_INTEGRITY, ERR_MANIFEST_INTEGRITY_MISMATCH, @@ -6,21 +14,14 @@ const { } = require('internal/errors').codes; const debug = require('internal/util/debuglog').debuglog('policy'); const SRI = require('internal/policy/sri'); -const { - SafeWeakMap, - FunctionPrototype, - Object, - RegExpPrototype -} = primordials; const crypto = require('crypto'); const { Buffer } = require('buffer'); const { URL } = require('url'); const { createHash, timingSafeEqual } = crypto; -const HashUpdate = FunctionPrototype.call.bind(crypto.Hash.prototype.update); -const HashDigest = FunctionPrototype.call.bind(crypto.Hash.prototype.digest); -const BufferEquals = FunctionPrototype.call.bind(Buffer.prototype.equals); -const BufferToString = FunctionPrototype.call.bind(Buffer.prototype.toString); -const RegExpTest = FunctionPrototype.call.bind(RegExpPrototype.test); +const HashUpdate = uncurryThis(crypto.Hash.prototype.update); +const HashDigest = uncurryThis(crypto.Hash.prototype.digest); +const BufferEquals = uncurryThis(Buffer.prototype.equals); +const BufferToString = uncurryThis(Buffer.prototype.toString); const { entries } = Object; const kIntegrities = new SafeWeakMap(); const kReactions = new SafeWeakMap(); @@ -76,7 +77,7 @@ class Manifest { const integrity = manifestEntries[i][1].integrity; if (integrity != null) { debug(`Manifest contains integrity for url ${url}`); - if (RegExpTest(kRelativeURLStringPattern, url)) { + if (RegExpPrototype.test(kRelativeURLStringPattern, url)) { url = new URL(url, manifestURL).href; } diff --git a/lib/internal/policy/sri.js b/lib/internal/policy/sri.js index 1cdec8d739eb99..55c3c0c507d7db 100644 --- a/lib/internal/policy/sri.js +++ b/lib/internal/policy/sri.js @@ -1,6 +1,12 @@ 'use strict'; // Value of https://w3c.github.io/webappsec-subresource-integrity/#the-integrity-attribute +const { + Object, + RegExpPrototype, + StringPrototype +} = primordials; + // Returns [{algorithm, value (in base64 string), options,}] const { ERR_SRI_PARSE @@ -19,19 +25,14 @@ Object.seal(kSRIPattern); const kAllWSP = new RegExp(`^${kWSP}*$`); Object.seal(kAllWSP); -const RegExpExec = Function.call.bind(RegExp.prototype.exec); -const RegExpTest = Function.call.bind(RegExp.prototype.test); -const StringSlice = Function.call.bind(String.prototype.slice); - const BufferFrom = require('buffer').Buffer.from; -const { defineProperty } = Object; const parse = (str) => { kSRIPattern.lastIndex = 0; let prevIndex = 0; let match; const entries = []; - while (match = RegExpExec(kSRIPattern, str)) { + while (match = RegExpPrototype.exec(kSRIPattern, str)) { if (match.index !== prevIndex) { throw new ERR_SRI_PARSE(str, prevIndex); } @@ -40,7 +41,7 @@ const parse = (str) => { } // Avoid setters being fired - defineProperty(entries, entries.length, { + Object.defineProperty(entries, entries.length, { enumerable: true, configurable: true, value: freeze({ @@ -54,7 +55,7 @@ const parse = (str) => { } if (prevIndex !== str.length) { - if (!RegExpTest(kAllWSP, StringSlice(str, prevIndex))) { + if (!RegExpPrototype.test(kAllWSP, StringPrototype.slice(str, prevIndex))) { throw new ERR_SRI_PARSE(str, prevIndex); } } diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index aa2bcce16c3871..ef9012535f7e7e 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -4,6 +4,12 @@ // run when setting up each thread, including the main // thread and the worker threads. +const { + RegExpPrototype, + SetPrototype, + StringPrototype +} = primordials; + const { errnoException, codes: { @@ -199,11 +205,6 @@ const replaceUnderscoresRegex = /_/g; const leadingDashesRegex = /^--?/; const trailingValuesRegex = /=.*$/; -// Save references so user code does not interfere -const replace = Function.call.bind(String.prototype.replace); -const has = Function.call.bind(Set.prototype.has); -const test = Function.call.bind(RegExp.prototype.test); - // This builds the initial process.allowedNodeEnvironmentFlags // from data in the config binding. function buildAllowedFlags() { @@ -243,7 +244,8 @@ function buildAllowedFlags() { } } - const trimLeadingDashes = (flag) => replace(flag, leadingDashesRegex, ''); + const trimLeadingDashes = + (flag) => StringPrototype.replace(flag, leadingDashesRegex, ''); // Save these for comparison against flags provided to // process.allowedNodeEnvironmentFlags.has() which lack leading dashes. @@ -281,12 +283,12 @@ function buildAllowedFlags() { // on a dummy option set and see whether it rejects the argument or // not. if (typeof key === 'string') { - key = replace(key, replaceUnderscoresRegex, '-'); - if (test(leadingDashesRegex, key)) { - key = replace(key, trailingValuesRegex, ''); - return has(this, key); + key = StringPrototype.replace(key, replaceUnderscoresRegex, '-'); + if (RegExpPrototype.test(leadingDashesRegex, key)) { + key = StringPrototype.replace(key, trailingValuesRegex, ''); + return SetPrototype.has(this, key); } - return has(nodeFlags, key); + return SetPrototype.has(nodeFlags, key); } return false; } diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 5f5ca5a0089cc7..bc65d25307002d 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -1,6 +1,6 @@ 'use strict'; -const { Reflect } = primordials; +const { FunctionPrototype, Reflect } = primordials; const { // For easy access to the nextTick state in the C++ land, @@ -37,8 +37,6 @@ const { } = require('internal/errors').codes; const FixedQueue = require('internal/fixed_queue'); -const FunctionBind = Function.call.bind(Function.prototype.bind); - // *Must* match Environment::TickInfo::Fields in src/env.h. const kHasTickScheduled = 0; @@ -176,7 +174,7 @@ function queueMicrotask(callback) { const asyncResource = createMicrotaskResource(); asyncResource.callback = callback; - enqueueMicrotask(FunctionBind(runMicrotask, asyncResource)); + enqueueMicrotask(FunctionPrototype.bind(runMicrotask, asyncResource)); } module.exports = { diff --git a/lib/internal/util.js b/lib/internal/util.js index ba332f7e852f1a..e7dca9a23ce454 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -387,17 +387,6 @@ function once(callback) { }; } -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); -} - module.exports = { assertCrypto, cachedResult, @@ -418,7 +407,6 @@ module.exports = { promisify, spliceOne, removeColors, - uncurryThis, // Symbol used to customize promisify conversion customPromisifyArgs: kCustomPromisifyArgsSymbol, diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index 18fb7905073a1e..2b0970a9eaed8c 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -1,5 +1,21 @@ 'use strict'; +const { + BigIntPrototype, + BooleanPrototype, + DatePrototype, + Number, + NumberPrototype, + Object, + ObjectPrototype: { + hasOwnProperty, + propertyIsEnumerable, + toString: objectToString + }, + StringPrototype, + SymbolPrototype +} = primordials; + const { compare } = internalBinding('buffer'); const { isAnyArrayBuffer, @@ -24,7 +40,6 @@ const { ONLY_ENUMERABLE } } = internalBinding('util'); -const { uncurryThis } = require('internal/util'); const kStrict = true; const kLoose = false; @@ -34,23 +49,6 @@ const kIsArray = 1; const kIsSet = 2; const kIsMap = 3; -const objectToString = uncurryThis(Object.prototype.toString); -const hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty); -const propertyIsEnumerable = uncurryThis(Object.prototype.propertyIsEnumerable); -const dateGetTime = uncurryThis(Date.prototype.getTime); - -const bigIntValueOf = uncurryThis(BigInt.prototype.valueOf); -const booleanValueOf = uncurryThis(Boolean.prototype.valueOf); -const numberValueOf = uncurryThis(Number.prototype.valueOf); -const symbolValueOf = uncurryThis(Symbol.prototype.valueOf); -const stringValueOf = uncurryThis(String.prototype.valueOf); - -const objectKeys = Object.keys; -const getPrototypeOf = Object.getPrototypeOf; -const getOwnPropertySymbols = Object.getOwnPropertySymbols; -const objectIs = Object.is; -const numberIsNaN = Number.isNaN; - // Check if they have the same source and flags function areSimilarRegExps(a, b) { return a.source === b.source && a.flags === b.flags; @@ -84,19 +82,23 @@ function areEqualArrayBuffers(buf1, buf2) { function isEqualBoxedPrimitive(val1, val2) { if (isNumberObject(val1)) { return isNumberObject(val2) && - objectIs(numberValueOf(val1), numberValueOf(val2)); + Object.is(NumberPrototype.valueOf(val1), + NumberPrototype.valueOf(val2)); } if (isStringObject(val1)) { - return isStringObject(val2) && stringValueOf(val1) === stringValueOf(val2); + return isStringObject(val2) && + StringPrototype.valueOf(val1) === StringPrototype.valueOf(val2); } if (isBooleanObject(val1)) { return isBooleanObject(val2) && - booleanValueOf(val1) === booleanValueOf(val2); + BooleanPrototype.valueOf(val1) === BooleanPrototype.valueOf(val2); } if (isBigIntObject(val1)) { - return isBigIntObject(val2) && bigIntValueOf(val1) === bigIntValueOf(val2); + return isBigIntObject(val2) && + BigIntPrototype.valueOf(val1) === BigIntPrototype.valueOf(val2); } - return isSymbolObject(val2) && symbolValueOf(val1) === symbolValueOf(val2); + return isSymbolObject(val2) && + SymbolPrototype.valueOf(val1) === SymbolPrototype.valueOf(val2); } // Notes: Type tags are historical [[Class]] properties that can be set by @@ -123,19 +125,19 @@ function innerDeepEqual(val1, val2, strict, memos) { if (val1 === val2) { if (val1 !== 0) return true; - return strict ? objectIs(val1, val2) : true; + return strict ? Object.is(val1, val2) : true; } // Check more closely if val1 and val2 are equal. if (strict) { if (typeof val1 !== 'object') { - return typeof val1 === 'number' && numberIsNaN(val1) && - numberIsNaN(val2); + return typeof val1 === 'number' && Number.isNaN(val1) && + Number.isNaN(val2); } if (typeof val2 !== 'object' || val1 === null || val2 === null) { return false; } - if (getPrototypeOf(val1) !== getPrototypeOf(val2)) { + if (Object.getPrototypeOf(val1) !== Object.getPrototypeOf(val2)) { return false; } } else { @@ -172,7 +174,7 @@ function innerDeepEqual(val1, val2, strict, memos) { return keyCheck(val1, val2, strict, memos, kNoIterator); } if (isDate(val1)) { - if (dateGetTime(val1) !== dateGetTime(val2)) { + if (DatePrototype.getTime(val1) !== DatePrototype.getTime(val2)) { return false; } } else if (isRegExp(val1)) { @@ -235,8 +237,8 @@ function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { // d) For Sets and Maps, equal contents // Note: this accounts for both named and indexed properties on Arrays. if (arguments.length === 5) { - aKeys = objectKeys(val1); - const bKeys = objectKeys(val2); + aKeys = Object.keys(val1); + const bKeys = Object.keys(val2); // The pair must have the same number of owned properties. if (aKeys.length !== bKeys.length) { @@ -253,7 +255,7 @@ function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { } if (strict && arguments.length === 5) { - const symbolKeysA = getOwnPropertySymbols(val1); + const symbolKeysA = Object.getOwnPropertySymbols(val1); if (symbolKeysA.length !== 0) { let count = 0; for (i = 0; i < symbolKeysA.length; i++) { @@ -268,13 +270,13 @@ function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { return false; } } - const symbolKeysB = getOwnPropertySymbols(val2); + const symbolKeysB = Object.getOwnPropertySymbols(val2); if (symbolKeysA.length !== symbolKeysB.length && getEnumerables(val2, symbolKeysB).length !== count) { return false; } } else { - const symbolKeysB = getOwnPropertySymbols(val2); + const symbolKeysB = Object.getOwnPropertySymbols(val2); if (symbolKeysB.length !== 0 && getEnumerables(val2, symbolKeysB).length !== 0) { return false; @@ -518,7 +520,7 @@ function objEquiv(a, b, strict, keys, memos, iterationType) { return false; } else { // Array is sparse. - const keysA = objectKeys(a); + const keysA = Object.keys(a); for (; i < keysA.length; i++) { const key = keysA[i]; if (!hasOwnProperty(b, key) || @@ -526,7 +528,7 @@ function objEquiv(a, b, strict, keys, memos, iterationType) { return false; } } - if (keysA.length !== objectKeys(b).length) { + if (keysA.length !== Object.keys(b).length) { return false; } return true; diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 81311664173a9f..e5a8b2ecb6d189 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1,6 +1,25 @@ 'use strict'; -const { JSON, Math } = primordials; +const { + BigIntPrototype, + BooleanPrototype, + DatePrototype, + ErrorPrototype, + JSON, + MapPrototype, + Math, + NumberPrototype, + Object, + ObjectPrototype: { + hasOwnProperty, + propertyIsEnumerable + }, + RegExpPrototype, + SetPrototype, + StringPrototype, + SymbolPrototype, + uncurryThis +} = primordials; const { getOwnNonIndexProperties, @@ -19,8 +38,7 @@ const { customInspectSymbol, isError, join, - removeColors, - uncurryThis + removeColors } = require('internal/util'); const { @@ -68,25 +86,6 @@ const { const assert = require('internal/assert'); -// Avoid monkey-patched built-ins. -const { Object } = primordials; - -const propertyIsEnumerable = uncurryThis(Object.prototype.propertyIsEnumerable); -const regExpToString = uncurryThis(RegExp.prototype.toString); -const dateToISOString = uncurryThis(Date.prototype.toISOString); -const dateToString = uncurryThis(Date.prototype.toString); -const errorToString = uncurryThis(Error.prototype.toString); - -const bigIntValueOf = uncurryThis(BigInt.prototype.valueOf); -const booleanValueOf = uncurryThis(Boolean.prototype.valueOf); -const numberValueOf = uncurryThis(Number.prototype.valueOf); -const symbolValueOf = uncurryThis(Symbol.prototype.valueOf); -const stringValueOf = uncurryThis(String.prototype.valueOf); - -const setValues = uncurryThis(Set.prototype.values); -const mapEntries = uncurryThis(Map.prototype.entries); -const dateGetTime = uncurryThis(Date.prototype.getTime); -const hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty); let hexSlice; const inspectDefaultOptions = Object.seal({ @@ -475,10 +474,10 @@ function noPrototypeIterator(ctx, value, recurseTimes) { let newVal; if (isSet(value)) { const clazz = clazzWithNullPrototype(Set, 'Set'); - newVal = new clazz(setValues(value)); + newVal = new clazz(SetPrototype.values(value)); } else if (isMap(value)) { const clazz = clazzWithNullPrototype(Map, 'Map'); - newVal = new clazz(mapEntries(value)); + newVal = new clazz(MapPrototype.entries(value)); } else if (Array.isArray(value)) { const clazz = clazzWithNullPrototype(Array, 'Array'); newVal = new clazz(value.length); @@ -650,7 +649,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { base = `[${name}]`; } else if (isRegExp(value)) { // Make RegExps say that they are RegExps - base = regExpToString(constructor !== null ? value : new RegExp(value)); + base = RegExpPrototype.toString( + constructor !== null ? value : new RegExp(value) + ); const prefix = getPrefix(constructor, tag, 'RegExp'); if (prefix !== 'RegExp ') base = `${prefix}${base}`; @@ -658,9 +659,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { return ctx.stylize(base, 'regexp'); } else if (isDate(value)) { // Make dates with properties first say the date - base = Number.isNaN(dateGetTime(value)) ? - dateToString(value) : - dateToISOString(value); + base = Number.isNaN(DatePrototype.getTime(value)) ? + DatePrototype.toString(value) : + DatePrototype.toISOString(value); const prefix = getPrefix(constructor, tag, 'Date'); if (prefix !== 'Date ') base = `${prefix}${base}`; @@ -705,23 +706,25 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { } else if (isBoxedPrimitive(value)) { let type; if (isNumberObject(value)) { - base = `[Number: ${getBoxedValue(numberValueOf(value))}]`; + base = `[Number: ${getBoxedValue(NumberPrototype.valueOf(value))}]`; type = 'number'; } else if (isStringObject(value)) { - base = `[String: ${getBoxedValue(stringValueOf(value), ctx)}]`; + base = `[String: ${ + getBoxedValue(StringPrototype.valueOf(value), ctx) + }]`; type = 'string'; // For boxed Strings, we have to remove the 0-n indexed entries, // since they just noisy up the output and are redundant // Make boxed primitive Strings look like such keys = keys.slice(value.length); } else if (isBooleanObject(value)) { - base = `[Boolean: ${getBoxedValue(booleanValueOf(value))}]`; + base = `[Boolean: ${getBoxedValue(BooleanPrototype.valueOf(value))}]`; type = 'boolean'; } else if (isBigIntObject(value)) { - base = `[BigInt: ${getBoxedValue(bigIntValueOf(value))}]`; + base = `[BigInt: ${getBoxedValue(BigIntPrototype.valueOf(value))}]`; type = 'bigint'; } else { - base = `[Symbol: ${getBoxedValue(symbolValueOf(value))}]`; + base = `[Symbol: ${getBoxedValue(SymbolPrototype.valueOf(value))}]`; type = 'symbol'; } if (keys.length === 0) { @@ -832,7 +835,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { function formatError(err, constructor, tag, ctx) { // TODO(BridgeAR): Always show the error code if present. - let stack = err.stack || errorToString(err); + let stack = err.stack || ErrorPrototype.toString(err); // A stack trace may contain arbitrary data. Only manipulate the output // for "regular errors" (errors that "look normal") for now. diff --git a/lib/internal/util/types.js b/lib/internal/util/types.js index f41d11443f2bb5..28ef7f27a68a6e 100644 --- a/lib/internal/util/types.js +++ b/lib/internal/util/types.js @@ -1,6 +1,6 @@ 'use strict'; -const { uncurryThis } = require('internal/util'); +const { uncurryThis } = primordials; const TypedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype); diff --git a/lib/util.js b/lib/util.js index f6b3082760dbe2..4b35784dc4bd69 100644 --- a/lib/util.js +++ b/lib/util.js @@ -21,7 +21,7 @@ 'use strict'; -const { Reflect } = primordials; +const { ObjectPrototype, Reflect } = primordials; const { codes: { @@ -47,12 +47,9 @@ const types = require('internal/util/types'); const { deprecate, getSystemErrorName: internalErrorName, - promisify, - uncurryThis + promisify } = require('internal/util'); -const objectToString = uncurryThis(Object.prototype.toString); - let internalDeepEqual; function isBoolean(arg) { @@ -87,6 +84,10 @@ function isObject(arg) { return arg !== null && typeof arg === 'object'; } +function isError(e) { + return ObjectPrototype.toString(e) === '[object Error]' || e instanceof Error; +} + function isFunction(arg) { return typeof arg === 'function'; } @@ -249,9 +250,7 @@ module.exports = { isRegExp: types.isRegExp, isObject, isDate: types.isDate, - isError(e) { - return objectToString(e) === '[object Error]' || e instanceof Error; - }, + isError, isFunction, isPrimitive, log, diff --git a/lib/vm.js b/lib/vm.js index b2875d1e1134d8..d06fc835fc9272 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -21,6 +21,8 @@ 'use strict'; +const { Array, ArrayPrototype } = primordials; + const { ContextifyScript, makeContext, @@ -41,9 +43,6 @@ const { const { kVmBreakFirstLineSymbol } = require('internal/util'); const kParsingContext = Symbol('script parsing context'); -const ArrayForEach = Function.call.bind(Array.prototype.forEach); -const ArrayIsArray = Array.isArray; - class Script extends ContextifyScript { constructor(code, options = {}) { code = `${code}`; @@ -317,10 +316,11 @@ function runInThisContext(code, options) { function compileFunction(code, params, options = {}) { validateString(code, 'code'); if (params !== undefined) { - if (!ArrayIsArray(params)) { + if (!Array.isArray(params)) { throw new ERR_INVALID_ARG_TYPE('params', 'Array', params); } - ArrayForEach(params, (param, i) => validateString(param, `params[${i}]`)); + ArrayPrototype.forEach(params, + (param, i) => validateString(param, `params[${i}]`)); } const { @@ -363,14 +363,14 @@ function compileFunction(code, params, options = {}) { ); } } - if (!ArrayIsArray(contextExtensions)) { + if (!Array.isArray(contextExtensions)) { throw new ERR_INVALID_ARG_TYPE( 'options.contextExtensions', 'Array', contextExtensions ); } - ArrayForEach(contextExtensions, (extension, i) => { + ArrayPrototype.forEach(contextExtensions, (extension, i) => { if (typeof extension !== 'object') { throw new ERR_INVALID_ARG_TYPE( `options.contextExtensions[${i}]`,