diff options
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 28 | ||||
-rw-r--r-- | test/bun.js/inspect.test.js | 9 |
2 files changed, 29 insertions, 8 deletions
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index eb0529e40..4b05f542e 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3543,6 +3543,8 @@ void JSC__JSValue__forEachProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* g } auto* clientData = WebCore::clientData(vm); + WTF::Vector<Identifier, 6> visitedProperties; + restart: if (fast) { bool anyHits = false; @@ -3555,24 +3557,30 @@ restart: if ((entry.attributes() & (PropertyAttribute::Function)) == 0 && (entry.attributes() & (PropertyAttribute::Builtin)) != 0) { return true; } + auto* prop = entry.key(); + + if (prop == vm.propertyNames->constructor + || prop == vm.propertyNames->length + || prop == vm.propertyNames->underscoreProto + || prop == vm.propertyNames->toStringTagSymbol) + return true; - if (entry.key() == vm.propertyNames->constructor - || entry.key() == vm.propertyNames->length - || entry.key() == vm.propertyNames->underscoreProto - || entry.key() == vm.propertyNames->toStringTagSymbol) + if (clientData->builtinNames().bunNativePtrPrivateName() == prop) return true; - if (clientData->builtinNames().bunNativePtrPrivateName() == entry.key()) + if (visitedProperties.contains(Identifier::fromUid(vm, prop))) { return true; + } + visitedProperties.append(Identifier::fromUid(vm, prop)); - ZigString key = toZigString(entry.key()); + ZigString key = toZigString(prop); if (key.len == 0) return true; JSC::JSValue propertyValue = objectToUse == object ? objectToUse->getDirect(entry.offset()) : JSValue(); if (!propertyValue || propertyValue.isGetterSetter()) { - propertyValue = objectToUse->get(globalObject, entry.key()); + propertyValue = objectToUse->get(globalObject, prop); } if (scope.exception()) @@ -3583,7 +3591,7 @@ restart: anyHits = true; JSC::EnsureStillAliveScope ensureStillAliveScope(propertyValue); - iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), entry.key()->isSymbol()); + iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), prop->isSymbol()); return true; }); if (scope.exception()) { @@ -3641,6 +3649,10 @@ restart: continue; } + if (visitedProperties.contains(property)) + continue; + visitedProperties.append(property); + ZigString key = toZigString(property.isSymbol() && !property.isPrivateName() ? property.impl() : property.string()); if (key.len == 0) diff --git a/test/bun.js/inspect.test.js b/test/bun.js/inspect.test.js index 738442211..d0f4f026a 100644 --- a/test/bun.js/inspect.test.js +++ b/test/bun.js/inspect.test.js @@ -1,5 +1,14 @@ import { it, expect, describe } from "bun:test"; +it("when prototype defines the same property, don't print the same property twice", () => { + var base = { + foo: "123", + }; + var obj = Object.create(base); + obj.foo = "456"; + expect(Bun.inspect(obj).trim()).toBe('{\n foo: "456"\n}'.trim()); +}); + it("Blob inspect", () => { expect(Bun.inspect(new Blob(["123"]))).toBe(`Blob (3 bytes)`); expect(Bun.inspect(new Blob(["123".repeat(900)]))).toBe(`Blob (2.70 KB)`); |