/src/mozilla-central/js/ipc/JavaScriptShared.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * vim: set ts=8 sw=4 et tw=80: |
3 | | * |
4 | | * This Source Code Form is subject to the terms of the Mozilla Public |
5 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
6 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
7 | | |
8 | | #ifndef mozilla_jsipc_JavaScriptShared_h__ |
9 | | #define mozilla_jsipc_JavaScriptShared_h__ |
10 | | |
11 | | #include "mozilla/HashFunctions.h" |
12 | | #include "mozilla/dom/DOMTypes.h" |
13 | | #include "mozilla/jsipc/CrossProcessObjectWrappers.h" |
14 | | #include "mozilla/jsipc/PJavaScript.h" |
15 | | #include "js/GCHashTable.h" |
16 | | #include "nsJSUtils.h" |
17 | | |
18 | | namespace mozilla { |
19 | | namespace jsipc { |
20 | | |
21 | | class ObjectId { |
22 | | public: |
23 | | // Use 47 bits at most, to be safe, since jsval privates are encoded as |
24 | | // doubles. See bug 1065811 comment 12 for an explanation. |
25 | | static const size_t SERIAL_NUMBER_BITS = 47; |
26 | | static const size_t FLAG_BITS = 1; |
27 | | static const uint64_t SERIAL_NUMBER_MAX = (uint64_t(1) << SERIAL_NUMBER_BITS) - 1; |
28 | | |
29 | | explicit ObjectId(uint64_t serialNumber, bool hasXrayWaiver) |
30 | | : serialNumber_(serialNumber), hasXrayWaiver_(hasXrayWaiver) |
31 | 0 | { |
32 | 0 | if (isInvalidSerialNumber(serialNumber)) { |
33 | 0 | MOZ_CRASH("Bad CPOW Id"); |
34 | 0 | } |
35 | 0 | } |
36 | | |
37 | 0 | bool operator==(const ObjectId& other) const { |
38 | 0 | bool equal = serialNumber() == other.serialNumber(); |
39 | 0 | MOZ_ASSERT_IF(equal, hasXrayWaiver() == other.hasXrayWaiver()); |
40 | 0 | return equal; |
41 | 0 | } |
42 | | |
43 | 0 | bool isNull() { return !serialNumber_; } |
44 | | |
45 | 0 | uint64_t serialNumber() const { return serialNumber_; } |
46 | 0 | bool hasXrayWaiver() const { return hasXrayWaiver_; } |
47 | 0 | uint64_t serialize() const { |
48 | 0 | MOZ_ASSERT(serialNumber(), "Don't send a null ObjectId over IPC"); |
49 | 0 | return uint64_t((serialNumber() << FLAG_BITS) | ((hasXrayWaiver() ? 1 : 0) << 0)); |
50 | 0 | } |
51 | | |
52 | 0 | static ObjectId nullId() { return ObjectId(); } |
53 | 0 | static Maybe<ObjectId> deserialize(uint64_t data) { |
54 | 0 | if (isInvalidSerialNumber(data >> FLAG_BITS)) { |
55 | 0 | return Nothing(); |
56 | 0 | } |
57 | 0 | return Some(ObjectId(data >> FLAG_BITS, data & 1)); |
58 | 0 | } |
59 | | |
60 | | // For use with StructGCPolicy. |
61 | 0 | void trace(JSTracer*) const {} |
62 | 0 | bool needsSweep() const { return false; } |
63 | | |
64 | | private: |
65 | 0 | ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {} |
66 | | |
67 | 0 | static bool isInvalidSerialNumber(uint64_t aSerialNumber) { |
68 | 0 | return aSerialNumber == 0 || aSerialNumber > SERIAL_NUMBER_MAX; |
69 | 0 | } |
70 | | |
71 | | uint64_t serialNumber_ : SERIAL_NUMBER_BITS; |
72 | | bool hasXrayWaiver_ : 1; |
73 | | }; |
74 | | |
75 | | class JavaScriptShared; |
76 | | |
77 | | // DefaultHasher<T> requires that T coerce to an integral type. We could make |
78 | | // ObjectId do that, but doing so would weaken our type invariants, so we just |
79 | | // reimplement it manually. |
80 | | struct ObjectIdHasher |
81 | | { |
82 | | typedef ObjectId Lookup; |
83 | 0 | static js::HashNumber hash(const Lookup& l) { |
84 | 0 | return mozilla::HashGeneric(l.serialize()); |
85 | 0 | } |
86 | 0 | static bool match(const ObjectId& k, const ObjectId& l) { |
87 | 0 | return k == l; |
88 | 0 | } |
89 | 0 | static void rekey(ObjectId& k, const ObjectId& newKey) { |
90 | 0 | k = newKey; |
91 | 0 | } |
92 | | }; |
93 | | |
94 | | // Map ids -> JSObjects |
95 | | class IdToObjectMap |
96 | | { |
97 | | typedef js::HashMap<ObjectId, JS::Heap<JSObject*>, ObjectIdHasher, js::SystemAllocPolicy> Table; |
98 | | |
99 | | public: |
100 | | IdToObjectMap(); |
101 | | |
102 | | void trace(JSTracer* trc, uint64_t minimumId = 0); |
103 | | void sweep(); |
104 | | |
105 | | bool add(ObjectId id, JSObject* obj); |
106 | | JSObject* find(ObjectId id); |
107 | | JSObject* findPreserveColor(ObjectId id); |
108 | | void remove(ObjectId id); |
109 | | |
110 | | void clear(); |
111 | | bool empty() const; |
112 | | |
113 | | #ifdef DEBUG |
114 | | bool has(const ObjectId& id, const JSObject* obj) const; |
115 | | #endif |
116 | | |
117 | | private: |
118 | | Table table_; |
119 | | }; |
120 | | |
121 | | // Map JSObjects -> ids |
122 | | class ObjectToIdMap |
123 | | { |
124 | | using Hasher = js::MovableCellHasher<JS::Heap<JSObject*>>; |
125 | | using Table = JS::GCHashMap<JS::Heap<JSObject*>, ObjectId, Hasher, js::SystemAllocPolicy>; |
126 | | |
127 | | public: |
128 | | ObjectToIdMap(); |
129 | | |
130 | | void trace(JSTracer* trc); |
131 | | void sweep(); |
132 | | |
133 | | bool add(JSContext* cx, JSObject* obj, ObjectId id); |
134 | | ObjectId find(JSObject* obj); |
135 | | void remove(JSObject* obj); |
136 | | void clear(); |
137 | | |
138 | | private: |
139 | | Table table_; |
140 | | }; |
141 | | |
142 | | class Logging; |
143 | | |
144 | | class JavaScriptShared : public CPOWManager |
145 | | { |
146 | | public: |
147 | | JavaScriptShared(); |
148 | | virtual ~JavaScriptShared(); |
149 | | |
150 | | void decref(); |
151 | | void incref(); |
152 | | |
153 | | bool Unwrap(JSContext* cx, const InfallibleTArray<CpowEntry>& aCpows, JS::MutableHandleObject objp) override; |
154 | | bool Wrap(JSContext* cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry>* outCpows) override; |
155 | | |
156 | | protected: |
157 | | bool toVariant(JSContext* cx, JS::HandleValue from, JSVariant* to); |
158 | | bool fromVariant(JSContext* cx, const JSVariant& from, JS::MutableHandleValue to); |
159 | | |
160 | | bool toJSIDVariant(JSContext* cx, JS::HandleId from, JSIDVariant* to); |
161 | | bool fromJSIDVariant(JSContext* cx, const JSIDVariant& from, JS::MutableHandleId to); |
162 | | |
163 | | bool toSymbolVariant(JSContext* cx, JS::Symbol* sym, SymbolVariant* symVarp); |
164 | | JS::Symbol* fromSymbolVariant(JSContext* cx, const SymbolVariant& symVar); |
165 | | |
166 | | bool fromDescriptor(JSContext* cx, JS::Handle<JS::PropertyDescriptor> desc, |
167 | | PPropertyDescriptor* out); |
168 | | bool toDescriptor(JSContext* cx, const PPropertyDescriptor& in, |
169 | | JS::MutableHandle<JS::PropertyDescriptor> out); |
170 | | |
171 | | bool toObjectOrNullVariant(JSContext* cx, JSObject* obj, ObjectOrNullVariant* objVarp); |
172 | | JSObject* fromObjectOrNullVariant(JSContext* cx, const ObjectOrNullVariant& objVar); |
173 | | |
174 | | bool convertIdToGeckoString(JSContext* cx, JS::HandleId id, nsString* to); |
175 | | bool convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId id); |
176 | | |
177 | | virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) = 0; |
178 | | virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) = 0; |
179 | | |
180 | | static void ConvertID(const nsID& from, JSIID* to); |
181 | | static void ConvertID(const JSIID& from, nsID* to); |
182 | | |
183 | | JSObject* findCPOWById(const ObjectId& objId); |
184 | | JSObject* findCPOWByIdPreserveColor(const ObjectId& objId); |
185 | | JSObject* findObjectById(JSContext* cx, const ObjectId& objId); |
186 | | |
187 | | #ifdef DEBUG |
188 | | bool hasCPOW(const ObjectId& objId, const JSObject* obj) { |
189 | | MOZ_ASSERT(obj); |
190 | | return findCPOWByIdPreserveColor(objId) == obj; |
191 | | } |
192 | | #endif |
193 | | |
194 | 0 | static bool LoggingEnabled() { return sLoggingEnabled; } |
195 | 0 | static bool StackLoggingEnabled() { return sStackLoggingEnabled; } |
196 | | |
197 | | friend class Logging; |
198 | | |
199 | | virtual bool isParent() = 0; |
200 | | |
201 | | virtual JSObject* scopeForTargetObjects() = 0; |
202 | | |
203 | | protected: |
204 | | uintptr_t refcount_; |
205 | | |
206 | | IdToObjectMap objects_; |
207 | | IdToObjectMap cpows_; |
208 | | |
209 | | uint64_t nextSerialNumber_; |
210 | | |
211 | | // nextCPOWNumber_ should be the value of nextSerialNumber_ in the other |
212 | | // process. The next new CPOW we get should have this serial number. |
213 | | uint64_t nextCPOWNumber_; |
214 | | |
215 | | // CPOW references can be weak, and any object we store in a map may be |
216 | | // GCed (at which point the CPOW will report itself "dead" to the owner). |
217 | | // This means that we don't want to store any js::Wrappers in the CPOW map, |
218 | | // because CPOW will die if the wrapper is GCed, even if the underlying |
219 | | // object is still alive. |
220 | | // |
221 | | // This presents a tricky situation for Xray waivers, since they're normally |
222 | | // represented as a special same-compartment wrapper. We have to strip them |
223 | | // off before putting them in the id-to-object and object-to-id maps, so we |
224 | | // need a way of distinguishing them at lookup-time. |
225 | | // |
226 | | // For the id-to-object map, we encode waiver-or-not information into the id |
227 | | // itself, which lets us do the right thing when accessing the object. |
228 | | // |
229 | | // For the object-to-id map, we just keep two maps, one for each type. |
230 | | ObjectToIdMap unwaivedObjectIds_; |
231 | | ObjectToIdMap waivedObjectIds_; |
232 | 0 | ObjectToIdMap& objectIdMap(bool waiver) { |
233 | 0 | return waiver ? waivedObjectIds_ : unwaivedObjectIds_; |
234 | 0 | } |
235 | | |
236 | | static bool sLoggingInitialized; |
237 | | static bool sLoggingEnabled; |
238 | | static bool sStackLoggingEnabled; |
239 | | }; |
240 | | |
241 | | } // namespace jsipc |
242 | | } // namespace mozilla |
243 | | |
244 | | #endif |