diff options
Diffstat (limited to 'js/ipc/JavaScriptShared.h')
-rw-r--r-- | js/ipc/JavaScriptShared.h | 236 |
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 |