-
Notifications
You must be signed in to change notification settings - Fork 29.6k
Commit
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: #23509 Refs: whatwg/console#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>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
} | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
joyeecheung
Author
Member
|
||
} | ||
|
||
|
@@ -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; |
☝️ This breaks BufferConsole in
jest-util
because it extendsconsole.Console
but provides its ownlog
and other methods. Since the line above changesthis[k].bind(this)
toConsole.prototype[k].bind(this)
theBufferConsole.prototype.log
is not used.