summaryrefslogtreecommitdiffstats
path: root/js/ipc/JavaScriptShared.h
blob: 5ecec742965ae4cec6f6648505fffe9cc44a3f69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sw=4 et tw=80:
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_jsipc_JavaScriptShared_h__
#define mozilla_jsipc_JavaScriptShared_h__

#include "mozilla/dom/DOMTypes.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/jsipc/PJavaScript.h"
#include "js/GCHashTable.h"
#include "nsJSUtils.h"

namespace mozilla {
namespace jsipc {

class ObjectId {
  public:
    // Use 47 bits at most, to be safe, since jsval privates are encoded as
    // doubles. See bug 1065811 comment 12 for an explanation.
    static const size_t SERIAL_NUMBER_BITS = 47;
    static const size_t FLAG_BITS = 1;
    static const uint64_t SERIAL_NUMBER_MAX = (uint64_t(1) << SERIAL_NUMBER_BITS) - 1;

    explicit ObjectId(uint64_t serialNumber, bool hasXrayWaiver)
      : serialNumber_(serialNumber), hasXrayWaiver_(hasXrayWaiver)
    {
        if (MOZ_UNLIKELY(serialNumber == 0 || serialNumber > SERIAL_NUMBER_MAX))
            MOZ_CRASH("Bad CPOW Id");
    }

    bool operator==(const ObjectId& other) const {
        bool equal = serialNumber() == other.serialNumber();
        MOZ_ASSERT_IF(equal, hasXrayWaiver() == other.hasXrayWaiver());
        return equal;
    }

    bool isNull() { return !serialNumber_; }

    uint64_t serialNumber() const { return serialNumber_; }
    bool hasXrayWaiver() const { return hasXrayWaiver_; }
    uint64_t serialize() const {
        MOZ_ASSERT(serialNumber(), "Don't send a null ObjectId over IPC");
        return uint64_t((serialNumber() << FLAG_BITS) | ((hasXrayWaiver() ? 1 : 0) << 0));
    }

    static ObjectId nullId() { return ObjectId(); }
    static ObjectId deserialize(uint64_t data) {
        return ObjectId(data >> FLAG_BITS, data & 1);
    }

    // For use with StructGCPolicy.
    void trace(JSTracer*) const {}
    bool needsSweep() const { return false; }

  private:
    ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {}

    uint64_t serialNumber_ : SERIAL_NUMBER_BITS;
    bool hasXrayWaiver_ : 1;
};

class JavaScriptShared;

// DefaultHasher<T> requires that T coerce to an integral type. We could make
// ObjectId do that, but doing so would weaken our type invariants, so we just
// reimplement it manually.
struct ObjectIdHasher
{
    typedef ObjectId Lookup;
    static js::HashNumber hash(const Lookup& l) {
        return l.serialize();
    }
    static bool match(const ObjectId& k, const ObjectId& l) {
        return k == l;
    }
    static void rekey(ObjectId& k, const ObjectId& newKey) {
        k = newKey;
    }
};

// Map ids -> JSObjects
class IdToObjectMap
{
    typedef js::HashMap<ObjectId, JS::Heap<JSObject*>, ObjectIdHasher, js::SystemAllocPolicy> Table;

  public:
    IdToObjectMap();

    bool init();
    void trace(JSTracer* trc, uint64_t minimumId = 0);
    void sweep();

    bool add(ObjectId id, JSObject* obj);
    JSObject* find(ObjectId id);
    JSObject* findPreserveColor(ObjectId id);
    void remove(ObjectId id);

    void clear();
    bool empty() const;

#ifdef DEBUG
    bool has(const ObjectId& id, const JSObject* obj) const;
#endif

  private:
    Table table_;
};

// Map JSObjects -> ids
class ObjectToIdMap
{
    using Hasher = js::MovableCellHasher<JS::Heap<JSObject*>>;
    using Table = JS::GCHashMap<JS::Heap<JSObject*>, ObjectId, Hasher, js::SystemAllocPolicy>;

  public:
    bool init();
    void trace(JSTracer* trc);
    void sweep();

    bool add(JSContext* cx, JSObject* obj, ObjectId id);
    ObjectId find(JSObject* obj);
    void remove(JSObject* obj);
    void clear();

  private:
    Table table_;
};

class Logging;

class JavaScriptShared : public CPOWManager
{
  public:
    JavaScriptShared();
    virtual ~JavaScriptShared();

    bool init();

    void decref();
    void incref();

    bool Unwrap(JSContext* cx, const InfallibleTArray<CpowEntry>& aCpows, JS::MutableHandleObject objp);
    bool Wrap(JSContext* cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry>* outCpows);

  protected:
    bool toVariant(JSContext* cx, JS::HandleValue from, JSVariant* to);
    bool fromVariant(JSContext* cx, const JSVariant& from, JS::MutableHandleValue to);

    bool toJSIDVariant(JSContext* cx, JS::HandleId from, JSIDVariant* to);
    bool fromJSIDVariant(JSContext* cx, const JSIDVariant& from, JS::MutableHandleId to);

    bool toSymbolVariant(JSContext* cx, JS::Symbol* sym, SymbolVariant* symVarp);
    JS::Symbol* fromSymbolVariant(JSContext* cx, const SymbolVariant& symVar);

    bool fromDescriptor(JSContext* cx, JS::Handle<JS::PropertyDescriptor> desc,
                        PPropertyDescriptor* out);
    bool toDescriptor(JSContext* cx, const PPropertyDescriptor& in,
                      JS::MutableHandle<JS::PropertyDescriptor> out);

    bool toObjectOrNullVariant(JSContext* cx, JSObject* obj, ObjectOrNullVariant* objVarp);
    JSObject* fromObjectOrNullVariant(JSContext* cx, const ObjectOrNullVariant& objVar);

    bool convertIdToGeckoString(JSContext* cx, JS::HandleId id, nsString* to);
    bool convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId id);

    virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) = 0;
    virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) = 0;

    static void ConvertID(const nsID& from, JSIID* to);
    static void ConvertID(const JSIID& from, nsID* to);

    JSObject* findCPOWById(const ObjectId& objId) {
        return cpows_.find(objId);
    }
    JSObject* findObjectById(JSContext* cx, const ObjectId& objId);

#ifdef DEBUG
    bool hasCPOW(const ObjectId& objId, const JSObject* obj) {
        return cpows_.has(objId, obj);
    }
#endif

    static bool LoggingEnabled() { return sLoggingEnabled; }
    static bool StackLoggingEnabled() { return sStackLoggingEnabled; }

    friend class Logging;

    virtual bool isParent() = 0;

    virtual JSObject* scopeForTargetObjects() = 0;

  protected:
    uintptr_t refcount_;

    IdToObjectMap objects_;
    IdToObjectMap cpows_;

    uint64_t nextSerialNumber_;

    // nextCPOWNumber_ should be the value of nextSerialNumber_ in the other
    // process. The next new CPOW we get should have this serial number.
    uint64_t nextCPOWNumber_;

    // CPOW references can be weak, and any object we store in a map may be
    // GCed (at which point the CPOW will report itself "dead" to the owner).
    // This means that we don't want to store any js::Wrappers in the CPOW map,
    // because CPOW will die if the wrapper is GCed, even if the underlying
    // object is still alive.
    //
    // This presents a tricky situation for Xray waivers, since they're normally
    // represented as a special same-compartment wrapper. We have to strip them
    // off before putting them in the id-to-object and object-to-id maps, so we
    // need a way of distinguishing them at lookup-time.
    //
    // For the id-to-object map, we encode waiver-or-not information into the id
    // itself, which lets us do the right thing when accessing the object.
    //
    // For the object-to-id map, we just keep two maps, one for each type.
    ObjectToIdMap unwaivedObjectIds_;
    ObjectToIdMap waivedObjectIds_;
    ObjectToIdMap& objectIdMap(bool waiver) {
        return waiver ? waivedObjectIds_ : unwaivedObjectIds_;
    }

    static bool sLoggingInitialized;
    static bool sLoggingEnabled;
    static bool sStackLoggingEnabled;
};

} // namespace jsipc
} // namespace mozilla

#endif