in src/bun.js/bindings/bindings.cpp [5094:5313]
static void JSC__JSValue__forEachPropertyImpl(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, void* arg2, void (*iter)(JSC__JSGlobalObject* arg0, void* ctx, ZigString* arg2, JSC__JSValue JSValue3, bool isSymbol, bool isPrivateSymbol))
{
ASSERT_NO_PENDING_EXCEPTION(globalObject);
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
JSC::JSObject* object = value.getObject();
if (!object)
return;
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_CATCH_SCOPE(vm);
size_t prototypeCount = 0;
JSC::Structure* structure = object->structure();
bool fast = !nonIndexedOnly && canPerformFastPropertyEnumerationForIterationBun(structure);
JSValue prototypeObject = value;
if (fast) {
if (structure->outOfLineSize() == 0 && structure->inlineSize() == 0) {
fast = false;
if (JSValue proto = object->getPrototype(vm, globalObject)) {
if ((structure = proto.structureOrNull())) {
prototypeObject = proto;
fast = canPerformFastPropertyEnumerationForIterationBun(structure);
prototypeCount = 1;
}
}
}
}
auto* propertyNames = vm.propertyNames;
auto& builtinNames = WebCore::builtinNames(vm);
WTF::Vector<Identifier, 6> visitedProperties;
restart:
if (fast) {
bool anyHits = false;
JSC::JSObject* objectToUse = prototypeObject.getObject();
structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
if ((entry.attributes() & (PropertyAttribute::Function)) == 0 && (entry.attributes() & (PropertyAttribute::Builtin)) != 0) {
return true;
}
auto* prop = entry.key();
if (prop == propertyNames->constructor
|| prop == propertyNames->underscoreProto
|| prop == propertyNames->toStringTagSymbol)
return true;
if (builtinNames.bunNativePtrPrivateName() == prop)
return true;
if (visitedProperties.contains(Identifier::fromUid(vm, prop))) {
return true;
}
visitedProperties.append(Identifier::fromUid(vm, prop));
ZigString key = toZigString(prop);
JSC::JSValue propertyValue = JSValue();
if (objectToUse == object) {
propertyValue = objectToUse->getDirect(entry.offset());
if (!propertyValue) {
scope.clearException();
return true;
}
}
if (!propertyValue || propertyValue.isGetterSetter() && !((entry.attributes() & PropertyAttribute::Accessor) != 0)) {
propertyValue = objectToUse->getIfPropertyExists(globalObject, prop);
}
if (scope.exception())
scope.clearException();
if (!propertyValue)
return true;
anyHits = true;
JSC::EnsureStillAliveScope ensureStillAliveScope(propertyValue);
bool isPrivate = prop->isSymbol() && Identifier::fromUid(vm, prop).isPrivateName();
if (isPrivate && !JSC::Options::showPrivateScriptsInStackTraces())
return true;
iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), prop->isSymbol(), isPrivate);
return true;
});
if (scope.exception()) {
scope.clearException();
}
if (anyHits) {
if (prototypeCount++ < 5) {
if (JSValue proto = prototypeObject.getPrototype(globalObject)) {
if (!(proto == globalObject->objectPrototype() || proto == globalObject->functionPrototype() || (proto.inherits<JSGlobalProxy>() && jsCast<JSGlobalProxy*>(proto)->target() != globalObject))) {
if ((structure = proto.structureOrNull())) {
prototypeObject = proto;
fast = canPerformFastPropertyEnumerationForIterationBun(structure);
goto restart;
}
}
}
}
return;
}
}
JSC::PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
{
JSObject* iterating = prototypeObject.getObject();
while (iterating && !(iterating == globalObject->objectPrototype() || iterating == globalObject->functionPrototype() || (iterating->inherits<JSGlobalProxy>() && jsCast<JSGlobalProxy*>(iterating)->target() != globalObject)) && prototypeCount++ < 5) {
if constexpr (nonIndexedOnly) {
iterating->getOwnNonIndexPropertyNames(globalObject, properties, DontEnumPropertiesMode::Include);
} else {
iterating->methodTable()->getOwnPropertyNames(iterating, globalObject, properties, DontEnumPropertiesMode::Include);
}
RETURN_IF_EXCEPTION(scope, void());
for (auto& property : properties) {
if (UNLIKELY(property.isEmpty() || property.isNull()))
continue;
// ignore constructor
if (property == propertyNames->constructor || builtinNames.bunNativePtrPrivateName() == property)
continue;
if constexpr (nonIndexedOnly) {
if (property == propertyNames->length) {
continue;
}
}
JSC::PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
if (!object->getPropertySlot(globalObject, property, slot))
continue;
if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) {
if (property == propertyNames->underscoreProto
|| property == propertyNames->toStringTagSymbol)
continue;
}
if (visitedProperties.contains(property))
continue;
visitedProperties.append(property);
ZigString key = toZigString(property.isSymbol() && !property.isPrivateName() ? property.impl() : property.string());
if (key.len == 0)
continue;
JSC::JSValue propertyValue = jsUndefined();
if ((slot.attributes() & PropertyAttribute::DontEnum) != 0) {
if ((slot.attributes() & PropertyAttribute::Accessor) != 0) {
// If we can't use getPureResult, let's at least say it was a [Getter]
if (!slot.isCacheableGetter()) {
propertyValue = slot.getterSetter();
} else {
propertyValue = slot.getPureResult();
}
} else if (slot.attributes() & PropertyAttribute::BuiltinOrFunction) {
propertyValue = slot.getValue(globalObject, property);
} else if (slot.isCustom()) {
propertyValue = slot.getValue(globalObject, property);
} else if (slot.isValue()) {
propertyValue = slot.getValue(globalObject, property);
} else if (object->getOwnPropertySlot(object, globalObject, property, slot)) {
propertyValue = slot.getValue(globalObject, property);
}
} else if (slot.isAccessor()) {
// If we can't use getPureResult, let's at least say it was a [Getter]
if (!slot.isCacheableGetter()) {
propertyValue = slot.getterSetter();
} else {
propertyValue = slot.getPureResult();
}
} else {
propertyValue = slot.getValue(globalObject, property);
}
if (scope.exception()) {
scope.clearException();
propertyValue = jsUndefined();
}
JSC::EnsureStillAliveScope ensureStillAliveScope(propertyValue);
bool isPrivate = property.isPrivateName();
if (isPrivate && !JSC::Options::showPrivateScriptsInStackTraces())
continue;
iter(globalObject, arg2, &key, JSC::JSValue::encode(propertyValue), property.isSymbol(), isPrivate);
}
if constexpr (nonIndexedOnly) {
break;
}
// reuse memory
properties.data()->propertyNameVector().shrink(0);
if (iterating->isCallable())
break;
if (iterating == globalObject)
break;
iterating = iterating->getPrototype(vm, globalObject).getObject();
}
}
properties.releaseData();
if (scope.exception()) {
scope.clearException();
return;
}
}