1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-05 05:49:20 -05:00

fix: inspecting prototypes of built-ins with custom inspect implementations should not throw (#11308)

This commit is contained in:
David Sherret 2021-07-08 09:43:36 -04:00 committed by GitHub
parent 5e092b19fe
commit 5fa58c9216
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 325 additions and 123 deletions

View file

@ -1,5 +1,10 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, unitTest } from "./test_util.ts"; import {
assert,
assertEquals,
assertStringIncludes,
unitTest,
} from "./test_util.ts";
import { concat } from "../../../test_util/std/bytes/mod.ts"; import { concat } from "../../../test_util/std/bytes/mod.ts";
unitTest(function blobString(): void { unitTest(function blobString(): void {
@ -104,3 +109,12 @@ unitTest(function blobConstructorNameIsBlob(): void {
const blob = new Blob(); const blob = new Blob();
assertEquals(blob.constructor.name, "Blob"); assertEquals(blob.constructor.name, "Blob");
}); });
unitTest(function blobCustomInspectFunction(): void {
const blob = new Blob();
assertEquals(
Deno.inspect(blob),
`Blob { size: 0, type: "" }`,
);
assertStringIncludes(Deno.inspect(Blob.prototype), "Blob");
});

View file

@ -0,0 +1,10 @@
import { assertEquals, assertStringIncludes, unitTest } from "./test_util.ts";
unitTest(function customInspectFunction(): void {
const blob = new DOMException("test");
assertEquals(
Deno.inspect(blob),
`DOMException: test`,
);
assertStringIncludes(Deno.inspect(DOMException.prototype), "DOMException");
});

View file

@ -2,6 +2,7 @@
import { import {
assert, assert,
assertEquals, assertEquals,
assertStringIncludes,
assertThrows, assertThrows,
deferred, deferred,
unitTest, unitTest,
@ -81,6 +82,32 @@ unitTest(function performanceMeasure() {
}); });
}); });
unitTest(function performanceCustomInspectFunction(): void {
assertStringIncludes(Deno.inspect(performance), "Performance");
assertStringIncludes(
Deno.inspect(Performance.prototype),
"Performance",
);
});
unitTest(function performanceMarkCustomInspectFunction(): void {
const mark1 = performance.mark("mark1");
assertStringIncludes(Deno.inspect(mark1), "PerformanceMark");
assertStringIncludes(
Deno.inspect(PerformanceMark.prototype),
"PerformanceMark",
);
});
unitTest(function performanceMeasureCustomInspectFunction(): void {
const measure1 = performance.measure("measure1");
assertStringIncludes(Deno.inspect(measure1), "PerformanceMeasure");
assertStringIncludes(
Deno.inspect(PerformanceMeasure.prototype),
"PerformanceMeasure",
);
});
unitTest(function performanceIllegalConstructor() { unitTest(function performanceIllegalConstructor() {
assertThrows(() => new Performance(), TypeError, "Illegal constructor"); assertThrows(() => new Performance(), TypeError, "Illegal constructor");
assertEquals(Performance.length, 0); assertEquals(Performance.length, 0);

View file

@ -1,5 +1,5 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { assertEquals, unitTest } from "./test_util.ts"; import { assertEquals, assertStringIncludes, unitTest } from "./test_util.ts";
unitTest(async function fromInit(): Promise<void> { unitTest(async function fromInit(): Promise<void> {
const req = new Request("http://foo/", { const req = new Request("http://foo/", {
@ -66,4 +66,5 @@ unitTest(function customInspectFunction(): void {
url: "https://example.com/" url: "https://example.com/"
}`, }`,
); );
assertStringIncludes(Deno.inspect(Request.prototype), "Request");
}); });

View file

@ -1,5 +1,10 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, unitTest } from "./test_util.ts"; import {
assert,
assertEquals,
assertStringIncludes,
unitTest,
} from "./test_util.ts";
unitTest(async function responseText() { unitTest(async function responseText() {
const response = new Response("hello world"); const response = new Response("hello world");
@ -67,4 +72,5 @@ unitTest(function customInspectFunction(): void {
url: "" url: ""
}`, }`,
); );
assertStringIncludes(Deno.inspect(Response.prototype), "Response");
}); });

View file

@ -87,6 +87,10 @@
MathFloor, MathFloor,
Number, Number,
NumberPrototypeToString, NumberPrototypeToString,
Proxy,
ReflectGet,
ReflectGetOwnPropertyDescriptor,
ReflectGetPrototypeOf,
WeakMap, WeakMap,
WeakSet, WeakSet,
} = window.__bootstrap.primordials; } = window.__bootstrap.primordials;
@ -1953,6 +1957,58 @@
}); });
} }
/** Creates a proxy that represents a subset of the properties
* of the original object optionally without evaluating the properties
* in order to get the values. */
function createFilteredInspectProxy({ object, keys, evaluate }) {
return new Proxy({}, {
get(_target, key) {
if (key === SymbolToStringTag) {
return object.constructor?.name;
} else if (ArrayPrototypeIncludes(keys, key)) {
return ReflectGet(object, key);
} else {
return undefined;
}
},
getOwnPropertyDescriptor(_target, key) {
if (!ArrayPrototypeIncludes(keys, key)) {
return undefined;
} else if (evaluate) {
return getEvaluatedDescriptor(object, key);
} else {
return getDescendantPropertyDescriptor(object, key) ??
getEvaluatedDescriptor(object, key);
}
},
has(_target, key) {
return ArrayPrototypeIncludes(keys, key);
},
ownKeys() {
return keys;
},
});
function getDescendantPropertyDescriptor(object, key) {
let propertyDescriptor = ReflectGetOwnPropertyDescriptor(object, key);
if (!propertyDescriptor) {
const prototype = ReflectGetPrototypeOf(object);
if (prototype) {
propertyDescriptor = getDescendantPropertyDescriptor(prototype, key);
}
}
return propertyDescriptor;
}
function getEvaluatedDescriptor(object, key) {
return {
configurable: true,
enumerable: true,
value: object[key],
};
}
}
// A helper function that will bind our own console implementation // A helper function that will bind our own console implementation
// with default implementation of Console from V8. This will cause // with default implementation of Console from V8. This will cause
// console messages to be piped to inspector console. // console messages to be piped to inspector console.
@ -1997,5 +2053,6 @@
customInspect, customInspect,
inspect, inspect,
wrapConsole, wrapConsole,
createFilteredInspectProxy,
}; };
})(this); })(this);

16
extensions/console/internal.d.ts vendored Normal file
View file

@ -0,0 +1,16 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
declare namespace globalThis {
declare namespace __bootstrap {
declare namespace console {
declare function createFilteredInspectProxy<TObject>(params: {
object: TObject;
keys: (keyof TObject)[];
evaluate: boolean;
}): Record<string, unknown>;
}
}
}

View file

@ -12,6 +12,7 @@
((window) => { ((window) => {
const webidl = window.__bootstrap.webidl; const webidl = window.__bootstrap.webidl;
const consoleInternal = window.__bootstrap.console;
const { HTTP_TOKEN_CODE_POINT_RE, byteUpperCase } = window.__bootstrap.infra; const { HTTP_TOKEN_CODE_POINT_RE, byteUpperCase } = window.__bootstrap.infra;
const { URL } = window.__bootstrap.url; const { URL } = window.__bootstrap.url;
const { guardFromHeaders } = window.__bootstrap.headers; const { guardFromHeaders } = window.__bootstrap.headers;
@ -393,14 +394,17 @@
} }
[SymbolFor("Deno.customInspect")](inspect) { [SymbolFor("Deno.customInspect")](inspect) {
const inner = { return inspect(consoleInternal.createFilteredInspectProxy({
bodyUsed: this.bodyUsed, object: this,
headers: this.headers, evaluate: this instanceof Request,
method: this.method, keys: [
redirect: this.redirect, "bodyUsed",
url: this.url, "headers",
}; "method",
return `Request ${inspect(inner)}`; "redirect",
"url",
],
}));
} }
} }

View file

@ -13,6 +13,7 @@
((window) => { ((window) => {
const webidl = window.__bootstrap.webidl; const webidl = window.__bootstrap.webidl;
const consoleInternal = window.__bootstrap.console;
const { HTTP_TAB_OR_SPACE, regexMatcher } = window.__bootstrap.infra; const { HTTP_TAB_OR_SPACE, regexMatcher } = window.__bootstrap.infra;
const { extractBody, mixinBody } = window.__bootstrap.fetchBody; const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
const { getLocationHref } = window.__bootstrap.location; const { getLocationHref } = window.__bootstrap.location;
@ -377,17 +378,20 @@
} }
[SymbolFor("Deno.customInspect")](inspect) { [SymbolFor("Deno.customInspect")](inspect) {
const inner = { return inspect(consoleInternal.createFilteredInspectProxy({
body: this.body, object: this,
bodyUsed: this.bodyUsed, evaluate: this instanceof Response,
headers: this.headers, keys: [
ok: this.ok, "body",
redirected: this.redirected, "bodyUsed",
status: this.status, "headers",
statusText: this.statusText, "ok",
url: this.url, "redirected",
}; "status",
return `Response ${inspect(inner)}`; "statusText",
"url",
],
}));
} }
} }

View file

@ -16,6 +16,7 @@
} = window.__bootstrap.primordials; } = window.__bootstrap.primordials;
const { webidl, structuredClone } = window.__bootstrap; const { webidl, structuredClone } = window.__bootstrap;
const consoleInternal = window.__bootstrap.console;
const { opNow } = window.__bootstrap.timers; const { opNow } = window.__bootstrap.timers;
const { DOMException } = window.__bootstrap.domException; const { DOMException } = window.__bootstrap.domException;
@ -175,7 +176,16 @@
} }
[customInspect](inspect) { [customInspect](inspect) {
return `${this.constructor.name} ${inspect(this.toJSON())}`; return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof PerformanceEntry,
keys: [
"name",
"entryType",
"startTime",
"duration",
],
}));
} }
} }
webidl.configurePrototype(PerformanceEntry); webidl.configurePrototype(PerformanceEntry);
@ -235,7 +245,17 @@
} }
[customInspect](inspect) { [customInspect](inspect) {
return `${this.constructor.name} ${inspect(this.toJSON())}`; return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof PerformanceMark,
keys: [
"name",
"entryType",
"startTime",
"duration",
"detail",
],
}));
} }
} }
webidl.configurePrototype(PerformanceMark); webidl.configurePrototype(PerformanceMark);
@ -283,7 +303,17 @@
} }
[customInspect](inspect) { [customInspect](inspect) {
return `${this.constructor.name} ${inspect(this.toJSON())}`; return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof PerformanceMeasure,
keys: [
"name",
"entryType",
"startTime",
"duration",
"detail",
],
}));
} }
} }
webidl.configurePrototype(PerformanceMeasure); webidl.configurePrototype(PerformanceMeasure);
@ -516,7 +546,11 @@
} }
[customInspect](inspect) { [customInspect](inspect) {
return `${this.constructor.name} ${inspect(this.toJSON())}`; return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof Performance,
keys: [],
}));
} }
get [SymbolToStringTag]() { get [SymbolToStringTag]() {

View file

@ -17,6 +17,7 @@
ObjectSetPrototypeOf, ObjectSetPrototypeOf,
} = window.__bootstrap.primordials; } = window.__bootstrap.primordials;
const webidl = window.__bootstrap.webidl; const webidl = window.__bootstrap.webidl;
const consoleInternal = window.__bootstrap.console;
// Defined in WebIDL 4.3. // Defined in WebIDL 4.3.
// https://heycam.github.io/webidl/#idl-DOMException // https://heycam.github.io/webidl/#idl-DOMException
@ -109,8 +110,20 @@
return "DOMException"; return "DOMException";
} }
[Symbol.for("Deno.customInspect")]() { [Symbol.for("Deno.customInspect")](inspect) {
if (this instanceof DOMException) {
return `DOMException: ${this.#message}`; return `DOMException: ${this.#message}`;
} else {
return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: false,
keys: [
"message",
"name",
"code",
],
}));
}
} }
} }

View file

@ -9,6 +9,7 @@
((window) => { ((window) => {
const webidl = window.__bootstrap.webidl; const webidl = window.__bootstrap.webidl;
const { DOMException } = window.__bootstrap.domException; const { DOMException } = window.__bootstrap.domException;
const consoleInternal = window.__bootstrap.console;
const { const {
ArrayPrototypeFilter, ArrayPrototypeFilter,
ArrayPrototypeIncludes, ArrayPrototypeIncludes,
@ -28,10 +29,7 @@
ObjectCreate, ObjectCreate,
ObjectDefineProperty, ObjectDefineProperty,
ObjectGetOwnPropertyDescriptor, ObjectGetOwnPropertyDescriptor,
Proxy,
ReflectDefineProperty, ReflectDefineProperty,
ReflectGet,
ReflectGetOwnPropertyDescriptor,
Symbol, Symbol,
SymbolFor, SymbolFor,
SymbolToStringTag, SymbolToStringTag,
@ -175,7 +173,11 @@
} }
[SymbolFor("Deno.privateCustomInspect")](inspect) { [SymbolFor("Deno.privateCustomInspect")](inspect) {
return inspect(buildFilteredPropertyInspectObject(this, EVENT_PROPS)); return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof Event,
keys: EVENT_PROPS,
}));
} }
get type() { get type() {
@ -397,43 +399,6 @@
} }
} }
function buildFilteredPropertyInspectObject(object, keys) {
// forward the subset of properties from `object` without evaluating
// as evaluation could lead to an error, which is better handled
// in the inspect code
return new Proxy({}, {
get(_target, key) {
if (key === SymbolToStringTag) {
return object.constructor?.name;
} else if (ArrayPrototypeIncludes(keys, key)) {
return ReflectGet(object, key);
} else {
return undefined;
}
},
getOwnPropertyDescriptor(_target, key) {
if (!ArrayPrototypeIncludes(keys, key)) {
return undefined;
}
return ReflectGetOwnPropertyDescriptor(object, key) ??
(object.prototype &&
ReflectGetOwnPropertyDescriptor(object.prototype, key)) ??
{
configurable: true,
enumerable: true,
value: object[key],
};
},
has(_target, key) {
return ArrayPrototypeIncludes(keys, key);
},
ownKeys() {
return keys;
},
});
}
function defineEnumerableProps( function defineEnumerableProps(
Ctor, Ctor,
props, props,
@ -1097,14 +1062,18 @@
} }
[SymbolFor("Deno.privateCustomInspect")](inspect) { [SymbolFor("Deno.privateCustomInspect")](inspect) {
return inspect(buildFilteredPropertyInspectObject(this, [ return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof ErrorEvent,
keys: [
...EVENT_PROPS, ...EVENT_PROPS,
"message", "message",
"filename", "filename",
"lineno", "lineno",
"colno", "colno",
"error", "error",
])); ],
}));
} }
} }
@ -1151,12 +1120,16 @@
} }
[SymbolFor("Deno.privateCustomInspect")](inspect) { [SymbolFor("Deno.privateCustomInspect")](inspect) {
return inspect(buildFilteredPropertyInspectObject(this, [ return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof CloseEvent,
keys: [
...EVENT_PROPS, ...EVENT_PROPS,
"wasClean", "wasClean",
"code", "code",
"reason", "reason",
])); ],
}));
} }
} }
@ -1179,12 +1152,16 @@
} }
[SymbolFor("Deno.privateCustomInspect")](inspect) { [SymbolFor("Deno.privateCustomInspect")](inspect) {
return inspect(buildFilteredPropertyInspectObject(this, [ return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof MessageEvent,
keys: [
...EVENT_PROPS, ...EVENT_PROPS,
"data", "data",
"origin", "origin",
"lastEventId", "lastEventId",
])); ],
}));
} }
} }
@ -1209,10 +1186,14 @@
} }
[SymbolFor("Deno.privateCustomInspect")](inspect) { [SymbolFor("Deno.privateCustomInspect")](inspect) {
return inspect(buildFilteredPropertyInspectObject(this, [ return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof CustomEvent,
keys: [
...EVENT_PROPS, ...EVENT_PROPS,
"detail", "detail",
])); ],
}));
} }
} }
@ -1232,12 +1213,16 @@
} }
[SymbolFor("Deno.privateCustomInspect")](inspect) { [SymbolFor("Deno.privateCustomInspect")](inspect) {
return inspect(buildFilteredPropertyInspectObject(this, [ return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof ProgressEvent,
keys: [
...EVENT_PROPS, ...EVENT_PROPS,
"lengthComputable", "lengthComputable",
"loaded", "loaded",
"total", "total",
])); ],
}));
} }
} }

View file

@ -36,6 +36,7 @@
WeakMapPrototypeHas, WeakMapPrototypeHas,
WeakMapPrototypeSet, WeakMapPrototypeSet,
} = globalThis.__bootstrap.primordials; } = globalThis.__bootstrap.primordials;
const consoleInternal = window.__bootstrap.console;
const { DOMException } = window.__bootstrap.domException; const { DOMException } = window.__bootstrap.domException;
class AssertionError extends Error { class AssertionError extends Error {
@ -3018,9 +3019,14 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${ return inspect(consoleInternal.createFilteredInspectProxy({
inspect({ highWaterMark: this.highWaterMark, size: this.size }) object: this,
}`; evaluate: this instanceof ByteLengthQueuingStrategy,
keys: [
"highWaterMark",
"size",
],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
@ -3069,9 +3075,14 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${ return inspect(consoleInternal.createFilteredInspectProxy({
inspect({ highWaterMark: this.highWaterMark, size: this.size }) object: this,
}`; evaluate: this instanceof CountQueuingStrategy,
keys: [
"highWaterMark",
"size",
],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
@ -3561,9 +3572,11 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${ return inspect(consoleInternal.createFilteredInspectProxy({
inspect({ desiredSize: this.desiredSize }) object: this,
}`; evaluate: this instanceof ReadableByteStreamController,
keys: ["desiredSize"],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
@ -3684,9 +3697,11 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${ return inspect(consoleInternal.createFilteredInspectProxy({
inspect({ desiredSize: this.desiredSize }) object: this,
}`; evaluate: this instanceof ReadableStreamDefaultController,
keys: ["desiredSize"],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
@ -3905,9 +3920,11 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${ return inspect(consoleInternal.createFilteredInspectProxy({
inspect({ desiredSize: this.desiredSize }) object: this,
}`; evaluate: this instanceof TransformStreamDefaultController,
keys: ["desiredSize"],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
@ -4182,13 +4199,15 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${ return inspect(consoleInternal.createFilteredInspectProxy({
inspect({ object: this,
closed: this.closed, evaluate: this instanceof WritableStreamDefaultWriter,
desiredSize: this.desiredSize, keys: [
ready: this.ready, "closed",
}) "desiredSize",
}`; "ready",
],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
@ -4240,7 +4259,11 @@
} }
[Symbol.for("Deno.customInspect")](inspect) { [Symbol.for("Deno.customInspect")](inspect) {
return `${this.constructor.name} ${inspect({})}`; return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof WritableStreamDefaultController,
keys: [],
}));
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {

View file

@ -34,6 +34,7 @@
TypeError, TypeError,
Uint8Array, Uint8Array,
} = window.__bootstrap.primordials; } = window.__bootstrap.primordials;
const consoleInternal = window.__bootstrap.console;
// TODO(lucacasonato): this needs to not be hardcoded and instead depend on // TODO(lucacasonato): this needs to not be hardcoded and instead depend on
// host os. // host os.
@ -362,7 +363,14 @@
} }
[SymbolFor("Deno.customInspect")](inspect) { [SymbolFor("Deno.customInspect")](inspect) {
return `Blob ${inspect({ size: this.size, type: this.#type })}`; return inspect(consoleInternal.createFilteredInspectProxy({
object: this,
evaluate: this instanceof Blob,
keys: [
"size",
"type",
],
}));
} }
} }