TaggedProto.h (5214B)
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_TaggedProto_h 8 #define vm_TaggedProto_h 9 10 #include "mozilla/Maybe.h" 11 12 #include "gc/Barrier.h" 13 #include "js/HashTable.h" 14 #include "js/RootingAPI.h" 15 16 class JSObject; 17 18 namespace js { 19 20 // Information about an object prototype, which can be either a particular 21 // object, null, or a lazily generated object. The latter is only used by 22 // certain kinds of proxies. 23 class TaggedProto { 24 public: 25 static JSObject* const LazyProto; 26 27 TaggedProto() : proto(nullptr) {} 28 TaggedProto(const TaggedProto& other) = default; 29 explicit TaggedProto(JSObject* proto) : proto(proto) {} 30 31 bool isDynamic() const { return proto == LazyProto; } 32 bool isObject() const { 33 /* Skip nullptr and LazyProto. */ 34 return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); 35 } 36 JSObject* toObject() const { 37 MOZ_ASSERT(isObject()); 38 return proto; 39 } 40 JSObject* toObjectOrNull() const { 41 MOZ_ASSERT(!proto || isObject()); 42 return proto; 43 } 44 JSObject* raw() const { return proto; } 45 46 bool operator==(const TaggedProto& other) const { 47 return proto == other.proto; 48 } 49 bool operator!=(const TaggedProto& other) const { 50 return proto != other.proto; 51 } 52 53 HashNumber hashCode() const; 54 55 void trace(JSTracer* trc); 56 57 private: 58 JSObject* proto; 59 }; 60 61 template <> 62 struct StableCellHasher<TaggedProto> { 63 using Key = TaggedProto; 64 using Lookup = TaggedProto; 65 66 static bool maybeGetHash(const Lookup& l, HashNumber* hashOut) { 67 if (!l.isObject()) { 68 *hashOut = hash(l); 69 return true; 70 } 71 72 return StableCellHasher<JSObject*>::maybeGetHash(l.toObject(), hashOut); 73 } 74 static bool ensureHash(const Lookup& l, HashNumber* hashOut) { 75 if (!l.isObject()) { 76 *hashOut = hash(l); 77 return true; 78 } 79 return StableCellHasher<JSObject*>::ensureHash(l.toObject(), hashOut); 80 } 81 static HashNumber hash(const Lookup& l) { 82 if (l.isDynamic()) { 83 return uint64_t(1); 84 } 85 if (!l.isObject()) { 86 return uint64_t(0); 87 } 88 return StableCellHasher<JSObject*>::hash(l.toObject()); 89 } 90 static bool match(const Key& k, const Lookup& l) { 91 return k.isDynamic() == l.isDynamic() && k.isObject() == l.isObject() && 92 (!k.isObject() || 93 StableCellHasher<JSObject*>::match(k.toObject(), l.toObject())); 94 } 95 }; 96 97 #ifdef DEBUG 98 MOZ_ALWAYS_INLINE void AssertTaggedProtoIsNotGray(const TaggedProto& proto) { 99 if (proto.isObject()) { 100 JS::AssertObjectIsNotGray(proto.toObject()); 101 } 102 } 103 #endif 104 105 template <> 106 struct InternalBarrierMethods<TaggedProto> { 107 static void preBarrier(TaggedProto& proto); 108 109 static void postBarrier(TaggedProto* vp, TaggedProto prev, TaggedProto next); 110 111 static void readBarrier(const TaggedProto& proto); 112 113 static bool isMarkable(const TaggedProto& proto) { return proto.isObject(); } 114 115 #ifdef DEBUG 116 static void assertThingIsNotGray(const TaggedProto& proto) { 117 AssertTaggedProtoIsNotGray(proto); 118 } 119 #endif 120 }; 121 122 template <class Wrapper> 123 class WrappedPtrOperations<TaggedProto, Wrapper> { 124 const TaggedProto& value() const { 125 return static_cast<const Wrapper*>(this)->get(); 126 } 127 128 public: 129 uintptr_t toWord() const { return value().toWord(); } 130 inline bool isDynamic() const { return value().isDynamic(); } 131 inline bool isObject() const { return value().isObject(); } 132 inline JSObject* toObject() const { return value().toObject(); } 133 inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } 134 JSObject* raw() const { return value().raw(); } 135 HashNumber hashCode() const { return value().hashCode(); } 136 uint64_t uniqueId() const { return value().uniqueId(); } 137 }; 138 139 // If the TaggedProto is a JSObject pointer, convert to that type and call |f| 140 // with the pointer. If the TaggedProto is lazy, returns None(). 141 template <typename F> 142 auto MapGCThingTyped(const TaggedProto& proto, F&& f) { 143 if (proto.isObject()) { 144 return mozilla::Some(f(proto.toObject())); 145 } 146 using ReturnType = decltype(f(static_cast<JSObject*>(nullptr))); 147 return mozilla::Maybe<ReturnType>(); 148 } 149 150 template <typename F> 151 bool ApplyGCThingTyped(const TaggedProto& proto, F&& f) { 152 return MapGCThingTyped(proto, 153 [&f](auto t) { 154 f(t); 155 return true; 156 }) 157 .isSome(); 158 } 159 160 // Since JSObject pointers are either nullptr or a valid object and since the 161 // object layout of TaggedProto is identical to a bare object pointer, we can 162 // safely treat a pointer to an already-rooted object (e.g. HandleObject) as a 163 // pointer to a TaggedProto. 164 inline Handle<TaggedProto> AsTaggedProto(HandleObject obj) { 165 static_assert(sizeof(JSObject*) == sizeof(TaggedProto), 166 "TaggedProto must be binary compatible with JSObject"); 167 return Handle<TaggedProto>::fromMarkedLocation( 168 reinterpret_cast<TaggedProto const*>(obj.address())); 169 } 170 171 } // namespace js 172 173 #endif // vm_TaggedProto_h