summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2018-10-12 08:38:40 -0700
committerDaniel Bevenius <daniel.bevenius@gmail.com>2018-10-26 05:33:43 +0200
commit6223236151f25975e380eef8470243ff8cf3f61d (patch)
treeb7a72f6cc706976b6380a9a378d169abe9a1456c /lib
parent817e2e8a762792b7997425548aaded4d7b08f601 (diff)
downloadandroid-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.js56
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;