From a2067ec46d410b691229b6fcaa12ff5f1dd223f3 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 24 Jun 2021 09:43:41 -0400 Subject: [PATCH] fix(inspect): eliminate panic inspecting event classes (#10979) --- cli/tests/unit/event_test.ts | 34 +++++++++++++++++++- extensions/web/02_event.js | 60 +++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/cli/tests/unit/event_test.ts b/cli/tests/unit/event_test.ts index 9eb90fa36c..9e51559646 100644 --- a/cli/tests/unit/event_test.ts +++ b/cli/tests/unit/event_test.ts @@ -1,5 +1,10 @@ // 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(function eventInitializedWithType(): void { const type = "click"; @@ -127,3 +132,30 @@ unitTest(function eventInspectOutput(): void { assertEquals(Deno.inspect(event), outputProvider(event)); } }); + +unitTest(function inspectEvent(): void { + // has a customInspect implementation that previously would throw on a getter + assertEquals( + Deno.inspect(Event.prototype), + `Event { + bubbles: [Getter/Setter], + cancelable: [Getter/Setter], + composed: [Getter/Setter], + currentTarget: [Getter/Setter], + defaultPrevented: [Getter/Setter], + eventPhase: [Getter/Setter], + srcElement: [Getter/Setter], + target: [Getter/Setter], + returnValue: [Getter/Setter], + timeStamp: [Getter/Setter], + type: [Getter/Setter] +}`, + ); + + // ensure this still works + assertStringIncludes( + Deno.inspect(new Event("test")), + // check a substring because one property is a timestamp + `Event {\n bubbles: false,\n cancelable: false,`, + ); +}); diff --git a/extensions/web/02_event.js b/extensions/web/02_event.js index 17d5cb54e9..40cfd30a85 100644 --- a/extensions/web/02_event.js +++ b/extensions/web/02_event.js @@ -144,7 +144,7 @@ } [Symbol.for("Deno.customInspect")](inspect) { - return buildCustomInspectOutput(this, EVENT_PROPS, inspect); + return inspect(buildFilteredPropertyInspectObject(this, EVENT_PROPS)); } get type() { @@ -395,9 +395,41 @@ } } - function buildCustomInspectOutput(object, keys, inspect) { - const inspectObject = Object.fromEntries(keys.map((k) => [k, object[k]])); - return `${object.constructor.name} ${inspect(inspectObject)}`; + 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 === Symbol.toStringTag) { + return object.constructor?.name; + } else if (keys.includes(key)) { + return Reflect.get(object, key); + } else { + return undefined; + } + }, + getOwnPropertyDescriptor(_target, key) { + if (!keys.includes(key)) { + return undefined; + } + + return Reflect.getOwnPropertyDescriptor(object, key) ?? + (object.prototype && + Reflect.getOwnPropertyDescriptor(object.prototype, key)) ?? + { + configurable: true, + enumerable: true, + value: object[key], + }; + }, + has(_target, key) { + return keys.includes(key); + }, + ownKeys() { + return keys; + }, + }); } function defineEnumerableProps( @@ -1053,14 +1085,14 @@ } [Symbol.for("Deno.customInspect")](inspect) { - return buildCustomInspectOutput(this, [ + return inspect(buildFilteredPropertyInspectObject(this, [ ...EVENT_PROPS, "message", "filename", "lineno", "colno", "error", - ], inspect); + ])); } } @@ -1107,12 +1139,12 @@ } [Symbol.for("Deno.customInspect")](inspect) { - return buildCustomInspectOutput(this, [ + return inspect(buildFilteredPropertyInspectObject(this, [ ...EVENT_PROPS, "wasClean", "code", "reason", - ], inspect); + ])); } } @@ -1135,12 +1167,12 @@ } [Symbol.for("Deno.customInspect")](inspect) { - return buildCustomInspectOutput(this, [ + return inspect(buildFilteredPropertyInspectObject(this, [ ...EVENT_PROPS, "data", "origin", "lastEventId", - ], inspect); + ])); } } @@ -1165,10 +1197,10 @@ } [Symbol.for("Deno.customInspect")](inspect) { - return buildCustomInspectOutput(this, [ + return inspect(buildFilteredPropertyInspectObject(this, [ ...EVENT_PROPS, "detail", - ], inspect); + ])); } } @@ -1188,12 +1220,12 @@ } [Symbol.for("Deno.customInspect")](inspect) { - return buildCustomInspectOutput(this, [ + return inspect(buildFilteredPropertyInspectObject(this, [ ...EVENT_PROPS, "lengthComputable", "loaded", "total", - ], inspect); + ])); } }