Line data Source code
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 0 : explicit ObjectId(uint64_t serialNumber, bool hasXrayWaiver)
30 0 : : serialNumber_(serialNumber), hasXrayWaiver_(hasXrayWaiver)
31 : {
32 0 : if (isInvalidSerialNumber(serialNumber))
33 0 : MOZ_CRASH("Bad CPOW Id");
34 0 : }
35 :
36 0 : bool operator==(const ObjectId& other) const {
37 0 : bool equal = serialNumber() == other.serialNumber();
38 0 : MOZ_ASSERT_IF(equal, hasXrayWaiver() == other.hasXrayWaiver());
39 0 : return equal;
40 : }
41 :
42 0 : bool isNull() { return !serialNumber_; }
43 :
44 0 : uint64_t serialNumber() const { return serialNumber_; }
45 0 : bool hasXrayWaiver() const { return hasXrayWaiver_; }
46 0 : uint64_t serialize() const {
47 0 : MOZ_ASSERT(serialNumber(), "Don't send a null ObjectId over IPC");
48 0 : return uint64_t((serialNumber() << FLAG_BITS) | ((hasXrayWaiver() ? 1 : 0) << 0));
49 : }
50 :
51 0 : static ObjectId nullId() { return ObjectId(); }
52 0 : static Maybe<ObjectId> deserialize(uint64_t data) {
53 0 : if (isInvalidSerialNumber(data >> FLAG_BITS)) {
54 : return Nothing();
55 : }
56 48 : return Some(ObjectId(data >> FLAG_BITS, data & 1));
57 : }
58 :
59 : // For use with StructGCPolicy.
60 : void trace(JSTracer*) const {}
61 : bool needsSweep() const { return false; }
62 :
63 : private:
64 : ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {}
65 :
66 : static bool isInvalidSerialNumber(uint64_t aSerialNumber) {
67 48 : return aSerialNumber == 0 || aSerialNumber > SERIAL_NUMBER_MAX;
68 : }
69 :
70 : uint64_t serialNumber_ : SERIAL_NUMBER_BITS;
71 : bool hasXrayWaiver_ : 1;
72 : };
73 :
74 : class JavaScriptShared;
75 :
76 : // DefaultHasher<T> requires that T coerce to an integral type. We could make
77 : // ObjectId do that, but doing so would weaken our type invariants, so we just
78 : // reimplement it manually.
79 : struct ObjectIdHasher
80 : {
81 : typedef ObjectId Lookup;
82 30 : static js::HashNumber hash(const Lookup& l) {
83 30 : return mozilla::HashGeneric(l.serialize());
84 : }
85 : static bool match(const ObjectId& k, const ObjectId& l) {
86 18 : return k == l;
87 : }
88 : static void rekey(ObjectId& k, const ObjectId& newKey) {
89 : k = newKey;
90 : }
91 : };
92 :
93 : // Map ids -> JSObjects
94 0 : class IdToObjectMap
95 : {
96 : typedef js::HashMap<ObjectId, JS::Heap<JSObject*>, ObjectIdHasher, js::SystemAllocPolicy> Table;
97 :
98 : public:
99 : IdToObjectMap();
100 :
101 : bool init();
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 4 : 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 : bool init();
129 : void trace(JSTracer* trc);
130 : void sweep();
131 :
132 : bool add(JSContext* cx, JSObject* obj, ObjectId id);
133 : ObjectId find(JSObject* obj);
134 : void remove(JSObject* obj);
135 : void clear();
136 :
137 : private:
138 : Table table_;
139 : };
140 :
141 : class Logging;
142 :
143 : class JavaScriptShared : public CPOWManager
144 : {
145 : public:
146 : JavaScriptShared();
147 : virtual ~JavaScriptShared();
148 :
149 : bool init();
150 :
151 : void decref();
152 : void incref();
153 :
154 : bool Unwrap(JSContext* cx, const InfallibleTArray<CpowEntry>& aCpows, JS::MutableHandleObject objp) override;
155 : bool Wrap(JSContext* cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry>* outCpows) override;
156 :
157 : protected:
158 : bool toVariant(JSContext* cx, JS::HandleValue from, JSVariant* to);
159 : bool fromVariant(JSContext* cx, const JSVariant& from, JS::MutableHandleValue to);
160 :
161 : bool toJSIDVariant(JSContext* cx, JS::HandleId from, JSIDVariant* to);
162 : bool fromJSIDVariant(JSContext* cx, const JSIDVariant& from, JS::MutableHandleId to);
163 :
164 : bool toSymbolVariant(JSContext* cx, JS::Symbol* sym, SymbolVariant* symVarp);
165 : JS::Symbol* fromSymbolVariant(JSContext* cx, const SymbolVariant& symVar);
166 :
167 : bool fromDescriptor(JSContext* cx, JS::Handle<JS::PropertyDescriptor> desc,
168 : PPropertyDescriptor* out);
169 : bool toDescriptor(JSContext* cx, const PPropertyDescriptor& in,
170 : JS::MutableHandle<JS::PropertyDescriptor> out);
171 :
172 : bool toObjectOrNullVariant(JSContext* cx, JSObject* obj, ObjectOrNullVariant* objVarp);
173 : JSObject* fromObjectOrNullVariant(JSContext* cx, const ObjectOrNullVariant& objVar);
174 :
175 : bool convertIdToGeckoString(JSContext* cx, JS::HandleId id, nsString* to);
176 : bool convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId id);
177 :
178 : virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) = 0;
179 : virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) = 0;
180 :
181 : static void ConvertID(const nsID& from, JSIID* to);
182 : static void ConvertID(const JSIID& from, nsID* to);
183 :
184 : JSObject* findCPOWById(const ObjectId& objId);
185 : JSObject* findCPOWByIdPreserveColor(const ObjectId& objId);
186 : JSObject* findObjectById(JSContext* cx, const ObjectId& objId);
187 :
188 : #ifdef DEBUG
189 0 : bool hasCPOW(const ObjectId& objId, const JSObject* obj) {
190 0 : MOZ_ASSERT(obj);
191 0 : return findCPOWByIdPreserveColor(objId) == obj;
192 : }
193 : #endif
194 :
195 0 : static bool LoggingEnabled() { return sLoggingEnabled; }
196 0 : static bool StackLoggingEnabled() { return sStackLoggingEnabled; }
197 :
198 : friend class Logging;
199 :
200 : virtual bool isParent() = 0;
201 :
202 : virtual JSObject* scopeForTargetObjects() = 0;
203 :
204 : protected:
205 : uintptr_t refcount_;
206 :
207 : IdToObjectMap objects_;
208 : IdToObjectMap cpows_;
209 :
210 : uint64_t nextSerialNumber_;
211 :
212 : // nextCPOWNumber_ should be the value of nextSerialNumber_ in the other
213 : // process. The next new CPOW we get should have this serial number.
214 : uint64_t nextCPOWNumber_;
215 :
216 : // CPOW references can be weak, and any object we store in a map may be
217 : // GCed (at which point the CPOW will report itself "dead" to the owner).
218 : // This means that we don't want to store any js::Wrappers in the CPOW map,
219 : // because CPOW will die if the wrapper is GCed, even if the underlying
220 : // object is still alive.
221 : //
222 : // This presents a tricky situation for Xray waivers, since they're normally
223 : // represented as a special same-compartment wrapper. We have to strip them
224 : // off before putting them in the id-to-object and object-to-id maps, so we
225 : // need a way of distinguishing them at lookup-time.
226 : //
227 : // For the id-to-object map, we encode waiver-or-not information into the id
228 : // itself, which lets us do the right thing when accessing the object.
229 : //
230 : // For the object-to-id map, we just keep two maps, one for each type.
231 : ObjectToIdMap unwaivedObjectIds_;
232 : ObjectToIdMap waivedObjectIds_;
233 : ObjectToIdMap& objectIdMap(bool waiver) {
234 : return waiver ? waivedObjectIds_ : unwaivedObjectIds_;
235 : }
236 :
237 : static bool sLoggingInitialized;
238 : static bool sLoggingEnabled;
239 : static bool sStackLoggingEnabled;
240 : };
241 :
242 : } // namespace jsipc
243 : } // namespace mozilla
244 :
245 : #endif
|