PropertyInfo.h (7272B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_PropertyInfo_h 8 #define vm_PropertyInfo_h 9 10 #include "mozilla/Assertions.h" 11 12 #include <limits> 13 #include <stdint.h> 14 15 #include "jstypes.h" 16 #include "NamespaceImports.h" 17 18 #include "js/GCVector.h" 19 #include "js/PropertyDescriptor.h" 20 #include "util/EnumFlags.h" 21 22 namespace js { 23 24 /* Limit on the number of slotful properties in an object. */ 25 static constexpr uint32_t SHAPE_INVALID_SLOT = Bit(24) - 1; 26 static constexpr uint32_t SHAPE_MAXIMUM_SLOT = Bit(24) - 2; 27 28 // Flags associated with each property stored in the shape tree. 29 enum class PropertyFlag : uint8_t { 30 // Property attributes. See also JS::PropertyAttribute. 31 Configurable = 1 << 0, 32 Enumerable = 1 << 1, 33 Writable = 1 << 2, 34 35 // Whether this is an accessor property. Accessor properties have a slot that 36 // stores a GetterSetter instance. 37 AccessorProperty = 1 << 3, 38 39 // If set, this is a custom data property. The property is exposed as a data 40 // property to JS code and PropertyDescriptor, but instead of an object slot 41 // it uses custom get/set logic. 42 // 43 // This is used to implement the special array.length and ArgumentsObject 44 // properties. 45 // 46 // This flag is deprecated (we don't want to add more uses). 47 CustomDataProperty = 1 << 4, 48 }; 49 50 class PropertyFlags : public EnumFlags<PropertyFlag> { 51 using Base = EnumFlags<PropertyFlag>; 52 using Base::Base; 53 54 public: 55 static const PropertyFlags defaultDataPropFlags; 56 57 static PropertyFlags fromRaw(uint8_t flags) { return PropertyFlags(flags); } 58 59 bool configurable() const { return hasFlag(PropertyFlag::Configurable); } 60 bool enumerable() const { return hasFlag(PropertyFlag::Enumerable); } 61 bool writable() const { 62 MOZ_ASSERT(isDataDescriptor()); 63 return hasFlag(PropertyFlag::Writable); 64 } 65 66 // Note: this returns true only for plain data properties with a slot. Returns 67 // false for custom data properties. See CustomDataProperty flag. 68 bool isDataProperty() const { 69 return !isAccessorProperty() && !isCustomDataProperty(); 70 } 71 bool isAccessorProperty() const { 72 return hasFlag(PropertyFlag::AccessorProperty); 73 } 74 bool isCustomDataProperty() const { 75 return hasFlag(PropertyFlag::CustomDataProperty); 76 } 77 78 // Note: unlike isDataProperty, this returns true also for custom data 79 // properties. 80 bool isDataDescriptor() const { return !isAccessorProperty(); } 81 }; 82 83 constexpr PropertyFlags PropertyFlags::defaultDataPropFlags = { 84 PropertyFlag::Configurable, PropertyFlag::Enumerable, 85 PropertyFlag::Writable}; 86 87 // PropertyInfo contains information (PropertyFlags, slot number) for a 88 // property stored in the Shape tree. Property lookups on NativeObjects return a 89 // PropertyInfo. 90 // 91 // There's also a CompactPropertyInfo type that's used by CompactPropMap to 92 // store small slot numbers (CompactPropertyInfo is two bytes instead of four). 93 template <typename T> 94 class PropertyInfoBase { 95 static_assert(std::is_same_v<T, uint32_t> || std::is_same_v<T, uint16_t>); 96 97 static constexpr uint32_t FlagsMask = 0xff; 98 static constexpr uint32_t SlotShift = 8; 99 100 T slotAndFlags_ = 0; 101 102 static_assert(SHAPE_INVALID_SLOT <= (UINT32_MAX >> SlotShift), 103 "SHAPE_INVALID_SLOT must fit in slotAndFlags_"); 104 static_assert(SHAPE_MAXIMUM_SLOT <= (UINT32_MAX >> SlotShift), 105 "SHAPE_MAXIMUM_SLOT must fit in slotAndFlags_"); 106 107 // Constructor is private, code should prefer Maybe<PropertyInfo>. This 108 // constructor is only used for the propInfos array in property maps 109 // (CompactPropMap and LinkedPropMap are friend classes for this reason). 110 PropertyInfoBase() = default; 111 112 template <typename U> 113 friend class PropertyInfoBase; 114 friend class CompactPropMap; 115 friend class LinkedPropMap; 116 117 public: 118 static constexpr size_t MaxSlotNumber = 119 std::numeric_limits<T>::max() >> SlotShift; 120 121 PropertyInfoBase(PropertyFlags flags, uint32_t slot) 122 : slotAndFlags_((slot << SlotShift) | flags.toRaw()) { 123 MOZ_ASSERT(maybeSlot() == slot); 124 MOZ_ASSERT(this->flags() == flags); 125 } 126 127 template <typename U> 128 explicit PropertyInfoBase(PropertyInfoBase<U> other) 129 : slotAndFlags_(other.slotAndFlags_) { 130 // Assert assigning PropertyInfo to CompactPropertyInfo doesn't lose 131 // information. 132 MOZ_ASSERT(slotAndFlags_ == other.slotAndFlags_); 133 } 134 135 bool isDataProperty() const { return flags().isDataProperty(); } 136 bool isCustomDataProperty() const { return flags().isCustomDataProperty(); } 137 bool isAccessorProperty() const { return flags().isAccessorProperty(); } 138 bool isDataDescriptor() const { return flags().isDataDescriptor(); } 139 140 bool hasSlot() const { return !isCustomDataProperty(); } 141 142 uint32_t slot() const { 143 MOZ_ASSERT(hasSlot()); 144 MOZ_ASSERT(maybeSlot() < SHAPE_INVALID_SLOT); 145 return maybeSlot(); 146 } 147 148 uint32_t maybeSlot() const { return slotAndFlags_ >> SlotShift; } 149 150 PropertyFlags flags() const { 151 return PropertyFlags::fromRaw(slotAndFlags_ & FlagsMask); 152 } 153 bool writable() const { return flags().writable(); } 154 bool configurable() const { return flags().configurable(); } 155 bool enumerable() const { return flags().enumerable(); } 156 157 JS::PropertyAttributes propAttributes() const { 158 JS::PropertyAttributes attrs{}; 159 if (configurable()) { 160 attrs += JS::PropertyAttribute::Configurable; 161 } 162 if (enumerable()) { 163 attrs += JS::PropertyAttribute::Enumerable; 164 } 165 if (isDataDescriptor() && writable()) { 166 attrs += JS::PropertyAttribute::Writable; 167 } 168 return attrs; 169 } 170 171 T toRaw() const { return slotAndFlags_; } 172 173 bool operator==(const PropertyInfoBase<T>& other) const { 174 return slotAndFlags_ == other.slotAndFlags_; 175 } 176 bool operator!=(const PropertyInfoBase<T>& other) const { 177 return !operator==(other); 178 } 179 }; 180 181 using PropertyInfo = PropertyInfoBase<uint32_t>; 182 using CompactPropertyInfo = PropertyInfoBase<uint16_t>; 183 184 static_assert(sizeof(PropertyInfo) == sizeof(uint32_t)); 185 static_assert(sizeof(CompactPropertyInfo) == sizeof(uint16_t)); 186 187 class PropertyInfoWithKey : public PropertyInfo { 188 PropertyKey key_; 189 190 public: 191 PropertyInfoWithKey(PropertyFlags flags, uint32_t slot, PropertyKey key) 192 : PropertyInfo(flags, slot), key_(key) {} 193 194 PropertyInfoWithKey(PropertyInfo prop, PropertyKey key) 195 : PropertyInfo(prop), key_(key) {} 196 197 PropertyKey key() const { return key_; } 198 199 void trace(JSTracer* trc) { 200 TraceRoot(trc, &key_, "PropertyInfoWithKey-key"); 201 } 202 }; 203 204 template <class Wrapper> 205 class WrappedPtrOperations<PropertyInfoWithKey, Wrapper> { 206 const PropertyInfoWithKey& value() const { 207 return static_cast<const Wrapper*>(this)->get(); 208 } 209 210 public: 211 bool isDataProperty() const { return value().isDataProperty(); } 212 uint32_t slot() const { return value().slot(); } 213 PropertyKey key() const { return value().key(); } 214 PropertyFlags flags() const { return value().flags(); } 215 }; 216 217 using PropertyInfoWithKeyVector = GCVector<PropertyInfoWithKey, 16>; 218 219 } // namespace js 220 221 #endif /* vm_PropertyInfo_h */