static void JSC__JSValue__forEachPropertyImpl()

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;
    }
}