diff options
Diffstat (limited to 'js/src/vm/PropertyInfo.h')
-rw-r--r-- | js/src/vm/PropertyInfo.h | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/js/src/vm/PropertyInfo.h b/js/src/vm/PropertyInfo.h new file mode 100644 index 0000000000..497c2a242a --- /dev/null +++ b/js/src/vm/PropertyInfo.h @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_PropertyInfo_h +#define vm_PropertyInfo_h + +#include "mozilla/Assertions.h" + +#include <limits> +#include <stdint.h> + +#include "jstypes.h" +#include "NamespaceImports.h" + +#include "js/GCVector.h" +#include "js/PropertyDescriptor.h" +#include "util/EnumFlags.h" + +namespace js { + +/* Limit on the number of slotful properties in an object. */ +static constexpr uint32_t SHAPE_INVALID_SLOT = Bit(24) - 1; +static constexpr uint32_t SHAPE_MAXIMUM_SLOT = Bit(24) - 2; + +// Flags associated with each property stored in the shape tree. +enum class PropertyFlag : uint8_t { + // Property attributes. See also JS::PropertyAttribute. + Configurable = 1 << 0, + Enumerable = 1 << 1, + Writable = 1 << 2, + + // Whether this is an accessor property. Accessor properties have a slot that + // stores a GetterSetter instance. + AccessorProperty = 1 << 3, + + // If set, this is a custom data property. The property is exposed as a data + // property to JS code and PropertyDescriptor, but instead of an object slot + // it uses custom get/set logic. + // + // This is used to implement the special array.length and ArgumentsObject + // properties. + // + // This flag is deprecated (we don't want to add more uses). + CustomDataProperty = 1 << 4, +}; + +class PropertyFlags : public EnumFlags<PropertyFlag> { + using Base = EnumFlags<PropertyFlag>; + using Base::Base; + + public: + static const PropertyFlags defaultDataPropFlags; + + static PropertyFlags fromRaw(uint8_t flags) { return PropertyFlags(flags); } + + bool configurable() const { return hasFlag(PropertyFlag::Configurable); } + bool enumerable() const { return hasFlag(PropertyFlag::Enumerable); } + bool writable() const { + MOZ_ASSERT(isDataDescriptor()); + return hasFlag(PropertyFlag::Writable); + } + + // Note: this returns true only for plain data properties with a slot. Returns + // false for custom data properties. See CustomDataProperty flag. + bool isDataProperty() const { + return !isAccessorProperty() && !isCustomDataProperty(); + } + bool isAccessorProperty() const { + return hasFlag(PropertyFlag::AccessorProperty); + } + bool isCustomDataProperty() const { + return hasFlag(PropertyFlag::CustomDataProperty); + } + + // Note: unlike isDataProperty, this returns true also for custom data + // properties. + bool isDataDescriptor() const { return !isAccessorProperty(); } +}; + +constexpr PropertyFlags PropertyFlags::defaultDataPropFlags = { + PropertyFlag::Configurable, PropertyFlag::Enumerable, + PropertyFlag::Writable}; + +// PropertyInfo contains information (PropertyFlags, slot number) for a +// property stored in the Shape tree. Property lookups on NativeObjects return a +// PropertyInfo. +// +// There's also a CompactPropertyInfo type that's used by CompactPropMap to +// store small slot numbers (CompactPropertyInfo is two bytes instead of four). +template <typename T> +class PropertyInfoBase { + static_assert(std::is_same_v<T, uint32_t> || std::is_same_v<T, uint16_t>); + + static constexpr uint32_t FlagsMask = 0xff; + static constexpr uint32_t SlotShift = 8; + + T slotAndFlags_ = 0; + + static_assert(SHAPE_INVALID_SLOT <= (UINT32_MAX >> SlotShift), + "SHAPE_INVALID_SLOT must fit in slotAndFlags_"); + static_assert(SHAPE_MAXIMUM_SLOT <= (UINT32_MAX >> SlotShift), + "SHAPE_MAXIMUM_SLOT must fit in slotAndFlags_"); + + // Constructor is private, code should prefer Maybe<PropertyInfo>. This + // constructor is only used for the propInfos array in property maps + // (CompactPropMap and LinkedPropMap are friend classes for this reason). + PropertyInfoBase() = default; + + template <typename U> + friend class PropertyInfoBase; + friend class CompactPropMap; + friend class LinkedPropMap; + + public: + static constexpr size_t MaxSlotNumber = + std::numeric_limits<T>::max() >> SlotShift; + + PropertyInfoBase(PropertyFlags flags, uint32_t slot) + : slotAndFlags_((slot << SlotShift) | flags.toRaw()) { + MOZ_ASSERT(maybeSlot() == slot); + MOZ_ASSERT(this->flags() == flags); + } + + template <typename U> + explicit PropertyInfoBase(PropertyInfoBase<U> other) + : slotAndFlags_(other.slotAndFlags_) { + // Assert assigning PropertyInfo to CompactPropertyInfo doesn't lose + // information. + MOZ_ASSERT(slotAndFlags_ == other.slotAndFlags_); + } + + bool isDataProperty() const { return flags().isDataProperty(); } + bool isCustomDataProperty() const { return flags().isCustomDataProperty(); } + bool isAccessorProperty() const { return flags().isAccessorProperty(); } + bool isDataDescriptor() const { return flags().isDataDescriptor(); } + + bool hasSlot() const { return !isCustomDataProperty(); } + + uint32_t slot() const { + MOZ_ASSERT(hasSlot()); + MOZ_ASSERT(maybeSlot() < SHAPE_INVALID_SLOT); + return maybeSlot(); + } + + uint32_t maybeSlot() const { return slotAndFlags_ >> SlotShift; } + + PropertyFlags flags() const { + return PropertyFlags::fromRaw(slotAndFlags_ & FlagsMask); + } + bool writable() const { return flags().writable(); } + bool configurable() const { return flags().configurable(); } + bool enumerable() const { return flags().enumerable(); } + + JS::PropertyAttributes propAttributes() const { + JS::PropertyAttributes attrs{}; + if (configurable()) { + attrs += JS::PropertyAttribute::Configurable; + } + if (enumerable()) { + attrs += JS::PropertyAttribute::Enumerable; + } + if (isDataDescriptor() && writable()) { + attrs += JS::PropertyAttribute::Writable; + } + return attrs; + } + + T toRaw() const { return slotAndFlags_; } + + bool operator==(const PropertyInfoBase<T>& other) const { + return slotAndFlags_ == other.slotAndFlags_; + } + bool operator!=(const PropertyInfoBase<T>& other) const { + return !operator==(other); + } +}; + +using PropertyInfo = PropertyInfoBase<uint32_t>; +using CompactPropertyInfo = PropertyInfoBase<uint16_t>; + +static_assert(sizeof(PropertyInfo) == sizeof(uint32_t)); +static_assert(sizeof(CompactPropertyInfo) == sizeof(uint16_t)); + +class PropertyInfoWithKey : public PropertyInfo { + PropertyKey key_; + + public: + PropertyInfoWithKey(PropertyFlags flags, uint32_t slot, PropertyKey key) + : PropertyInfo(flags, slot), key_(key) {} + + PropertyInfoWithKey(PropertyInfo prop, PropertyKey key) + : PropertyInfo(prop), key_(key) {} + + PropertyKey key() const { return key_; } + + void trace(JSTracer* trc) { + TraceRoot(trc, &key_, "PropertyInfoWithKey-key"); + } +}; + +template <class Wrapper> +class WrappedPtrOperations<PropertyInfoWithKey, Wrapper> { + const PropertyInfoWithKey& value() const { + return static_cast<const Wrapper*>(this)->get(); + } + + public: + bool isDataProperty() const { return value().isDataProperty(); } + uint32_t slot() const { return value().slot(); } + PropertyKey key() const { return value().key(); } + PropertyFlags flags() const { return value().flags(); } +}; + +using PropertyInfoWithKeyVector = GCVector<PropertyInfoWithKey, 16>; + +} // namespace js + +#endif /* vm_PropertyInfo_h */ |