aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorGravatar Ashcon Partovi <ashcon@partovi.net> 2023-08-24 19:30:55 -0700
committerGravatar Ashcon Partovi <ashcon@partovi.net> 2023-08-24 20:11:20 -0700
commitdacd00d7082fede526867f145dd36456143ee263 (patch)
tree02dce991c7b1eaef7db1b5b4a896a863c5463ecc /packages
parenta068e9c8db5702a5e84e3ee9b3f0d99ab130e85d (diff)
downloadbun-dacd00d7082fede526867f145dd36456143ee263.tar.gz
bun-dacd00d7082fede526867f145dd36456143ee263.tar.zst
bun-dacd00d7082fede526867f145dd36456143ee263.zip
Improve preview code
Diffstat (limited to 'packages')
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/__snapshots__/preview.test.ts.snap143
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/adapter.ts106
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/fixtures/preview.js99
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/preview.test.ts62
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/preview.ts110
5 files changed, 415 insertions, 105 deletions
diff --git a/packages/bun-debug-adapter-protocol/debugger/__snapshots__/preview.test.ts.snap b/packages/bun-debug-adapter-protocol/debugger/__snapshots__/preview.test.ts.snap
new file mode 100644
index 000000000..0acc17575
--- /dev/null
+++ b/packages/bun-debug-adapter-protocol/debugger/__snapshots__/preview.test.ts.snap
@@ -0,0 +1,143 @@
+// Bun Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`remoteObjectToString 1`] = `"undefined"`;
+
+exports[`remoteObjectToString 2`] = `"null"`;
+
+exports[`remoteObjectToString 3`] = `"true"`;
+
+exports[`remoteObjectToString 4`] = `"false"`;
+
+exports[`remoteObjectToString 5`] = `"0"`;
+
+exports[`remoteObjectToString 6`] = `"1"`;
+
+exports[`remoteObjectToString 7`] = `"3.141592653589793"`;
+
+exports[`remoteObjectToString 8`] = `"-2.718281828459045"`;
+
+exports[`remoteObjectToString 9`] = `"NaN"`;
+
+exports[`remoteObjectToString 10`] = `"Infinity"`;
+
+exports[`remoteObjectToString 11`] = `"-Infinity"`;
+
+exports[`remoteObjectToString 12`] = `"0n"`;
+
+exports[`remoteObjectToString 13`] = `"1n"`;
+
+exports[`remoteObjectToString 14`] = `"10000000000000n"`;
+
+exports[`remoteObjectToString 15`] = `"-10000000000000n"`;
+
+exports[`remoteObjectToString 16`] = `""""`;
+
+exports[`remoteObjectToString 17`] = `"" ""`;
+
+exports[`remoteObjectToString 18`] = `""Hello""`;
+
+exports[`remoteObjectToString 19`] = `""Hello World""`;
+
+exports[`remoteObjectToString 20`] = `"Array(0)"`;
+
+exports[`remoteObjectToString 21`] = `"Array(3) [1, 2, 3]"`;
+
+exports[`remoteObjectToString 22`] = `"Array(4) ["a", 1, null, undefined]"`;
+
+exports[`remoteObjectToString 23`] = `"Array(2) [1, Array]"`;
+
+exports[`remoteObjectToString 24`] = `"Array(1) [Array]"`;
+
+exports[`remoteObjectToString 25`] = `"{}"`;
+
+exports[`remoteObjectToString 26`] = `"{a: 1}"`;
+
+exports[`remoteObjectToString 27`] = `"{a: 1, b: 2, c: 3}"`;
+
+exports[`remoteObjectToString 28`] = `"{a: Object}"`;
+
+exports[`remoteObjectToString 29`] = `
+"ƒ() {
+}"
+`;
+
+exports[`remoteObjectToString 30`] = `
+"ƒ namedFunction() {
+}"
+`;
+
+exports[`remoteObjectToString 31`] = `
+"class {
+}"
+`;
+
+exports[`remoteObjectToString 32`] = `
+"class namedClass {
+}"
+`;
+
+exports[`remoteObjectToString 33`] = `
+"class namedClass {
+ a() {
+ }
+ b = 1;
+ c = [
+ null,
+ undefined,
+ "a",
+ {
+ a: 1,
+ b: 2,
+ c: 3
+ }
+ ];
+}"
+`;
+
+exports[`remoteObjectToString 34`] = `"Wed Dec 31 1969 16:00:00 GMT-0800 (Pacific Standard Time)"`;
+
+exports[`remoteObjectToString 35`] = `"Invalid Date"`;
+
+exports[`remoteObjectToString 36`] = `"/(?:)/"`;
+
+exports[`remoteObjectToString 37`] = `"/abc/"`;
+
+exports[`remoteObjectToString 38`] = `"/abc/g"`;
+
+exports[`remoteObjectToString 39`] = `"/abc/"`;
+
+exports[`remoteObjectToString 40`] = `"Set(0)"`;
+
+exports[`remoteObjectToString 41`] = `"Set(3) [1, 2, 3]"`;
+
+exports[`remoteObjectToString 42`] = `"WeakSet(0)"`;
+
+exports[`remoteObjectToString 43`] = `"WeakSet(3) [{a: 1}, {b: 2}, {c: 3}]"`;
+
+exports[`remoteObjectToString 44`] = `"Map(0)"`;
+
+exports[`remoteObjectToString 45`] = `"Map(3) {"a" => 1, "b" => 2, "c" => 3}"`;
+
+exports[`remoteObjectToString 46`] = `"WeakMap(0)"`;
+
+exports[`remoteObjectToString 47`] = `"WeakMap(3) {{a: 1} => 1, {b: 2} => 2, {c: 3} => 3}"`;
+
+exports[`remoteObjectToString 48`] = `"Symbol()"`;
+
+exports[`remoteObjectToString 49`] = `"Symbol(namedSymbol)"`;
+
+exports[`remoteObjectToString 50`] = `"Error"`;
+
+exports[`remoteObjectToString 51`] = `"TypeError: This is a TypeError"`;
+
+exports[`remoteObjectToString 52`] = `"Headers {append: ƒ, delete: ƒ, get: ƒ, getAll: ƒ, has: ƒ, …}"`;
+
+exports[`remoteObjectToString 53`] = `"Headers {a: "1", append: ƒ, b: "2", delete: ƒ, get: ƒ, …}"`;
+
+exports[`remoteObjectToString 54`] = `"Request {arrayBuffer: ƒ, blob: ƒ, body: null, bodyUsed: false, cache: "default", …}"`;
+
+exports[`remoteObjectToString 55`] = `"Request {arrayBuffer: ƒ, blob: ƒ, body: ReadableStream, bodyUsed: false, cache: "default", …}"`;
+
+exports[`remoteObjectToString 56`] = `"Response {arrayBuffer: ƒ, blob: ƒ, body: null, bodyUsed: false, clone: ƒ, …}"`;
+
+exports[`remoteObjectToString 57`] = `"Response {arrayBuffer: ƒ, blob: ƒ, body: ReadableStream, bodyUsed: false, clone: ƒ, …}"`;
diff --git a/packages/bun-debug-adapter-protocol/debugger/adapter.ts b/packages/bun-debug-adapter-protocol/debugger/adapter.ts
index 01bbd6052..9dc55fe38 100644
--- a/packages/bun-debug-adapter-protocol/debugger/adapter.ts
+++ b/packages/bun-debug-adapter-protocol/debugger/adapter.ts
@@ -6,6 +6,7 @@ import type { ChildProcess } from "node:child_process";
import { spawn, spawnSync } from "node:child_process";
import capabilities from "./capabilities";
import { Location, SourceMap } from "./sourcemap";
+import { remoteObjectToString } from "./preview";
import { compare, parse } from "semver";
type InitializeRequest = DAP.InitializeRequest & {
@@ -1521,111 +1522,6 @@ function callFrameToId(callFrame: JSC.Console.CallFrame): string {
return `${url}:${lineNumber}:${columnNumber}`;
}
-function remoteObjectToString(remoteObject: JSC.Runtime.RemoteObject): string {
- const { type, subtype, value, description, className, preview } = remoteObject;
- switch (type) {
- case "undefined":
- return "undefined";
- case "boolean":
- case "string":
- return JSON.stringify(value ?? description);
- case "number":
- return description ?? JSON.stringify(value);
- case "symbol":
- case "bigint":
- return description!;
- case "function":
- return description!.replace("function", "ƒ") || "ƒ";
- }
- switch (subtype) {
- case "null":
- return "null";
- case "regexp":
- case "date":
- case "error":
- return description!;
- }
- if (preview) {
- return objectPreviewToString(preview);
- }
- if (className) {
- return className;
- }
- return description || "Object";
-}
-
-function objectPreviewToString(objectPreview: JSC.Runtime.ObjectPreview): string {
- const { type, subtype, entries, properties, overflow, description, size } = objectPreview;
- if (type !== "object") {
- return remoteObjectToString(objectPreview);
- }
- let items: string[];
- if (entries) {
- items = entries.map(entryPreviewToString);
- } else if (properties) {
- if (isIndexed(subtype)) {
- items = properties.map(indexedPropertyPreviewToString);
- } else {
- items = properties.map(namedPropertyPreviewToString);
- }
- } else {
- items = ["…"];
- }
- if (overflow) {
- items.push("…");
- }
- let label: string;
- if (description === "Object") {
- label = "";
- } else if (size === undefined) {
- label = description!;
- } else {
- label = `${description}(${size})`;
- }
- if (!items.length) {
- return label || "{}";
- }
- if (label) {
- label += " ";
- }
- if (isIndexed(subtype)) {
- return `${label}[${items.join(", ")}]`;
- }
- return `${label}{${items.join(", ")}}`;
-}
-
-function propertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
- const { type, value, ...preview } = propertyPreview;
- if (type === "accessor") {
- return "ƒ";
- }
- return remoteObjectToString({ ...preview, type, description: value });
-}
-
-function entryPreviewToString(entryPreview: JSC.Runtime.EntryPreview): string {
- const { key, value } = entryPreview;
- if (key) {
- return `${objectPreviewToString(key)} => ${objectPreviewToString(value)}`;
- }
- return objectPreviewToString(value);
-}
-
-function namedPropertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
- const { name, valuePreview } = propertyPreview;
- if (valuePreview) {
- return `${name}: ${objectPreviewToString(valuePreview)}`;
- }
- return `${name}: ${propertyPreviewToString(propertyPreview)}`;
-}
-
-function indexedPropertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
- const { valuePreview } = propertyPreview;
- if (valuePreview) {
- return objectPreviewToString(valuePreview);
- }
- return propertyPreviewToString(propertyPreview);
-}
-
function sanitizeExpression(expression: string): string {
expression = expression.trim();
if (expression.startsWith("{")) {
diff --git a/packages/bun-debug-adapter-protocol/debugger/fixtures/preview.js b/packages/bun-debug-adapter-protocol/debugger/fixtures/preview.js
new file mode 100644
index 000000000..15062240b
--- /dev/null
+++ b/packages/bun-debug-adapter-protocol/debugger/fixtures/preview.js
@@ -0,0 +1,99 @@
+console.log(
+ undefined,
+ null,
+ true,
+ false,
+ 0,
+ 1,
+ Math.PI,
+ -Math.E,
+ NaN,
+ Infinity,
+ -Infinity,
+ BigInt(0),
+ BigInt(1),
+ BigInt("10000000000000"),
+ BigInt("-10000000000000"),
+ "",
+ " ",
+ "Hello",
+ "Hello World",
+ [],
+ [1, 2, 3],
+ ["a", 1, null, undefined],
+ [1, [2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]],
+ [[[[[]]]]],
+ {},
+ { a: 1 },
+ { a: 1, b: 2, c: 3 },
+ { a: { b: { c: { d: { e: { f: { g: { h: { i: { j: 10 } } } } } } } } } },
+ function () {},
+ function namedFunction() {},
+ class {},
+ class namedClass {},
+ class namedClass {
+ a() {}
+ b = 1;
+ c = [
+ null,
+ undefined,
+ "a",
+ {
+ a: 1,
+ b: 2,
+ c: 3,
+ },
+ ];
+ },
+ new Date(0),
+ new Date(NaN),
+ new RegExp(),
+ new RegExp("abc"),
+ new RegExp("abc", "g"),
+ /abc/,
+ new Set(),
+ new Set([1, 2, 3]),
+ new WeakSet(),
+ new WeakSet([{ a: 1 }, { b: 2 }, { c: 3 }]),
+ new Map(),
+ new Map([
+ ["a", 1],
+ ["b", 2],
+ ["c", 3],
+ ]),
+ new WeakMap(),
+ new WeakMap([
+ [{ a: 1 }, 1],
+ [{ b: 2 }, 2],
+ [{ c: 3 }, 3],
+ ]),
+ Symbol(),
+ Symbol("namedSymbol"),
+ new Error(),
+ new TypeError("This is a TypeError"),
+ //"a".repeat(10000),
+ //["a"].fill("a", 0, 10000),
+ new Headers(),
+ new Headers({
+ a: "1",
+ b: "2",
+ }),
+ new Request("https://example.com/"),
+ new Request("https://example.com/", {
+ method: "POST",
+ headers: {
+ a: "1",
+ b: "2",
+ },
+ body: '{"example":true}',
+ }),
+ new Response(),
+ new Response('{"example":true}', {
+ status: 200,
+ statusText: "OK",
+ headers: {
+ a: "1",
+ b: "2",
+ },
+ }),
+);
diff --git a/packages/bun-debug-adapter-protocol/debugger/preview.test.ts b/packages/bun-debug-adapter-protocol/debugger/preview.test.ts
new file mode 100644
index 000000000..666913719
--- /dev/null
+++ b/packages/bun-debug-adapter-protocol/debugger/preview.test.ts
@@ -0,0 +1,62 @@
+import { beforeAll, afterAll, test, expect } from "bun:test";
+import type { JSC } from "../../bun-inspector-protocol";
+import { WebSocketInspector } from "../../bun-inspector-protocol";
+import type { PipedSubprocess } from "bun";
+import { spawn } from "bun";
+import { remoteObjectToString } from "./preview";
+
+let subprocess: PipedSubprocess | undefined;
+let objects: JSC.Runtime.RemoteObject[] = [];
+
+beforeAll(async () => {
+ subprocess = spawn({
+ cwd: import.meta.dir,
+ cmd: [process.argv0, "--inspect-wait=0", "fixtures/preview.js"],
+ stdout: "pipe",
+ stderr: "pipe",
+ stdin: "pipe",
+ });
+ const decoder = new TextDecoder();
+ let url: URL;
+ for await (const chunk of subprocess!.stdout) {
+ const text = decoder.decode(chunk);
+ if (text.includes("ws://")) {
+ url = new URL(/(ws:\/\/.*)/.exec(text)![0]);
+ break;
+ }
+ }
+ objects = await new Promise((resolve, reject) => {
+ const inspector = new WebSocketInspector({
+ url,
+ listener: {
+ ["Inspector.connected"]: () => {
+ inspector.send("Inspector.enable");
+ inspector.send("Runtime.enable");
+ inspector.send("Console.enable");
+ inspector.send("Debugger.enable");
+ inspector.send("Debugger.resume");
+ inspector.send("Inspector.initialized");
+ },
+ ["Inspector.disconnected"]: error => {
+ reject(error);
+ },
+ ["Console.messageAdded"]: ({ message }) => {
+ const { parameters } = message;
+ resolve(parameters!);
+ inspector.close();
+ },
+ },
+ });
+ inspector.start();
+ });
+});
+
+afterAll(() => {
+ subprocess?.kill();
+});
+
+test("remoteObjectToString", () => {
+ for (const object of objects) {
+ expect(remoteObjectToString(object)).toMatchSnapshot();
+ }
+});
diff --git a/packages/bun-debug-adapter-protocol/debugger/preview.ts b/packages/bun-debug-adapter-protocol/debugger/preview.ts
new file mode 100644
index 000000000..6012623d2
--- /dev/null
+++ b/packages/bun-debug-adapter-protocol/debugger/preview.ts
@@ -0,0 +1,110 @@
+import type { JSC } from "../../bun-inspector-protocol";
+
+export function remoteObjectToString(remoteObject: JSC.Runtime.RemoteObject): string {
+ const { type, subtype, value, description, className, preview } = remoteObject;
+ switch (type) {
+ case "undefined":
+ return "undefined";
+ case "boolean":
+ case "number":
+ return description ?? JSON.stringify(value);
+ case "string":
+ return JSON.stringify(value ?? description);
+ case "symbol":
+ case "bigint":
+ return description!;
+ case "function":
+ return description!.replace("function", "ƒ") || "ƒ";
+ }
+ switch (subtype) {
+ case "null":
+ return "null";
+ case "regexp":
+ case "date":
+ case "error":
+ return description!;
+ }
+ if (preview) {
+ return objectPreviewToString(preview);
+ }
+ if (className) {
+ return className;
+ }
+ return description || "Object";
+}
+
+export function objectPreviewToString(objectPreview: JSC.Runtime.ObjectPreview): string {
+ const { type, subtype, entries, properties, overflow, description, size } = objectPreview;
+ if (type !== "object") {
+ return remoteObjectToString(objectPreview);
+ }
+ let items: string[];
+ if (entries) {
+ items = entries.map(entryPreviewToString).sort();
+ } else if (properties) {
+ if (isIndexed(subtype)) {
+ items = properties.map(indexedPropertyPreviewToString).sort();
+ } else {
+ items = properties.map(namedPropertyPreviewToString).sort();
+ }
+ } else {
+ items = ["…"];
+ }
+ if (overflow) {
+ items.push("…");
+ }
+ let label: string;
+ if (description === "Object") {
+ label = "";
+ } else if (size === undefined) {
+ label = description!;
+ } else {
+ label = `${description}(${size})`;
+ }
+ if (!items.length) {
+ return label || "{}";
+ }
+ if (label) {
+ label += " ";
+ }
+ if (isIndexed(subtype)) {
+ return `${label}[${items.join(", ")}]`;
+ }
+ return `${label}{${items.join(", ")}}`;
+}
+
+function propertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
+ const { type, value, ...preview } = propertyPreview;
+ if (type === "accessor") {
+ return "ƒ";
+ }
+ return remoteObjectToString({ ...preview, type, description: value });
+}
+
+function entryPreviewToString(entryPreview: JSC.Runtime.EntryPreview): string {
+ const { key, value } = entryPreview;
+ if (key) {
+ return `${objectPreviewToString(key)} => ${objectPreviewToString(value)}`;
+ }
+ return objectPreviewToString(value);
+}
+
+function namedPropertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
+ const { name, valuePreview } = propertyPreview;
+ if (valuePreview) {
+ return `${name}: ${objectPreviewToString(valuePreview)}`;
+ }
+ return `${name}: ${propertyPreviewToString(propertyPreview)}`;
+}
+
+function indexedPropertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
+ const { valuePreview } = propertyPreview;
+ if (valuePreview) {
+ return objectPreviewToString(valuePreview);
+ }
+ return propertyPreviewToString(propertyPreview);
+}
+
+function isIndexed(type?: JSC.Runtime.RemoteObject["subtype"]): boolean {
+ return type === "array" || type === "set" || type === "weakset";
+}