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