|  | /* | 
|  | *  Copyright (C) 2005-2020 Apple Inc. All rights reserved. | 
|  | * | 
|  | *  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 "DOMAnnotation.h" | 
|  | #include "DisallowVMEntry.h" | 
|  | #include "GetVM.h" | 
|  | #include "JSCJSValue.h" | 
|  | #include "PropertyName.h" | 
|  | #include "PropertyOffset.h" | 
|  | #include "ScopeOffset.h" | 
|  | #include <wtf/Assertions.h> | 
|  | #include <wtf/ForbidHeapAllocation.h> | 
|  |  | 
|  | namespace JSC { | 
|  | class GetterSetter; | 
|  | class JSObject; | 
|  | class JSModuleEnvironment; | 
|  |  | 
|  | // ECMA 262-3 8.6.1 | 
|  | // Property attributes | 
|  | enum class PropertyAttribute : unsigned { | 
|  | None              = 0, | 
|  | ReadOnly          = 1 << 1,  // property can be only read, not written | 
|  | DontEnum          = 1 << 2,  // property doesn't appear in (for .. in ..) | 
|  | DontDelete        = 1 << 3,  // property can't be deleted | 
|  | Accessor          = 1 << 4,  // property is a getter/setter | 
|  | CustomAccessor    = 1 << 5, | 
|  | CustomValue       = 1 << 6, | 
|  | CustomAccessorOrValue = CustomAccessor | CustomValue, | 
|  | AccessorOrCustomAccessorOrValue = Accessor | CustomAccessor | CustomValue, | 
|  | ReadOnlyOrAccessorOrCustomAccessor = ReadOnly | Accessor | CustomAccessor, | 
|  |  | 
|  | // Things that are used by static hashtables are not in the attributes byte in PropertyMapEntry. | 
|  | Function          = 1 << 8,  // property is a function - only used by static hashtables | 
|  | Builtin           = 1 << 9,  // property is a builtin function - only used by static hashtables | 
|  | ConstantInteger   = 1 << 10, // property is a constant integer - only used by static hashtables | 
|  | CellProperty      = 1 << 11, // property is a lazy property - only used by static hashtables | 
|  | ClassStructure    = 1 << 12, // property is a lazy class structure - only used by static hashtables | 
|  | PropertyCallback  = 1 << 13, // property that is a lazy property callback - only used by static hashtables | 
|  | DOMAttribute      = 1 << 14, // property is a simple DOM attribute - only used by static hashtables | 
|  | DOMJITAttribute   = 1 << 15, // property is a DOM JIT attribute - only used by static hashtables | 
|  | DOMJITFunction    = 1 << 16, // property is a DOM JIT function - only used by static hashtables | 
|  | BuiltinOrFunction = Builtin | Function, // helper only used by static hashtables | 
|  | BuiltinOrFunctionOrLazyProperty = Builtin | Function | CellProperty | ClassStructure | PropertyCallback, // helper only used by static hashtables | 
|  | BuiltinOrFunctionOrAccessorOrLazyProperty = Builtin | Function | Accessor | CellProperty | ClassStructure | PropertyCallback, // helper only used by static hashtables | 
|  | BuiltinOrFunctionOrAccessorOrLazyPropertyOrConstant = Builtin | Function | Accessor | CellProperty | ClassStructure | PropertyCallback | ConstantInteger // helper only used by static hashtables | 
|  | }; | 
|  |  | 
|  | static constexpr unsigned operator| (PropertyAttribute a, PropertyAttribute b) { return static_cast<unsigned>(a) | static_cast<unsigned>(b); } | 
|  | static constexpr unsigned operator| (unsigned a, PropertyAttribute b) { return a | static_cast<unsigned>(b); } | 
|  | static constexpr unsigned operator| (PropertyAttribute a, unsigned b) { return static_cast<unsigned>(a) | b; } | 
|  | static constexpr unsigned operator&(unsigned a, PropertyAttribute b) { return a & static_cast<unsigned>(b); } | 
|  | static constexpr bool operator<(PropertyAttribute a, PropertyAttribute b) { return static_cast<unsigned>(a) < static_cast<unsigned>(b); } | 
|  | static constexpr unsigned operator~(PropertyAttribute a) { return ~static_cast<unsigned>(a); } | 
|  | static constexpr bool operator<(PropertyAttribute a, unsigned b) { return static_cast<unsigned>(a) < b; } | 
|  | static inline unsigned& operator|=(unsigned& a, PropertyAttribute b) { return a |= static_cast<unsigned>(b); } | 
|  |  | 
|  | enum CacheabilityType : uint8_t { | 
|  | CachingDisallowed, | 
|  | CachingAllowed | 
|  | }; | 
|  |  | 
|  | inline unsigned attributesForStructure(unsigned attributes) | 
|  | { | 
|  | // The attributes that are used just for the static hashtable are at bit 8 and higher. | 
|  | return static_cast<uint8_t>(attributes); | 
|  | } | 
|  |  | 
|  | using GetValueFunc = EncodedJSValue(JIT_OPERATION_ATTRIBUTES*)(JSGlobalObject*, EncodedJSValue thisValue, PropertyName); | 
|  | using GetValueFuncWithPtr = EncodedJSValue(JIT_OPERATION_ATTRIBUTES*)(JSGlobalObject*, EncodedJSValue thisValue, PropertyName, void*); | 
|  |  | 
|  | using PutValueFunc = bool (JIT_OPERATION_ATTRIBUTES*)(JSGlobalObject*, EncodedJSValue baseObject, EncodedJSValue value, PropertyName); | 
|  | using PutValueFuncWithPtr = bool (JIT_OPERATION_ATTRIBUTES*)(JSGlobalObject*, EncodedJSValue baseObject, EncodedJSValue value, PropertyName, void*); | 
|  |  | 
|  | class PropertySlot { | 
|  |  | 
|  | // We rely on PropertySlot being stack allocated when used. This is needed | 
|  | // because we rely on some of its fields being a GC root. For example, it | 
|  | // may be the only thing that points to the GetterSetter property it has. | 
|  | WTF_FORBID_HEAP_ALLOCATION; | 
|  |  | 
|  | enum PropertyType : uint8_t { | 
|  | TypeUnset, | 
|  | TypeValue, | 
|  | TypeGetter, | 
|  | TypeCustom, | 
|  | }; | 
|  |  | 
|  | public: | 
|  | enum class InternalMethodType : uint8_t { | 
|  | Get, // [[Get]] internal method in the spec. | 
|  | HasProperty, // [[HasProperty]] internal method in the spec. | 
|  | GetOwnProperty, // [[GetOwnProperty]] internal method in the spec. | 
|  | VMInquiry, // Our VM is just poking around. When this is the InternalMethodType, getOwnPropertySlot is not allowed to do user observable actions. | 
|  | }; | 
|  |  | 
|  | enum class AdditionalDataType : uint8_t { | 
|  | None, | 
|  | DOMAttribute, // Annotated with DOMAttribute information. | 
|  | ModuleNamespace, // ModuleNamespaceObject's environment access. | 
|  | }; | 
|  |  | 
|  | explicit PropertySlot(const JSValue thisValue, InternalMethodType internalMethodType, VM* vmForInquiry = nullptr) | 
|  | : m_thisValue(thisValue) | 
|  | , m_internalMethodType(internalMethodType) | 
|  | { | 
|  | if (isVMInquiry()) | 
|  | disallowVMEntry.emplace(*vmForInquiry); | 
|  | } | 
|  |  | 
|  | using GetValueFunc = JSC::GetValueFunc; | 
|  | using GetValueFuncWithPtr = JSC::GetValueFuncWithPtr; | 
|  |  | 
|  | JSValue getValue(JSGlobalObject*, PropertyName) const; | 
|  | JSValue getValue(JSGlobalObject*, uint64_t propertyName) const; | 
|  | JSValue getPureResult() const; | 
|  |  | 
|  | bool isCacheable() const { return isUnset() || m_cacheability == CachingAllowed; } | 
|  | bool isUnset() const { return m_propertyType == TypeUnset; } | 
|  | bool isValue() const { return m_propertyType == TypeValue; } | 
|  | bool isAccessor() const { return m_propertyType == TypeGetter; } | 
|  | bool isCustom() const { return m_propertyType == TypeCustom; } | 
|  | bool isCacheableValue() const { return isCacheable() && isValue(); } | 
|  | bool isCacheableGetter() const { return isCacheable() && isAccessor(); } | 
|  | bool isCacheableCustom() const { return isCacheable() && isCustom(); } | 
|  | void setIsTaintedByOpaqueObject() { m_isTaintedByOpaqueObject = true; } | 
|  | bool isTaintedByOpaqueObject() const { return m_isTaintedByOpaqueObject; } | 
|  |  | 
|  | InternalMethodType internalMethodType() const { return m_internalMethodType; } | 
|  | bool isVMInquiry() const { return m_internalMethodType == InternalMethodType::VMInquiry; } | 
|  |  | 
|  | void disableCaching() | 
|  | { | 
|  | m_cacheability = CachingDisallowed; | 
|  | } | 
|  |  | 
|  | unsigned attributes() const { return m_attributes; } | 
|  |  | 
|  | PropertyOffset cachedOffset() const | 
|  | { | 
|  | ASSERT(isCacheable()); | 
|  | return m_offset; | 
|  | } | 
|  |  | 
|  | GetterSetter* getterSetter() const | 
|  | { | 
|  | ASSERT(isAccessor()); | 
|  | return m_data.getter.getterSetter; | 
|  | } | 
|  |  | 
|  | GetValueFunc customGetter() const | 
|  | { | 
|  | ASSERT(isCustom()); | 
|  | return m_data.custom.getValue; | 
|  | } | 
|  |  | 
|  | PutValueFunc customSetter() const | 
|  | { | 
|  | ASSERT(isCustom()); | 
|  | return m_data.custom.putValue; | 
|  | } | 
|  |  | 
|  | JSObject* slotBase() const | 
|  | { | 
|  | return m_slotBase; | 
|  | } | 
|  |  | 
|  | WatchpointSet* watchpointSet() const | 
|  | { | 
|  | return m_watchpointSet; | 
|  | } | 
|  |  | 
|  | std::optional<DOMAttributeAnnotation> domAttribute() const | 
|  | { | 
|  | if (m_additionalDataType == AdditionalDataType::DOMAttribute) | 
|  | return m_additionalData.domAttribute; | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | struct ModuleNamespaceSlot { | 
|  | JSModuleEnvironment* environment; | 
|  | unsigned scopeOffset; | 
|  | }; | 
|  |  | 
|  | std::optional<ModuleNamespaceSlot> moduleNamespaceSlot() const | 
|  | { | 
|  | if (m_additionalDataType == AdditionalDataType::ModuleNamespace) | 
|  | return m_additionalData.moduleNamespaceSlot; | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | void setValue(JSObject* slotBase, unsigned attributes, JSValue value) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | m_data.value = JSValue::encode(value); | 
|  | m_attributes = attributes; | 
|  |  | 
|  | ASSERT(slotBase); | 
|  | m_slotBase = slotBase; | 
|  | m_propertyType = TypeValue; | 
|  |  | 
|  | ASSERT(m_cacheability == CachingDisallowed); | 
|  | } | 
|  |  | 
|  | void setValue(JSObject* slotBase, unsigned attributes, JSValue value, PropertyOffset offset) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | ASSERT(value); | 
|  | m_data.value = JSValue::encode(value); | 
|  | m_attributes = attributes; | 
|  |  | 
|  | ASSERT(slotBase); | 
|  | m_slotBase = slotBase; | 
|  | m_propertyType = TypeValue; | 
|  | m_offset = offset; | 
|  |  | 
|  | m_cacheability = CachingAllowed; | 
|  | } | 
|  |  | 
|  | void setValue(JSString*, unsigned attributes, JSValue value) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | ASSERT(value); | 
|  | m_data.value = JSValue::encode(value); | 
|  | m_attributes = attributes; | 
|  |  | 
|  | m_slotBase = nullptr; | 
|  | m_propertyType = TypeValue; | 
|  |  | 
|  | ASSERT(m_cacheability == CachingDisallowed); | 
|  | } | 
|  |  | 
|  | void setValueModuleNamespace(JSObject* slotBase, unsigned attributes, JSValue value, JSModuleEnvironment* environment, ScopeOffset scopeOffset) | 
|  | { | 
|  | setValue(slotBase, attributes, value); | 
|  | m_additionalDataType = AdditionalDataType::ModuleNamespace; | 
|  | m_additionalData.moduleNamespaceSlot.environment = environment; | 
|  | m_additionalData.moduleNamespaceSlot.scopeOffset = scopeOffset.offset(); | 
|  | } | 
|  |  | 
|  | void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, PutValueFunc putValue = nullptr) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | ASSERT(getValue); | 
|  | assertIsCFunctionPtr(getValue); | 
|  | m_data.custom.getValue = getValue; | 
|  | assertIsNullOrCFunctionPtr(putValue); | 
|  | m_data.custom.putValue = putValue; | 
|  | m_attributes = attributes; | 
|  |  | 
|  | ASSERT(slotBase); | 
|  | m_slotBase = slotBase; | 
|  | m_propertyType = TypeCustom; | 
|  | ASSERT(m_cacheability == CachingDisallowed); | 
|  | } | 
|  |  | 
|  | void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, PutValueFunc putValue, DOMAttributeAnnotation domAttribute) | 
|  | { | 
|  | setCustom(slotBase, attributes, getValue, putValue); | 
|  | m_additionalDataType = AdditionalDataType::DOMAttribute; | 
|  | m_additionalData.domAttribute = domAttribute; | 
|  | } | 
|  |  | 
|  | void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, PutValueFunc putValue = nullptr) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | ASSERT(getValue); | 
|  | assertIsCFunctionPtr(getValue); | 
|  | m_data.custom.getValue = getValue; | 
|  | assertIsNullOrCFunctionPtr(putValue); | 
|  | m_data.custom.putValue = putValue; | 
|  | m_attributes = attributes; | 
|  |  | 
|  | ASSERT(slotBase); | 
|  | m_slotBase = slotBase; | 
|  | m_propertyType = TypeCustom; | 
|  |  | 
|  | m_cacheability = CachingAllowed; | 
|  | } | 
|  |  | 
|  | void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, PutValueFunc putValue, DOMAttributeAnnotation domAttribute) | 
|  | { | 
|  | setCacheableCustom(slotBase, attributes, getValue, putValue); | 
|  | m_additionalDataType = AdditionalDataType::DOMAttribute; | 
|  | m_additionalData.domAttribute = domAttribute; | 
|  | } | 
|  |  | 
|  | void setGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | ASSERT(getterSetter); | 
|  | m_data.getter.getterSetter = getterSetter; | 
|  | m_attributes = attributes; | 
|  |  | 
|  | ASSERT(slotBase); | 
|  | m_slotBase = slotBase; | 
|  | m_propertyType = TypeGetter; | 
|  |  | 
|  | ASSERT(m_cacheability == CachingDisallowed); | 
|  | } | 
|  |  | 
|  | void setCacheableGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter, PropertyOffset offset) | 
|  | { | 
|  | ASSERT(attributes == attributesForStructure(attributes)); | 
|  |  | 
|  | ASSERT(getterSetter); | 
|  | m_data.getter.getterSetter = getterSetter; | 
|  | m_attributes = attributes; | 
|  |  | 
|  | ASSERT(slotBase); | 
|  | m_slotBase = slotBase; | 
|  | m_propertyType = TypeGetter; | 
|  | m_offset = offset; | 
|  |  | 
|  | m_cacheability = CachingAllowed; | 
|  | } | 
|  |  | 
|  | JSValue thisValue() const | 
|  | { | 
|  | return m_thisValue; | 
|  | } | 
|  |  | 
|  | void setThisValue(JSValue thisValue) | 
|  | { | 
|  | m_thisValue = thisValue; | 
|  | } | 
|  |  | 
|  | void setUndefined() | 
|  | { | 
|  | m_data.value = JSValue::encode(jsUndefined()); | 
|  | m_attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete | PropertyAttribute::DontEnum; | 
|  |  | 
|  | m_slotBase = nullptr; | 
|  | m_propertyType = TypeValue; | 
|  | } | 
|  |  | 
|  | void setWatchpointSet(WatchpointSet& set) | 
|  | { | 
|  | ASSERT(set.isStillValid()); | 
|  | m_watchpointSet = &set; | 
|  | } | 
|  |  | 
|  | private: | 
|  | JS_EXPORT_PRIVATE JSValue functionGetter(JSGlobalObject*) const; | 
|  | JS_EXPORT_PRIVATE JSValue customGetter(JSGlobalObject*, PropertyName) const; | 
|  |  | 
|  | union { | 
|  | EncodedJSValue value; | 
|  | struct { | 
|  | GetterSetter* getterSetter; | 
|  | } getter; | 
|  | struct { | 
|  | GetValueFunc getValue; | 
|  | PutValueFunc putValue; | 
|  | } custom; | 
|  | } m_data; | 
|  |  | 
|  | unsigned m_attributes { 0 }; | 
|  | PropertyOffset m_offset { invalidOffset }; | 
|  | JSValue m_thisValue; | 
|  | JSObject* m_slotBase { nullptr }; | 
|  | WatchpointSet* m_watchpointSet { nullptr }; | 
|  | CacheabilityType m_cacheability { CachingDisallowed }; | 
|  | PropertyType m_propertyType { TypeUnset }; | 
|  | InternalMethodType m_internalMethodType; | 
|  | AdditionalDataType m_additionalDataType { AdditionalDataType::None }; | 
|  | bool m_isTaintedByOpaqueObject { false }; | 
|  | public: | 
|  | std::optional<DisallowVMEntry> disallowVMEntry; | 
|  | private: | 
|  | union { | 
|  | DOMAttributeAnnotation domAttribute; | 
|  | ModuleNamespaceSlot moduleNamespaceSlot; | 
|  | } m_additionalData { { nullptr, nullptr } }; | 
|  | }; | 
|  |  | 
|  | ALWAYS_INLINE JSValue PropertySlot::getValue(JSGlobalObject* globalObject, PropertyName propertyName) const | 
|  | { | 
|  | if (m_propertyType == TypeValue) | 
|  | return JSValue::decode(m_data.value); | 
|  | if (m_propertyType == TypeGetter) | 
|  | return functionGetter(globalObject); | 
|  | return customGetter(globalObject, propertyName); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue PropertySlot::getValue(JSGlobalObject* globalObject, uint64_t propertyName) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | if (m_propertyType == TypeValue) | 
|  | return JSValue::decode(m_data.value); | 
|  | if (m_propertyType == TypeGetter) | 
|  | return functionGetter(globalObject); | 
|  | return customGetter(globalObject, Identifier::from(vm, propertyName)); | 
|  | } | 
|  |  | 
|  | } // namespace JSC |