/*
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
 *  Copyright (C) 2003-2020 Apple Inc. All rights reserved.
 *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#pragma once

#include "BrandedStructure.h"
#include "Error.h"
#include "JSObject.h"
#include "JSTypedArrays.h"
#include "Lookup.h"
#include "StructureInlines.h"
#include "TypedArrayType.h"

namespace JSC {

ALWAYS_INLINE bool JSObject::getPropertySlot(JSGlobalData& vm, ExecState* exec, unsigned propertyName, PropertySlot& slot)
{
    JSObject* object = this;
    while (true) {
        Structure* structure = object->structure();
    	bool hasSlot = structure->classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot);
    	RETURN_IF_EXCEPTION(vm, false);
		if (hasSlot)
			return true;
        if (UNLIKELY(slot.isVMInquiry() && slot.isTaintedByOpaqueObject()))
            return false;
        if (structure->typeInfo().type() == ProxyObjectType && slot.internalMethodType() == PropertySlot::InternalMethodType::HasProperty)
            return false;
        if (isTypedArrayType(structure->typeInfo().type()) && propertyName >= jsCast<JSArrayBufferView*>(object)->length())
            return false;
        JSValue prototype;
        if (LIKELY(!structure->typeInfo().overridesGetPrototype() || slot.internalMethodType() == PropertySlot::InternalMethodType::VMInquiry))
            prototype = object->getPrototypeDirect();
        else {
            prototype = object->getPrototype(vm, exec);
        	RETURN_IF_EXCEPTION(vm, false);
        }
        if (!prototype.isObject())
            return false;
        object = asObject(prototype);
    }
    return false;
}

ALWAYS_INLINE bool JSObject::getPropertySlot(JSGlobalData& vm, ExecState* exec, uint64_t propertyName, PropertySlot& slot)
{
    if (LIKELY(propertyName <= MAX_ARRAY_INDEX))
        return getPropertySlot(vm, exec, static_cast<uint32_t>(propertyName), slot);
    return getPropertySlot(vm, exec, Identifier::from(vm, propertyName), slot);
}

// Section 7.3.17 of the spec.
template <typename AddFunction> // Add function should have a type like: (JSValue, RuntimeType) -> bool
void createListFromArrayLike(ExecState* exec, JSValue arrayLikeValue, RuntimeTypeMask legalTypesFilter, const String& notAnObjectErroMessage, const String& illegalTypeErrorMessage, AddFunction addFunction)
{
    VM& vm = exec->vm();

    if (!arrayLikeValue.isObject()) {
        throwTypeError(exec, vm, notAnObjectErroMessage);
        return;
    }

    JSValue lengthProperty = arrayLikeValue.get(vm, exec, vm.propertyNames->length);
    RETURN_IF_EXCEPTION(vm, void());
    double lengthAsDouble = lengthProperty.toLength(exec);
    RETURN_IF_EXCEPTION(vm, void());
    RELEASE_ASSERT(lengthAsDouble >= 0.0 && lengthAsDouble == std::trunc(lengthAsDouble));
    uint64_t length = static_cast<uint64_t>(lengthAsDouble);
    for (uint64_t index = 0; index < length; index++) {
        JSValue  next = arrayLikeValue.get(vm, exec, index);
        RETURN_IF_EXCEPTION(vm, void());

        RuntimeType type = runtimeTypeForValue(vm, next);
        if (!(type & legalTypesFilter)) {
            throwTypeError(exec, vm, illegalTypeErrorMessage);
            return;
        }

        bool exitEarly = addFunction(next, type);
        if (exitEarly)
            return;
    }
}

ALWAYS_INLINE bool JSObject::canPerformFastPutInlineExcludingProto(JSGlobalData& vm)
{
    // Check if there are any setters or getters in the prototype chain
    JSValue prototype;
    JSObject* obj = this;
    while (true) {
        Structure* structure = obj->structure(vm);
        if (structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || structure->typeInfo().overridesGetPrototype())
            return false;
        if (obj != this && structure->typeInfo().overridesPut())
            return false;

        prototype = obj->getPrototypeDirect();
        if (prototype.isNull())
            return true;

        obj = asObject(prototype);
    }

    ASSERT_NOT_REACHED();
}

ALWAYS_INLINE bool JSObject::canPerformFastPutInline(VM& vm, const PropertyName& propertyName)
{
    if (UNLIKELY(propertyName == vm.propertyNames->underscoreProto))
        return false;
    return canPerformFastPutInlineExcludingProto(vm);
}

ALWAYS_INLINE bool JSObject::getOwnPropertySlotInline(JSGlobalData& vm, ExecState* exec, const PropertyName& propertyName, PropertySlot& slot)
{
    if (UNLIKELY(TypeInfo::overridesGetOwnPropertySlot(structure()->typeInfo().inlineTypeFlags())))
        return methodTable(vm)->getOwnPropertySlot(this, exec, propertyName, slot);
    return JSObject::getOwnPropertySlot(this, exec, propertyName, slot);
}

// https://tc39.es/ecma262/#sec-ordinaryset
ALWAYS_INLINE bool JSObject::putInlineForJSObject(JSCell* cell, ExecState* exec, VM& vm, const PropertyName& propertyName, JSValue value, PutPropertySlot& slot)
{
    JSObject* thisObject = jsCast<JSObject*>(cell);
    ASSERT(value);
    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));

    // Try indexed put first. This is required for correctness, since loads on property names that appear like
    // valid indices will never look in the named property storage.
    if (Optional<uint32_t> index = parseIndex(propertyName)) {
        if (UNLIKELY(isThisValueAltered(slot, thisObject)))
            return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
        return thisObject->methodTable(vm)->putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode());
    }

    if (!thisObject->canPerformFastPutInline(vm, propertyName))
        return thisObject->putInlineSlow(exec, propertyName, value, slot);
    if (UNLIKELY(isThisValueAltered(slot, thisObject)))
        return definePropertyOnReceiver(exec, propertyName, value, slot);
    if (UNLIKELY(thisObject->hasNonReifiedStaticProperties(vm)))
        return thisObject->putInlineFastReplacingStaticPropertyIfNeeded(exec, propertyName, value, slot);
    return thisObject->putInlineFast(exec, propertyName, value, slot);
}

ALWAYS_INLINE bool JSObject::putInlineFast(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
    VM& vm = exec->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    auto error = putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot);
    if (!error.isNull())
        return typeError(exec, vm, slot.isStrictMode(), error);
    return true;
}

// https://tc39.es/ecma262/#sec-createdataproperty
ALWAYS_INLINE bool JSObject::createDataProperty(ExecState* exec, PropertyName propertyName, JSValue value, bool shouldThrow)
{
    PropertyDescriptor descriptor(value, static_cast<unsigned>(PropertyAttribute::None));
    return methodTable()->defineOwnProperty(this, exec, propertyName, descriptor, shouldThrow);
}

ALWAYS_INLINE PropertyOffset JSObject::prepareToPutDirectWithoutTransition(VM& vm, DeferGC& deferGC, PropertyName propertyName, unsigned attributes)
{
    Butterfly* newButterfly = m_butterfly;
    Structure* structure = this->structure();
    if(UNLIKELY(structure->putWillGrowOutOfLineStorage()))
        newButterfly = allocateMoreOutOfLineStorage(vm, deferGC, structure->outOfLineCapacity(), structure->suggestedNewOutOfLineStorageCapacity());
    structure = this->structure();
    PropertyOffset offset = structure->addPropertyWithoutTransition(vm, propertyName, attributes);
    setButterfly(vm, newButterfly);
    return offset;
}

ALWAYS_INLINE bool JSObject::mayInterceptIndexedAccesses() const
{
    return structure()->mayInterceptIndexedAccesses();
}

ALWAYS_INLINE bool JSObject::mayInterceptIndexedAccesses(VM& vm) const
{
    return structure(vm)->mayInterceptIndexedAccesses();
}

// https://tc39.es/ecma262/#sec-hasproperty
ALWAYS_INLINE bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
{
    PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty);
    return const_cast<JSObject*>(this)->getPropertySlot(exec->vm(), exec, propertyName, slot);
}

ALWAYS_INLINE bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
{
    PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty);
    return const_cast<JSObject*>(this)->getPropertySlot(exec->vm(), exec, propertyName, slot);
}

ALWAYS_INLINE bool JSObject::hasProperty(ExecState* exec, uint64_t propertyName) const
{
    if (LIKELY(propertyName <= MAX_ARRAY_INDEX))
        return hasProperty(exec, static_cast<uint32_t>(propertyName));
    ASSERT(propertyName <= maxSafeInteger());
    return hasProperty(exec, Identifier::from(exec->vm(), propertyName));
}

ALWAYS_INLINE bool JSObject::hasEnumerableProperty(JSGlobalData& vm, ExecState* exec, PropertyName propertyName) const
{
    auto scope = DECLARE_THROW_SCOPE(vm);
    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
    bool hasProperty = const_cast<JSObject*>(this)->getPropertySlot(vm, exec, propertyName, slot);
    RETURN_IF_EXCEPTION(scope, false);
    if (!hasProperty)
        return false;
    return !(slot.attributes() & DontEnum) || (slot.slotBase() && slot.slotBase()->structure(vm)->typeInfo().getOwnPropertySlotMayBeWrongAboutDontEnum());
}

ALWAYS_INLINE bool JSObject::hasEnumerableProperty(JSGlobalData& vm, ExecState* exec, unsigned propertyName) const
{
    auto scope = DECLARE_THROW_SCOPE(vm);
    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
    bool hasProperty = const_cast<JSObject*>(this)->getPropertySlot(vm, exec, propertyName, slot);
    RETURN_IF_EXCEPTION(scope, false);
    if (!hasProperty)
        return false;
    return !(slot.attributes() & DontEnum) || (slot.slotBase() && slot.slotBase()->structure(vm)->typeInfo().getOwnPropertySlotMayBeWrongAboutDontEnum());
}

inline bool JSObject::canGetIndexQuicklyForTypedArray(unsigned i) const
{
    switch (structure()->typeInfo().type()) {
#define CASE_TYPED_ARRAY_TYPE(name) \
    case name ## ArrayType :\
        return jsCast<const JS ## name ## Array *>(this)->canGetIndexQuickly(i);
        FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(CASE_TYPED_ARRAY_TYPE)
#undef CASE_TYPED_ARRAY_TYPE
    default:
        return false;
    }
}

inline bool JSObject::canSetIndexQuicklyForTypedArray(unsigned i, JSValue value) const
{
    switch (structure()->typeInfo().type()) {
#define CASE_TYPED_ARRAY_TYPE(name) \
    case name ## ArrayType :\
        return jsCast<const JS ## name ## Array *>(this)->canSetIndexQuickly(i, value);
        FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(CASE_TYPED_ARRAY_TYPE)
#undef CASE_TYPED_ARRAY_TYPE
    default:
        return false;
    }
}

inline JSValue JSObject::getIndexQuicklyForTypedArray(unsigned i) const
{
    switch (structure()->typeInfo().type()) {
#define CASE_TYPED_ARRAY_TYPE(name) \
    case name ## ArrayType : {\
        auto* typedArray = jsCast<const JS ## name ## Array *>(this);\
        RELEASE_ASSERT(typedArray->canGetIndexQuickly(i));\
        return typedArray->getIndexQuickly(i);\
    }
        FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(CASE_TYPED_ARRAY_TYPE)
#undef CASE_TYPED_ARRAY_TYPE
    default:
        RELEASE_ASSERT_NOT_REACHED();
        return JSValue();
    }
}

inline void JSObject::setIndexQuicklyForTypedArray(unsigned i, JSValue value)
{
    switch (structure()->typeInfo().type()) {
#define CASE_TYPED_ARRAY_TYPE(name) \
    case name ## ArrayType : {\
        auto* typedArray = jsCast<JS ## name ## Array *>(this);\
        RELEASE_ASSERT(typedArray->canSetIndexQuickly(i, value));\
        typedArray->setIndexQuickly(i, value);\
        break;\
    }
        FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(CASE_TYPED_ARRAY_TYPE)
#undef CASE_TYPED_ARRAY_TYPE
    default:
        RELEASE_ASSERT_NOT_REACHED();
        return;
    }
}

// HasOwnProperty(O, P) from section 7.3.11 in the spec.
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasownproperty
ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
{
	ASSERT(slot.internalMethodType() == PropertySlot::InternalMethodType::GetOwnProperty);
    const MethodTable* methodTable = const_cast<JSObject*>(this)->methodTable();
    if (LIKELY(methodTable->getOwnPropertySlot == JSObject::getOwnPropertySlot))
        return JSObject::getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
    return methodTable->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
}

ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
{
    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
    return hasOwnProperty(exec, propertyName, slot);
}

ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const
{
    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
    return const_cast<JSObject*>(this)->methodTable()->getOwnPropertySlotByIndex(const_cast<JSObject*>(this), exec, propertyName, slot);
}

template<JSObject::PutMode mode>
ALWAYS_INLINE ASCIILiteral JSObject::putDirectInternal(JSGlobalData& vm, const PropertyName& propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot)
{
    ASSERT(value);
    ASSERT(value.isGetterSetter() == !!(attributes & Accessor));
    ASSERT(value.isCustomGetterSetter() == !!(attributes & PropertyAttribute::CustomAccessorOrValue));
	ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
    ASSERT(!parseIndex(propertyName));

    Structure* structure = this->structure(vm);
    if (structure->isDictionary()) {
        unsigned currentAttributes;
        PropertyOffset offset = structure->get(vm, propertyName, currentAttributes);
        if (offset != invalidOffset) {
            if (mode == PutModePut && (currentAttributes & ReadOnlyOrAccessorOrCustomAccessor))
                return ReadonlyPropertyChangeError;

            putDirect(vm, offset, value);
            structure->didReplaceProperty(vm, offset);
            // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition.
			// https://bugs.webkit.org/show_bug.cgi?id=214342
			if (mode == PutModeDefineOwnProperty && (attributes != currentAttributes || (attributes & AccessorOrCustomAccessorOrValue))) {
				DeferredStructureTransitionWatchpointFire deferred(vm, structure);
                setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes, &deferred));
			} else {
				ASSERT(!(currentAttributes & PropertyAttribute::AccessorOrCustomAccessorOrValue));
				slot.setExistingProperty(this, offset);
			}

            return ASCIILiteral::null();
        }

        if (mode == PutModePut && !isStructureExtensible())
            return NonExtensibleObjectPropertyDefineError;

        DeferGC deferGC(vm.heap);
        Butterfly* newButterfly = m_butterfly;
        if (UNLIKELY(structure->putWillGrowOutOfLineStorage()))
            newButterfly = allocateMoreOutOfLineStorage(vm, deferGC, structure->outOfLineCapacity(), structure->suggestedNewOutOfLineStorageCapacity());

        offset = structure->addPropertyWithoutTransition(vm, propertyName, attributes);
        setButterfly(vm, newButterfly);

        validateOffset(offset);
        ASSERT(structure()->isValidOffset(offset));
        putDirect(vm, offset, value);
        slot.setNewProperty(this, offset);
        if (attributes & ReadOnly)
            structure->setContainsReadOnlyProperties();
        return ASCIILiteral::null();
    }

    PropertyOffset offset;
    size_t currentCapacity = structure->outOfLineCapacity();
    if (Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName.uid(), attributes, offset)) {
        DeferGC deferGC(vm.heap);
    	Butterfly* newButterfly = m_butterfly;
		if (currentCapacity != newStructure->outOfLineCapacity()) {
			newButterfly = allocateMoreOutOfLineStorage(vm, deferGC, currentCapacity, newStructure->outOfLineCapacity());
			WTF::storeStoreFence();
			setButterfly(vm, newButterfly);
			WTF::storeStoreFence();
		}

		validateOffset(offset);
		ASSERT(newStructure->isValidOffset(offset));
        putDirect(vm, offset, value);
        setStructure(vm, newStructure);
        slot.setNewProperty(this, offset);
        return ASCIILiteral::null();
    }

    unsigned currentAttributes;
    offset = structure->get(vm, propertyName, currentAttributes);
    if (offset != invalidOffset) {
        if (mode == PutModePut && (currentAttributes & ReadOnlyOrAccessorOrCustomAccessor))
            return ReadonlyPropertyChangeError;

        structure->didReplaceProperty(vm, offset);
        putDirect(vm, offset, value);

        // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition.
        // https://bugs.webkit.org/show_bug.cgi?id=214342
        if (mode == PutModeDefineOwnProperty && (attributes != currentAttributes || (attributes & AccessorOrCustomAccessorOrValue))) {
        	// We want the structure transition watchpoint to fire after this object has switched structure.
            // This allows adaptive watchpoints to observe if the new structure is the one we want.
            DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
            setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes, &deferredWatchpointFire));
    	} else {
			ASSERT(!(currentAttributes & PropertyAttribute::AccessorOrCustomAccessorOrValue));
			slot.setExistingProperty(this, offset);
		}

        return ASCIILiteral::null();
    }

    if (mode == PutModePut && !isStructureExtensible())
        return NonExtensibleObjectPropertyDefineError;

    // We want the structure transition watchpoint to fire after this object has switched
    // structure. This allows adaptive watchpoints to observe if the new structure is the one
    // we want.
    DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, this->structure());

    Structure* newStructure = Structure::addNewPropertyTransition(vm, this->structure(), propertyName, attributes, offset, slot.context(), &deferredWatchpointFire);

    validateOffset(offset);
    ASSERT(structure->isValidOffset(offset));
    DeferGC deferGC(vm.heap);
    size_t oldCapacity = structure->outOfLineCapacity();
    size_t newCapacity = newStructure->outOfLineCapacity();
    ASSERT(oldCapacity <= newCapacity);
    if (oldCapacity != newCapacity) {
        Butterfly* newButterfly = allocateMoreOutOfLineStorage(vm, deferGC, oldCapacity, newCapacity);
        WTF::storeStoreFence();
        setButterfly(vm, newButterfly);
        WTF::storeStoreFence();
    }
    putDirect(vm, offset, value);
    setStructure(vm, newStructure);
    slot.setNewProperty(this, offset);
    if (attributes & ReadOnly)
    	newStructure->setContainsReadOnlyProperties();
    return ASCIILiteral::null();
}

inline bool JSObject::deleteProperty(ExecState* exec, PropertyName propertyName)
{
    return this->methodTable()->deleteProperty(this, exec, propertyName);
}

inline bool JSObject::deleteProperty(ExecState* exec, uint32_t propertyName)
{
    return this->methodTable()->deletePropertyByIndex(this, exec, propertyName);
}

inline bool JSObject::deleteProperty(ExecState* exec, uint64_t propertyName)
{
    if (LIKELY(propertyName <= MAX_ARRAY_INDEX))
        return deleteProperty(exec, static_cast<uint32_t>(propertyName));
    ASSERT(propertyName <= maxSafeInteger());
    return deleteProperty(exec, Identifier::from(exec->vm(), propertyName));
}

inline JSValue JSObject::get(VM& vm, ExecState* exec, uint64_t propertyName) const
{
    if (LIKELY(propertyName <= MAX_ARRAY_INDEX))
        return get(vm, exec, static_cast<uint32_t>(propertyName));
    ASSERT(propertyName <= maxSafeInteger());
    return get(vm, exec, Identifier::from(vm, propertyName));
}

ALWAYS_INLINE void JSObject::getNonReifiedStaticPropertyNames(VM& vm, PropertyNameArray& propertyNames, DontEnumPropertiesMode mode)
{
    if (staticPropertiesReified())
        return;

    // Add properties from the static hashtables of properties
    for (const auto* info = classInfo(); info; info = info->parentClass) {
        const auto* table = info->staticPropHashTable;
        if (!table)
            continue;

        for (auto iter = table->begin(); iter != table->end(); ++iter) {
			if (mode == DontEnumPropertiesMode::Include || !(iter->attributes() & DontEnum))
				propertyNames.add(Identifier::fromString(vm, iter.key()));
		}
    }
}

JSObject* createInvalidPrivateNameError(ExecState*);
JSObject* createRedefinedPrivateNameError(ExecState*);
JSObject* createReinstallPrivateMethodError(ExecState*);
JSObject* createPrivateMethodAccessError(ExecState*);

ALWAYS_INLINE bool JSObject::getPrivateFieldSlot(JSObject* object, JSGlobalData& vm, PropertyName propertyName, PropertySlot& slot)
{
    ASSERT(propertyName.isPrivateName());
    Structure* structure = object->structure(vm);

    unsigned attributes;
    PropertyOffset offset = structure->get(vm, propertyName, attributes);
    if (offset == invalidOffset)
        return false;

    JSValue value = object->getDirect(offset);
    ASSERT(value);
    if (value.isCell()) {
        JSCell* cell = value.asCell();
        JSType type = cell->structure()->typeInfo().type();
        UNUSED_PARAM(cell);
        ASSERT_UNUSED(type, type != GetterSetterType && type != CustomGetterSetterType);
        // FIXME: For now, private fields do not support getter/setter fields. Later on, we will need to fill in accessor metadata here,
        // as in JSObject::getOwnNonIndexPropertySlot()
        // https://bugs.webkit.org/show_bug.cgi?id=194435
    }

    slot.setValue(object, attributes, value, offset);
    return true;
}

inline bool JSObject::hasPrivateField(JSGlobalData& vm, ExecState* exec, PropertyName propertyName)
{
    ASSERT(propertyName.isPrivateName());
    unsigned attributes;
    return structure(vm)->get(vm, propertyName, attributes) != invalidOffset;
}

inline bool JSObject::getPrivateField(JSGlobalData& vm, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
{
    auto scope = DECLARE_THROW_SCOPE(vm);
    if (!JSObject::getPrivateFieldSlot(this, vm, propertyName, slot)) {
        throwException(exec, scope, createInvalidPrivateNameError(exec));
        RELEASE_AND_RETURN(scope, false);
    }
    EXCEPTION_ASSERT(!scope.exception());
    RELEASE_AND_RETURN(scope, true);
}

inline void JSObject::setPrivateField(JSGlobalData& vm, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& putSlot)
{
    auto scope = DECLARE_THROW_SCOPE(vm);
    PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty);
    if (!JSObject::getPrivateFieldSlot(this, vm, propertyName, slot)) {
        throwException(exec, scope, createInvalidPrivateNameError(exec));
        RELEASE_AND_RETURN(scope, void());
    }
    EXCEPTION_ASSERT(!scope.exception());

    scope.release();
    putDirect(vm, propertyName, value, putSlot);
}

inline void JSObject::definePrivateField(JSGlobalData& vm, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& putSlot)
{
    auto scope = DECLARE_THROW_SCOPE(vm);
    PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty);
    if (JSObject::getPrivateFieldSlot(this, vm, propertyName, slot)) {
        throwException(exec, scope, createRedefinedPrivateNameError(exec));
        RELEASE_AND_RETURN(scope, void());
    }
    EXCEPTION_ASSERT(!scope.exception());

    scope.release();
    putDirect(vm, propertyName, value, putSlot);
}

inline bool JSObject::hasPrivateBrand(JSGlobalData& vm, ExecState* exec, JSValue brand)
{
    ASSERT(brand.isSymbol() && asSymbol(brand)->uid().isPrivate());
    Structure* structure = this->structure(vm);
    return structure->isBrandedStructure() && jsCast<BrandedStructure*>(structure)->checkBrand(asSymbol(brand));
}

inline void JSObject::checkPrivateBrand(JSGlobalData& vm, ExecState* exec, JSValue brand)
{
    ASSERT(brand.isSymbol() && asSymbol(brand)->uid().isPrivate());
    auto scope = DECLARE_THROW_SCOPE(vm);

    Structure* structure = this->structure(vm);
    if (!structure->isBrandedStructure() || !jsCast<BrandedStructure*>(structure)->checkBrand(asSymbol(brand)))
        throwException(exec, scope, createPrivateMethodAccessError(exec));
}

inline void JSObject::setPrivateBrand(JSGlobalData& vm, ExecState* exec, JSValue brand)
{
    ASSERT(brand.isSymbol() && asSymbol(brand)->uid().isPrivate());
    auto scope = DECLARE_THROW_SCOPE(vm);

    Structure* structure = this->structure(vm);
    if (structure->isBrandedStructure() && jsCast<BrandedStructure*>(structure)->checkBrand(asSymbol(brand))) {
        throwException(exec, scope, createReinstallPrivateMethodError(exec));
        RELEASE_AND_RETURN(scope, void());
    }
    EXCEPTION_ASSERT(!scope.exception());

    scope.release();

    DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);

    Structure* newStructure = Structure::setBrandTransition(vm, structure, asSymbol(brand), &deferredWatchpointFire);
    ASSERT(newStructure->isBrandedStructure());
    ASSERT(newStructure->outOfLineCapacity() || !this->structure(vm)->outOfLineCapacity());
    this->setStructure(vm, newStructure);
}

} // namespace JSC
