summaryrefslogtreecommitdiffstats
path: root/js/ipc/JavaScriptShared.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/ipc/JavaScriptShared.h')
-rw-r--r--js/ipc/JavaScriptShared.h236
1 files changed, 236 insertions, 0 deletions
diff --git a/js/ipc/JavaScriptShared.h b/js/ipc/JavaScriptShared.h
new file mode 100644
index 000000000..4de153826
--- /dev/null
+++ b/js/ipc/JavaScriptShared.h
@@ -0,0 +1,236 @@
+/* -*- 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);
+ 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