diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2018-10-12 08:38:40 -0700 |
---|---|---|
committer | Daniel Bevenius <daniel.bevenius@gmail.com> | 2018-10-26 05:33:43 +0200 |
commit | 6223236151f25975e380eef8470243ff8cf3f61d (patch) | |
tree | b7a72f6cc706976b6380a9a378d169abe9a1456c /lib | |
parent | 817e2e8a762792b7997425548aaded4d7b08f601 (diff) | |
download | android-node-v8-6223236151f25975e380eef8470243ff8cf3f61d.tar.gz android-node-v8-6223236151f25975e380eef8470243ff8cf3f61d.tar.bz2 android-node-v8-6223236151f25975e380eef8470243ff8cf3f61d.zip |
lib: make the global console [[Prototype]] an empty object
From the WHATWG console spec:
> For historical web-compatibility reasons, the namespace object for
> console must have as its [[Prototype]] an empty object, created as
> if by ObjectCreate(%ObjectPrototype%), instead of %ObjectPrototype%.
Since in Node.js, the Console constructor has been exposed through
require('console'), we need to keep the Console constructor but
we cannot actually use `new Console` to construct the global console.
This patch changes the prototype chain of the global console object,
so the console.Console.prototype is not in the global console prototype
chain anymore.
```
const proto = Object.getPrototypeOf(global.console);
// Before this patch
proto.constructor === global.console.Console
// After this patch
proto.constructor === Object
```
But, we still maintain that
```
global.console instanceof global.console.Console
```
through a custom Symbol.hasInstance function of Console that tests
for a special symbol kIsConsole for backwards compatibility.
This fixes a case in the console Web Platform Test that we commented
out.
PR-URL: https://github.com/nodejs/node/pull/23509
Refs: https://github.com/whatwg/console/issues/3
Refs: https://console.spec.whatwg.org/#console-namespace
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: John-David Dalton <john.david.dalton@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/console.js | 56 |
1 files changed, 50 insertions, 6 deletions
diff --git a/lib/console.js b/lib/console.js index 96bd185585..2e56f7bbba 100644 --- a/lib/console.js +++ b/lib/console.js @@ -60,17 +60,21 @@ let cliTable; // Track amount of indentation required via `console.group()`. const kGroupIndent = Symbol('kGroupIndent'); - const kFormatForStderr = Symbol('kFormatForStderr'); const kFormatForStdout = Symbol('kFormatForStdout'); const kGetInspectOptions = Symbol('kGetInspectOptions'); const kColorMode = Symbol('kColorMode'); +const kIsConsole = Symbol('kIsConsole'); function Console(options /* or: stdout, stderr, ignoreErrors = true */) { - if (!(this instanceof Console)) { + // We have to test new.target here to see if this function is called + // with new, because we need to define a custom instanceof to accommodate + // the global console. + if (!new.target) { return new Console(...arguments); } + this[kIsConsole] = true; if (!options || typeof options.write === 'function') { options = { stdout: options, @@ -125,7 +129,7 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) { var keys = Object.keys(Console.prototype); for (var v = 0; v < keys.length; v++) { var k = keys[v]; - this[k] = this[k].bind(this); + this[k] = Console.prototype[k].bind(this); } } @@ -465,10 +469,50 @@ Console.prototype.table = function(tabularData, properties) { return final(keys, values); }; -module.exports = new Console({ +function noop() {} + +// See https://console.spec.whatwg.org/#console-namespace +// > For historical web-compatibility reasons, the namespace object +// > for console must have as its [[Prototype]] an empty object, +// > created as if by ObjectCreate(%ObjectPrototype%), +// > instead of %ObjectPrototype%. + +// Since in Node.js, the Console constructor has been exposed through +// require('console'), we need to keep the Console constructor but +// we cannot actually use `new Console` to construct the global console. +// Therefore, the console.Console.prototype is not +// in the global console prototype chain anymore. +const globalConsole = Object.create({}); +const tempConsole = new Console({ stdout: process.stdout, stderr: process.stderr }); -module.exports.Console = Console; -function noop() {} +// Since Console is not on the prototype chain of the global console, +// the symbol properties on Console.prototype have to be looked up from +// the global console itself. +for (const prop of Object.getOwnPropertySymbols(Console.prototype)) { + globalConsole[prop] = Console.prototype[prop]; +} + +// Reflect.ownKeys() is used here for retrieving Symbols +for (const prop of Reflect.ownKeys(tempConsole)) { + const desc = { ...(Reflect.getOwnPropertyDescriptor(tempConsole, prop)) }; + // Since Console would bind method calls onto the instance, + // make sure the methods are called on globalConsole instead of + // tempConsole. + if (typeof Console.prototype[prop] === 'function') { + desc.value = Console.prototype[prop].bind(globalConsole); + } + Reflect.defineProperty(globalConsole, prop, desc); +} + +globalConsole.Console = Console; + +Object.defineProperty(Console, Symbol.hasInstance, { + value(instance) { + return instance[kIsConsole]; + } +}); + +module.exports = globalConsole; |