diff options
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 23 | ||||
-rw-r--r-- | test/bun.js/empty.js | 1 | ||||
-rw-r--r-- | test/bun.js/inspect.test.js | 48 | ||||
-rw-r--r-- | test/bun.js/spawn-streaming-stdout.test.ts | 57 |
4 files changed, 88 insertions, 41 deletions
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 864e431c6..b2b0973db 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3765,6 +3765,7 @@ void JSC__JSValue__forEachProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* g restart: if (fast) { bool anyHits = false; + JSC::JSObject* objectToUse = prototypeObject.getObject(); structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { if ((entry.attributes() & PropertyAttribute::Accessor) != 0 && (entry.attributes() & PropertyAttribute::DontEnum) != 0) { return true; @@ -3785,9 +3786,9 @@ restart: if (key.len == 0) return true; - JSC::JSValue propertyValue = object->getDirect(entry.offset()); + JSC::JSValue propertyValue = objectToUse->getDirect(entry.offset()); if (!propertyValue || propertyValue.isGetterSetter()) { - propertyValue = object->get(globalObject, entry.key()); + propertyValue = objectToUse->get(globalObject, entry.key()); } if (scope.exception()) @@ -3809,12 +3810,14 @@ restart: if (anyHits) { - if (prototypeCount++ < 3) { + if (prototypeCount++ < 5) { if (JSValue proto = prototypeObject.getPrototype(globalObject)) { - if ((structure = proto.structureOrNull())) { - prototypeObject = proto; - fast = canPerformFastPropertyEnumerationForIterationBun(structure); - goto restart; + if (!(proto == globalObject->objectPrototype() || proto == globalObject->functionPrototype())) { + if ((structure = proto.structureOrNull())) { + prototypeObject = proto; + fast = canPerformFastPropertyEnumerationForIterationBun(structure); + goto restart; + } } } } @@ -3828,7 +3831,7 @@ restart: JSObject* iterating = prototypeObject.getObject(); - while (iterating && prototypeCount++ < 3 && !(iterating == globalObject->objectPrototype() || iterating == globalObject->functionPrototype())) { + while (iterating && !(iterating == globalObject->objectPrototype() || iterating == globalObject->functionPrototype()) && prototypeCount++ < 5) { iterating->methodTable()->getOwnPropertyNames(iterating, globalObject, properties, DontEnumPropertiesMode::Include); RETURN_IF_EXCEPTION(scope, void()); for (auto& property : properties) { @@ -3839,8 +3842,8 @@ restart: if (property == vm.propertyNames->constructor || clientData->builtinNames().bunNativePtrPrivateName() == property) continue; - JSC::PropertySlot slot(object, PropertySlot::InternalMethodType::Get); - if (!object->getPropertySlot(globalObject, property, slot)) + JSC::PropertySlot slot(iterating, PropertySlot::InternalMethodType::Get); + if (!iterating->getPropertySlot(globalObject, property, slot)) continue; if ((slot.attributes() & PropertyAttribute::Accessor) != 0) { diff --git a/test/bun.js/empty.js b/test/bun.js/empty.js new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/test/bun.js/empty.js @@ -0,0 +1 @@ +export {}; diff --git a/test/bun.js/inspect.test.js b/test/bun.js/inspect.test.js index 3646b3706..5dc0bc645 100644 --- a/test/bun.js/inspect.test.js +++ b/test/bun.js/inspect.test.js @@ -273,3 +273,51 @@ describe("latin1 supplemental", () => { // }); } }); + +const fixture = [ + () => Bun.file("/tmp/log.txt").stream(), + () => Bun.file("/tmp/log.1.txt").stream().getReader(), + () => Bun.file("/tmp/log.2.txt").writer(), + () => + new WritableStream({ + write(chunk) {}, + }), + () => require("events"), + () => { + return new import.meta.require("events").EventEmitter(); + }, + async () => await import("node:assert"), + async () => await import("./empty.js"), + () => import.meta.require("./empty.js"), + () => new Proxy({ yolo: 1 }, {}), + () => + new Proxy( + { yolo: 1 }, + { + get(target, prop) { + return prop + "!"; + }, + has(target, prop) { + return true; + }, + ownKeys() { + return ["foo"]; + }, + }, + ), +]; + +describe("crash testing", () => { + for (let input of fixture) { + it(`inspecting "${input + .toString() + .slice(0, 20) + .replaceAll("\n", "\\n")}" doesn't crash`, async () => { + try { + Bun.inspect(await input()); + } catch (e) { + // this can throw its fine + } + }); + } +}); diff --git a/test/bun.js/spawn-streaming-stdout.test.ts b/test/bun.js/spawn-streaming-stdout.test.ts index 1f736533a..1d19fe5da 100644 --- a/test/bun.js/spawn-streaming-stdout.test.ts +++ b/test/bun.js/spawn-streaming-stdout.test.ts @@ -5,37 +5,32 @@ import { gcTick } from "gc"; test("spawn can read from stdout multiple chunks", async () => { gcTick(true); - var exited; - await (async function () { - const proc = spawn({ - cmd: [bunExe(), import.meta.dir + "/spawn-streaming-stdout-repro.js"], - stdout: "pipe", - env: { - BUN_DEBUG_QUIET_LOGS: 1, - }, - }); - exited = proc.exited; - gcTick(true); - var counter = 0; - try { - for await (var chunk of proc.stdout) { - gcTick(true); - expect(new TextDecoder().decode(chunk)).toBe("Wrote to stdout\n"); - counter++; - if (counter > 3) break; - } - } catch (e) { - console.log(e.stack); - throw e; - } - gcTick(true); + for (let i = 0; i < 10; i++) + await (async function () { + var exited; + const proc = spawn({ + cmd: [bunExe(), import.meta.dir + "/spawn-streaming-stdout-repro.js"], + stdout: "pipe", + stderr: "ignore", + env: { + BUN_DEBUG_QUIET_LOGS: 1, + }, + }); + exited = proc.exited; + let counter = 0; + try { + for await (var chunk of proc.stdout) { + expect(new TextDecoder().decode(chunk)).toBe("Wrote to stdout\n"); + counter++; - expect(counter).toBe(4); - gcTick(); - proc.kill(); - gcTick(); - })(); - await exited; - gcTick(true); + if (counter > 3) break; + } + } catch (e) { + console.log(e.stack); + throw e; + } + expect(counter).toBe(4); + await exited; + })(); }); |