aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-11-27 19:37:14 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-11-27 19:37:14 -0800
commit002f4ecc9e0dfb19ea9e5032b812b9b976706995 (patch)
tree34fae197918c8064d456a855b987c0b8bd36dc03
parent495f25501f7bcfddec6e91cca7815cc6220fb53e (diff)
downloadbun-002f4ecc9e0dfb19ea9e5032b812b9b976706995.tar.gz
bun-002f4ecc9e0dfb19ea9e5032b812b9b976706995.tar.zst
bun-002f4ecc9e0dfb19ea9e5032b812b9b976706995.zip
Fix crash in console.log
-rw-r--r--src/bun.js/bindings/bindings.cpp23
-rw-r--r--test/bun.js/empty.js1
-rw-r--r--test/bun.js/inspect.test.js48
-rw-r--r--test/bun.js/spawn-streaming-stdout.test.ts57
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;
+ })();
});