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