diff --git a/js/ipc/JavaScriptBase.h b/js/ipc/JavaScriptBase.h index 7ed41fe07311..79624efaaf41 100644 --- a/js/ipc/JavaScriptBase.h +++ b/js/ipc/JavaScriptBase.h @@ -35,181 +35,181 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base /*** IPC handlers ***/ - bool AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs) { - return Answer::AnswerPreventExtensions(objId, rs); + bool AnswerPreventExtensions(const uint64_t &objId, ReturnStatus *rs) { + return Answer::AnswerPreventExtensions(ObjectId::deserialize(objId), rs); } - bool AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id, + bool AnswerGetPropertyDescriptor(const uint64_t &objId, const nsString &id, ReturnStatus *rs, PPropertyDescriptor *out) { - return Answer::AnswerGetPropertyDescriptor(objId, id, rs, out); + return Answer::AnswerGetPropertyDescriptor(ObjectId::deserialize(objId), id, rs, out); } - bool AnswerGetOwnPropertyDescriptor(const ObjectId &objId, + bool AnswerGetOwnPropertyDescriptor(const uint64_t &objId, const nsString &id, ReturnStatus *rs, PPropertyDescriptor *out) { - return Answer::AnswerGetOwnPropertyDescriptor(objId, id, rs, out); + return Answer::AnswerGetOwnPropertyDescriptor(ObjectId::deserialize(objId), id, rs, out); } - bool AnswerDefineProperty(const ObjectId &objId, const nsString &id, + bool AnswerDefineProperty(const uint64_t &objId, const nsString &id, const PPropertyDescriptor &flags, ReturnStatus *rs) { - return Answer::AnswerDefineProperty(objId, id, flags, rs); + return Answer::AnswerDefineProperty(ObjectId::deserialize(objId), id, flags, rs); } - bool AnswerDelete(const ObjectId &objId, const nsString &id, + bool AnswerDelete(const uint64_t &objId, const nsString &id, ReturnStatus *rs, bool *success) { - return Answer::AnswerDelete(objId, id, rs, success); + return Answer::AnswerDelete(ObjectId::deserialize(objId), id, rs, success); } - bool AnswerHas(const ObjectId &objId, const nsString &id, + bool AnswerHas(const uint64_t &objId, const nsString &id, ReturnStatus *rs, bool *bp) { - return Answer::AnswerHas(objId, id, rs, bp); + return Answer::AnswerHas(ObjectId::deserialize(objId), id, rs, bp); } - bool AnswerHasOwn(const ObjectId &objId, const nsString &id, + bool AnswerHasOwn(const uint64_t &objId, const nsString &id, ReturnStatus *rs, bool *bp) { - return Answer::AnswerHasOwn(objId, id, rs, bp); + return Answer::AnswerHasOwn(ObjectId::deserialize(objId), id, rs, bp); } - bool AnswerGet(const ObjectId &objId, const ObjectVariant &receiverVar, + bool AnswerGet(const uint64_t &objId, const ObjectVariant &receiverVar, const nsString &id, ReturnStatus *rs, JSVariant *result) { - return Answer::AnswerGet(objId, receiverVar, id, rs, result); + return Answer::AnswerGet(ObjectId::deserialize(objId), receiverVar, id, rs, result); } - bool AnswerSet(const ObjectId &objId, const ObjectVariant &receiverVar, + bool AnswerSet(const uint64_t &objId, const ObjectVariant &receiverVar, const nsString &id, const bool &strict, const JSVariant &value, ReturnStatus *rs, JSVariant *result) { - return Answer::AnswerSet(objId, receiverVar, id, strict, value, rs, result); + return Answer::AnswerSet(ObjectId::deserialize(objId), receiverVar, id, strict, value, rs, result); } - bool AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, + bool AnswerIsExtensible(const uint64_t &objId, ReturnStatus *rs, bool *result) { - return Answer::AnswerIsExtensible(objId, rs, result); + return Answer::AnswerIsExtensible(ObjectId::deserialize(objId), rs, result); } - bool AnswerCallOrConstruct(const ObjectId &objId, const nsTArray &argv, + bool AnswerCallOrConstruct(const uint64_t &objId, const nsTArray &argv, const bool &construct, ReturnStatus *rs, JSVariant *result, nsTArray *outparams) { - return Answer::AnswerCallOrConstruct(objId, argv, construct, rs, result, outparams); + return Answer::AnswerCallOrConstruct(ObjectId::deserialize(objId), argv, construct, rs, result, outparams); } - bool AnswerHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp) { - return Answer::AnswerHasInstance(objId, v, rs, bp); + bool AnswerHasInstance(const uint64_t &objId, const JSVariant &v, ReturnStatus *rs, bool *bp) { + return Answer::AnswerHasInstance(ObjectId::deserialize(objId), v, rs, bp); } - bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue, + bool AnswerObjectClassIs(const uint64_t &objId, const uint32_t &classValue, bool *result) { - return Answer::AnswerObjectClassIs(objId, classValue, result); + return Answer::AnswerObjectClassIs(ObjectId::deserialize(objId), classValue, result); } - bool AnswerClassName(const ObjectId &objId, nsString *result) { - return Answer::AnswerClassName(objId, result); + bool AnswerClassName(const uint64_t &objId, nsString *result) { + return Answer::AnswerClassName(ObjectId::deserialize(objId), result); } - bool AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags, + bool AnswerGetPropertyNames(const uint64_t &objId, const uint32_t &flags, ReturnStatus *rs, nsTArray *names) { - return Answer::AnswerGetPropertyNames(objId, flags, rs, names); + return Answer::AnswerGetPropertyNames(ObjectId::deserialize(objId), flags, rs, names); } - bool AnswerInstanceOf(const ObjectId &objId, const JSIID &iid, + bool AnswerInstanceOf(const uint64_t &objId, const JSIID &iid, ReturnStatus *rs, bool *instanceof) { - return Answer::AnswerInstanceOf(objId, iid, rs, instanceof); + return Answer::AnswerInstanceOf(ObjectId::deserialize(objId), iid, rs, instanceof); } - bool AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth, + bool AnswerDOMInstanceOf(const uint64_t &objId, const int &prototypeID, const int &depth, ReturnStatus *rs, bool *instanceof) { - return Answer::AnswerDOMInstanceOf(objId, prototypeID, depth, rs, instanceof); + return Answer::AnswerDOMInstanceOf(ObjectId::deserialize(objId), prototypeID, depth, rs, instanceof); } - bool AnswerIsCallable(const ObjectId &objId, bool *result) { - return Answer::AnswerIsCallable(objId, result); + bool AnswerIsCallable(const uint64_t &objId, bool *result) { + return Answer::AnswerIsCallable(ObjectId::deserialize(objId), result); } - bool AnswerIsConstructor(const ObjectId &objId, bool *result) { - return Answer::AnswerIsConstructor(objId, result); + bool AnswerIsConstructor(const uint64_t &objId, bool *result) { + return Answer::AnswerIsConstructor(ObjectId::deserialize(objId), result); } - bool RecvDropObject(const ObjectId &objId) { - return Answer::RecvDropObject(objId); + bool RecvDropObject(const uint64_t &objId) { + return Answer::RecvDropObject(ObjectId::deserialize(objId)); } /*** Dummy call handlers ***/ bool SendDropObject(const ObjectId &objId) { - return Base::SendDropObject(objId); + return Base::SendDropObject(objId.serialize()); } bool CallPreventExtensions(const ObjectId &objId, ReturnStatus *rs) { - return Base::CallPreventExtensions(objId, rs); + return Base::CallPreventExtensions(objId.serialize(), rs); } bool CallGetPropertyDescriptor(const ObjectId &objId, const nsString &id, ReturnStatus *rs, PPropertyDescriptor *out) { - return Base::CallGetPropertyDescriptor(objId, id, rs, out); + return Base::CallGetPropertyDescriptor(objId.serialize(), id, rs, out); } bool CallGetOwnPropertyDescriptor(const ObjectId &objId, const nsString &id, ReturnStatus *rs, PPropertyDescriptor *out) { - return Base::CallGetOwnPropertyDescriptor(objId, id, rs, out); + return Base::CallGetOwnPropertyDescriptor(objId.serialize(), id, rs, out); } bool CallDefineProperty(const ObjectId &objId, const nsString &id, const PPropertyDescriptor &flags, ReturnStatus *rs) { - return Base::CallDefineProperty(objId, id, flags, rs); + return Base::CallDefineProperty(objId.serialize(), id, flags, rs); } bool CallDelete(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *success) { - return Base::CallDelete(objId, id, rs, success); + return Base::CallDelete(objId.serialize(), id, rs, success); } bool CallHas(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp) { - return Base::CallHas(objId, id, rs, bp); + return Base::CallHas(objId.serialize(), id, rs, bp); } bool CallHasOwn(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp) { - return Base::CallHasOwn(objId, id, rs, bp); + return Base::CallHasOwn(objId.serialize(), id, rs, bp); } bool CallGet(const ObjectId &objId, const ObjectVariant &receiverVar, const nsString &id, ReturnStatus *rs, JSVariant *result) { - return Base::CallGet(objId, receiverVar, id, rs, result); + return Base::CallGet(objId.serialize(), receiverVar, id, rs, result); } bool CallSet(const ObjectId &objId, const ObjectVariant &receiverVar, const nsString &id, const bool &strict, const JSVariant &value, ReturnStatus *rs, JSVariant *result) { - return Base::CallSet(objId, receiverVar, id, strict, value, rs, result); + return Base::CallSet(objId.serialize(), receiverVar, id, strict, value, rs, result); } bool CallIsExtensible(const ObjectId &objId, ReturnStatus *rs, bool *result) { - return Base::CallIsExtensible(objId, rs, result); + return Base::CallIsExtensible(objId.serialize(), rs, result); } bool CallCallOrConstruct(const ObjectId &objId, const nsTArray &argv, const bool &construct, ReturnStatus *rs, JSVariant *result, nsTArray *outparams) { - return Base::CallCallOrConstruct(objId, argv, construct, rs, result, outparams); + return Base::CallCallOrConstruct(objId.serialize(), argv, construct, rs, result, outparams); } bool CallHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp) { - return Base::CallHasInstance(objId, v, rs, bp); + return Base::CallHasInstance(objId.serialize(), v, rs, bp); } bool CallObjectClassIs(const ObjectId &objId, const uint32_t &classValue, bool *result) { - return Base::CallObjectClassIs(objId, classValue, result); + return Base::CallObjectClassIs(objId.serialize(), classValue, result); } bool CallClassName(const ObjectId &objId, nsString *result) { - return Base::CallClassName(objId, result); + return Base::CallClassName(objId.serialize(), result); } bool CallGetPropertyNames(const ObjectId &objId, const uint32_t &flags, ReturnStatus *rs, nsTArray *names) { - return Base::CallGetPropertyNames(objId, flags, rs, names); + return Base::CallGetPropertyNames(objId.serialize(), flags, rs, names); } bool CallInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs, bool *instanceof) { - return Base::CallInstanceOf(objId, iid, rs, instanceof); + return Base::CallInstanceOf(objId.serialize(), iid, rs, instanceof); } bool CallDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth, ReturnStatus *rs, bool *instanceof) { - return Base::CallDOMInstanceOf(objId, prototypeID, depth, rs, instanceof); + return Base::CallDOMInstanceOf(objId.serialize(), prototypeID, depth, rs, instanceof); } bool CallIsCallable(const ObjectId &objId, bool *result) { - return Base::CallIsCallable(objId, result); + return Base::CallIsCallable(objId.serialize(), result); } bool CallIsConstructor(const ObjectId &objId, bool *result) { - return Base::CallIsConstructor(objId, result); + return Base::CallIsConstructor(objId.serialize(), result); } /* The following code is needed to suppress a bogus MSVC warning (C4250). */ diff --git a/js/ipc/JavaScriptLogging.h b/js/ipc/JavaScriptLogging.h index 404a6e2cc400..260795000169 100644 --- a/js/ipc/JavaScriptLogging.h +++ b/js/ipc/JavaScriptLogging.h @@ -160,9 +160,9 @@ class Logging case JSVariant::TObjectVariant: { const ObjectVariant &ovar = value.get_ObjectVariant(); if (ovar.type() == ObjectVariant::TLocalObject) - formatObject(incoming, true, ovar.get_LocalObject().id(), out); + formatObject(incoming, true, ObjectId::deserialize(ovar.get_LocalObject().serializedId()), out); else - formatObject(incoming, false, ovar.get_RemoteObject().id(), out); + formatObject(incoming, false, ObjectId::deserialize(ovar.get_RemoteObject().serializedId()), out); break; } case JSVariant::Tdouble: { diff --git a/js/ipc/JavaScriptShared.cpp b/js/ipc/JavaScriptShared.cpp index 7755cb1505ad..1f6d959c7cc4 100644 --- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -123,7 +123,7 @@ ObjectToIdMap::find(JSObject *obj) { Table::Ptr p = table_->lookup(obj); if (!p) - return 0; + return ObjectId::nullId(); return p->value(); } @@ -162,7 +162,7 @@ bool JavaScriptShared::sStackLoggingEnabled; JavaScriptShared::JavaScriptShared(JSRuntime *rt) : rt_(rt), refcount_(1), - lastId_(0) + nextSerialNumber_(1) { if (!sLoggingInitialized) { sLoggingInitialized = true; @@ -381,7 +381,7 @@ JavaScriptShared::ConvertID(const JSIID &from, nsID *to) } JSObject * -JavaScriptShared::findObjectById(JSContext *cx, uint32_t objId) +JavaScriptShared::findObjectById(JSContext *cx, const ObjectId &objId) { RootedObject obj(cx, findObjectById(objId)); if (!obj) { diff --git a/js/ipc/JavaScriptShared.h b/js/ipc/JavaScriptShared.h index 070efca5e5e1..e771eadaac87 100644 --- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -16,7 +16,47 @@ namespace mozilla { namespace jsipc { -typedef uint64_t ObjectId; +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 isCallable) + : serialNumber_(serialNumber), isCallable_(isCallable) + { + 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, isCallable() == other.isCallable()); + return equal; + } + + bool isNull() { return !serialNumber_; } + + uint64_t serialNumber() const { return serialNumber_; } + bool isCallable() const { return isCallable_; } + uint64_t serialize() const { + MOZ_ASSERT(serialNumber(), "Don't send a null ObjectId over IPC"); + return uint64_t((serialNumber() << FLAG_BITS) | ((isCallable() ? 1 : 0) << 0)); + } + + static ObjectId nullId() { return ObjectId(); } + static ObjectId deserialize(uint64_t data) { + return ObjectId(data >> FLAG_BITS, data & 1); + } + + private: + ObjectId() : serialNumber_(0), isCallable_(false) {} + + uint64_t serialNumber_ : SERIAL_NUMBER_BITS; + bool isCallable_ : 1; +}; class JavaScriptShared; @@ -36,12 +76,27 @@ class CpowIdHolder : public CpowHolder const InfallibleTArray &cpows_; }; +// DefaultHasher 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::DefaultHasher TableKeyHasher; - - typedef js::HashMap, TableKeyHasher, js::SystemAllocPolicy> Table; + typedef js::HashMap, ObjectIdHasher, js::SystemAllocPolicy> Table; public: IdToObjectMap(); @@ -95,9 +150,6 @@ class JavaScriptShared void decref(); void incref(); - static const uint32_t OBJECT_EXTRA_BITS = 1; - static const uint32_t OBJECT_IS_CALLABLE = (1 << 0); - bool Unwrap(JSContext *cx, const InfallibleTArray &aCpows, JS::MutableHandleObject objp); bool Wrap(JSContext *cx, JS::HandleObject aObj, InfallibleTArray *outCpows); @@ -119,13 +171,13 @@ class JavaScriptShared static void ConvertID(const nsID &from, JSIID *to); static void ConvertID(const JSIID &from, nsID *to); - JSObject *findCPOWById(uint32_t objId) { + JSObject *findCPOWById(const ObjectId &objId) { return cpows_.find(objId); } - JSObject *findObjectById(uint32_t objId) { + JSObject *findObjectById(const ObjectId &objId) { return objects_.find(objId); } - JSObject *findObjectById(JSContext *cx, uint32_t objId); + JSObject *findObjectById(JSContext *cx, const ObjectId &objId); static bool LoggingEnabled() { return sLoggingEnabled; } static bool StackLoggingEnabled() { return sStackLoggingEnabled; } @@ -143,7 +195,7 @@ class JavaScriptShared IdToObjectMap objects_; IdToObjectMap cpows_; - ObjectId lastId_; + uint64_t nextSerialNumber_; ObjectToIdMap objectIds_; static bool sLoggingInitialized; @@ -151,9 +203,6 @@ class JavaScriptShared static bool sStackLoggingEnabled; }; -// Use 47 at most, to be safe, since jsval privates are encoded as doubles. -static const uint64_t MAX_CPOW_IDS = (uint64_t(1) << 47) - 1; - } // namespace jsipc } // namespace mozilla diff --git a/js/ipc/JavaScriptTypes.ipdlh b/js/ipc/JavaScriptTypes.ipdlh index 6a6a67da2c48..07b359f29c14 100644 --- a/js/ipc/JavaScriptTypes.ipdlh +++ b/js/ipc/JavaScriptTypes.ipdlh @@ -29,12 +29,12 @@ struct JSIID struct LocalObject { - uint64_t id; + uint64_t serializedId; }; struct RemoteObject { - uint64_t id; + uint64_t serializedId; }; union ObjectVariant diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp index 835866ec0f81..199a7550a071 100644 --- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -38,8 +38,8 @@ WrapperOwner::idOfUnchecked(JSObject *obj) Value v = GetProxyExtra(obj, 1); MOZ_ASSERT(v.isDouble()); - ObjectId objId = BitwiseCast(v.toDouble()); - MOZ_ASSERT(objId); + ObjectId objId = ObjectId::deserialize(BitwiseCast(v.toDouble())); + MOZ_ASSERT(!objId.isNull()); return objId; } @@ -872,13 +872,13 @@ WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *ob // in findObjectById. obj = js::UncheckedUnwrap(obj, false); if (obj && IsCPOW(obj) && OwnerOf(obj) == this) { - *objVarp = LocalObject(idOf(obj)); + *objVarp = LocalObject(idOf(obj).serialize()); return true; } ObjectId id = objectIds_.find(obj); - if (id) { - *objVarp = RemoteObject(id); + if (!id.isNull()) { + *objVarp = RemoteObject(id.serialize()); return true; } @@ -887,22 +887,13 @@ WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *ob if (mozilla::dom::IsDOMObject(obj)) mozilla::dom::TryPreserveWrapper(obj); - id = ++lastId_; - if (id > MAX_CPOW_IDS) { - JS_ReportError(cx, "CPOW id limit reached"); - return false; - } - - id <<= OBJECT_EXTRA_BITS; - if (JS::IsCallable(obj)) - id |= OBJECT_IS_CALLABLE; - + id = ObjectId(nextSerialNumber_++, JS::IsCallable(obj)); if (!objects_.add(id, obj)) return false; if (!objectIds_.add(cx, obj, id)) return false; - *objVarp = RemoteObject(id); + *objVarp = RemoteObject(id.serialize()); return true; } @@ -919,14 +910,9 @@ WrapperOwner::fromObjectVariant(JSContext *cx, ObjectVariant objVar) JSObject * WrapperOwner::fromRemoteObjectVariant(JSContext *cx, RemoteObject objVar) { - ObjectId objId = objVar.id(); + ObjectId objId = ObjectId::deserialize(objVar.serializedId()); RootedObject obj(cx, findCPOWById(objId)); if (!obj) { - // If we didn't find an existing CPOW, we need to create one. - if (objId > MAX_CPOW_IDS) { - JS_ReportError(cx, "unusable CPOW id"); - return nullptr; - } // All CPOWs live in the privileged junk scope. RootedObject junkScope(cx, xpc::PrivilegedJunkScope()); @@ -947,7 +933,7 @@ WrapperOwner::fromRemoteObjectVariant(JSContext *cx, RemoteObject objVar) incref(); SetProxyExtra(obj, 0, PrivateValue(this)); - SetProxyExtra(obj, 1, DoubleValue(BitwiseCast(objId))); + SetProxyExtra(obj, 1, DoubleValue(BitwiseCast(objId.serialize()))); } if (!JS_WrapObject(cx, &obj)) @@ -958,7 +944,7 @@ WrapperOwner::fromRemoteObjectVariant(JSContext *cx, RemoteObject objVar) JSObject * WrapperOwner::fromLocalObjectVariant(JSContext *cx, LocalObject objVar) { - ObjectId id = objVar.id(); + ObjectId id = ObjectId::deserialize(objVar.serializedId()); Rooted obj(cx, findObjectById(cx, id)); if (!obj) return nullptr;