From 0a59bbbf031a0c6cc1414541b8b44adc2669d034 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 23 May 2014 17:32:38 -0400 Subject: [PATCH] Bug 1007878 part 2. Add a C++ type to represent MozMap. r=khuey --- dom/bindings/BindingUtils.h | 81 ++++++++++++++++++++++ dom/bindings/Codegen.py | 39 +++++++++-- dom/bindings/MozMap.h | 132 ++++++++++++++++++++++++++++++++++++ dom/bindings/Nullable.h | 17 +++++ dom/bindings/TypedArray.h | 21 ++++++ dom/bindings/moz.build | 1 + xpcom/glue/nsTHashtable.h | 2 +- 7 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 dom/bindings/MozMap.h diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 4e62d9372a94..cb375237c75d 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -31,6 +31,7 @@ #include "qsObjectHelper.h" #include "xpcpublic.h" #include "nsIVariant.h" +#include "pldhash.h" // For PLDHashOperator #include "nsWrapperCacheInlines.h" @@ -43,6 +44,7 @@ xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle v, const nsIID& iid, vo namespace mozilla { namespace dom { +template class MozMap; struct SelfRef { @@ -1977,6 +1979,33 @@ public: } }; +// XXXbz It's not clear whether it's better to add a pldhash dependency here +// (for PLDHashOperator) or add a BindingUtils.h dependency (for +// SequenceTracer) to MozMap.h... +template +static PLDHashOperator +TraceMozMapValue(T* aValue, void* aClosure) +{ + JSTracer* trc = static_cast(aClosure); + // Act like it's a one-element sequence to leverage all that infrastructure. + SequenceTracer::TraceSequence(trc, aValue, aValue + 1); + return PL_DHASH_NEXT; +} + +// sequence +template +class SequenceTracer, false, false, false> +{ + explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated + +public: + static void TraceSequence(JSTracer* trc, MozMap* seqp, MozMap* end) { + for (; seqp != end; ++seqp) { + seqp->EnumerateValues(TraceMozMapValue, trc); + } + } +}; + template void DoTraceSequence(JSTracer* trc, FallibleTArray& seq) { @@ -2050,6 +2079,58 @@ public: SequenceType mSequenceType; }; +// Rooter class for MozMap; this is what we mostly use in the codegen. +template +class MOZ_STACK_CLASS MozMapRooter : private JS::CustomAutoRooter +{ +public: + MozMapRooter(JSContext *aCx, MozMap* aMozMap + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), + mMozMap(aMozMap), + mMozMapType(eMozMap) + { + } + + MozMapRooter(JSContext *aCx, Nullable>* aMozMap + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), + mNullableMozMap(aMozMap), + mMozMapType(eNullableMozMap) + { + } + +private: + enum MozMapType { + eMozMap, + eNullableMozMap + }; + + virtual void trace(JSTracer *trc) MOZ_OVERRIDE + { + MozMap* mozMap; + if (mMozMapType == eMozMap) { + mozMap = mMozMap; + } else { + MOZ_ASSERT(mMozMapType == eNullableMozMap); + if (mNullableMozMap->IsNull()) { + // Nothing to do + return; + } + mozMap = &mNullableMozMap->Value(); + } + + mozMap->EnumerateValues(TraceMozMapValue, trc); + } + + union { + MozMap* mMozMap; + Nullable>* mNullableMozMap; + }; + + MozMapType mMozMapType; +}; + template class MOZ_STACK_CLASS RootedUnion : public T, private JS::CustomAutoRooter diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 6106b04d5744..f65c0e2297db 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -39,7 +39,7 @@ def toBindingNamespace(arg): def isTypeCopyConstructible(type): - # Nullable and sequence stuff doesn't affect copy/constructibility + # Nullable and sequence stuff doesn't affect copy-constructibility type = type.unroll() return (type.isPrimitive() or type.isString() or type.isEnum() or (type.isUnion() and @@ -1028,7 +1028,7 @@ class CGHeaders(CGWrapper): headerSet.add(self.getDeclarationFilename(unrolled.inner)) elif unrolled.isCallback(): # Callbacks are both a type and an object - headerSet.add(self.getDeclarationFilename(t.unroll())) + headerSet.add(self.getDeclarationFilename(unrolled)) elif unrolled.isFloat() and not unrolled.isUnrestricted(): # Restricted floats are tested for finiteness bindingHeaders.add("mozilla/FloatingPoint.h") @@ -1038,6 +1038,14 @@ class CGHeaders(CGWrapper): declareIncludes.add(filename) elif unrolled.isPrimitive(): bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") + elif unrolled.isMozMap(): + if dictionary or jsImplementedDescriptors: + declareIncludes.add("mozilla/dom/MozMap.h") + else: + bindingHeaders.add("mozilla/dom/MozMap.h") + # Also add headers for the type the MozMap is + # parametrized over, if needed. + addHeadersForType((t.inner, descriptor, dictionary)) map(addHeadersForType, getAllTypes(descriptors + callbackDescriptors, dictionaries, @@ -1153,6 +1161,8 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): assert not descriptor or not dictionary t = t.unroll() + while t.isMozMap(): + t = t.inner.unroll() if not t.isUnion(): continue name = str(t) @@ -1163,10 +1173,10 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): owningUnionStructs[name] = CGUnionStruct(t, providers[0], ownsMembers=True) - for f in t.flatMemberTypes: - f = f.unroll() + def addHeadersForType(f): if f.nullable(): headers.add("mozilla/dom/Nullable.h") + f = f.unroll() if f.isInterface(): if f.isSpiderMonkeyInterface(): headers.add("jsfriendapi.h") @@ -1202,6 +1212,14 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): # the right header to be able to Release() in our inlined # code. headers.add(CGHeaders.getDeclarationFilename(f)) + elif f.isMozMap(): + headers.add("mozilla/dom/MozMap.h") + # And add headers for the type we're parametrized over + addHeadersForType(f.inner) + + for f in t.flatMemberTypes: + assert not f.nullable() + addHeadersForType(f) return (headers, implheaders, declarations, CGList(SortedDictValues(unionStructs) + @@ -1230,7 +1248,7 @@ def UnionConversions(descriptors, dictionaries, callbacks, config): if name not in unionConversions: providers = getRelevantProviders(descriptor, config) unionConversions[name] = CGUnionConversionStruct(t, providers[0]) - for f in t.flatMemberTypes: + def addHeadersForType(f, providers): f = f.unroll() if f.isInterface(): if f.isSpiderMonkeyInterface(): @@ -1257,6 +1275,13 @@ def UnionConversions(descriptors, dictionaries, callbacks, config): headers.add(CGHeaders.getDeclarationFilename(f.inner)) elif f.isPrimitive(): headers.add("mozilla/dom/PrimitiveConversions.h") + elif f.isMozMap(): + headers.add("mozilla/dom/MozMap.h") + # And the internal type of the MozMap + addHeadersForType(f.inner, providers) + + for f in t.flatMemberTypes: + addHeadersForType(f, providers) return (headers, CGWrapper(CGList(SortedDictValues(unionConversions), "\n"), @@ -10955,6 +10980,8 @@ class CGForwardDeclarations(CGWrapper): # since we don't know which one we might want builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False)) builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True)) + elif t.isMozMap(): + forwardDeclareForType(t.inner, workerness) # Don't need to do anything for void, primitive, string, any or object. # There may be some other cases we are missing. @@ -11058,6 +11085,8 @@ class CGBindingRoot(CGThing): def checkForXPConnectImpls(typeInfo): type, _, _ = typeInfo type = type.unroll() + while type.isMozMap(): + type = type.inner.unroll() if not type.isInterface() or not type.isGeckoInterface(): return False try: diff --git a/dom/bindings/MozMap.h b/dom/bindings/MozMap.h new file mode 100644 index 000000000000..3934abafef0d --- /dev/null +++ b/dom/bindings/MozMap.h @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 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/. */ + +/** + * Class for representing MozMap arguments. This is an nsTHashtable + * under the hood, but we don't want to leak that implementation + * detail. + */ + +#ifndef mozilla_dom_MozMap_h +#define mozilla_dom_MozMap_h + +#include "nsTHashtable.h" +#include "nsHashKeys.h" +#include "nsStringGlue.h" +#include "nsTArray.h" +#include "mozilla/Move.h" + +namespace mozilla { +namespace dom { + +namespace binding_detail { +template +class MozMapEntry : public nsStringHashKey +{ +public: + MozMapEntry(const nsAString* aKeyTypePointer) + : nsStringHashKey(aKeyTypePointer) + { + } + + // Move constructor so we can do MozMaps of MozMaps. + MozMapEntry(MozMapEntry&& aOther) + : nsStringHashKey(aOther), + mData(Move(aOther.mData)) + { + } + + DataType mData; +}; +} // namespace binding_detail + +template +class MozMap : protected nsTHashtable> +{ +public: + typedef typename binding_detail::MozMapEntry EntryType; + typedef nsTHashtable Base; + typedef MozMap SelfType; + + MozMap() + { + } + + // Move constructor so we can do MozMap of MozMap. + MozMap(SelfType&& aOther) : + Base(Move(aOther)) + { + } + + // The return value is only safe to use until an AddEntry call. + const DataType& Get(const nsAString& aKey) const + { + const EntryType* ent = this->GetEntry(aKey); + MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?"); + return ent->mData; + } + + // The return value is only safe to use until an AddEntry call. + const DataType* GetIfExists(const nsAString& aKey) const + { + const EntryType* ent = this->GetEntry(aKey); + if (!ent) { + return nullptr; + } + return &ent->mData; + } + + void GetKeys(nsTArray& aKeys) const { + // Sadly, EnumerateEntries is not a const method + const_cast(this)->EnumerateEntries(KeyEnumerator, &aKeys); + } + + // XXXbz we expose this generic enumerator for tracing. Otherwise we'd end up + // with a dependency on BindingUtils.h here for the SequenceTracer bits. + typedef PLDHashOperator (* Enumerator)(DataType* aValue, void* aClosure); + void EnumerateValues(Enumerator aEnumerator, void *aClosure) + { + ValueEnumClosure args = { aEnumerator, aClosure }; + this->EnumerateEntries(ValueEnumerator, &args); + } + + DataType* AddEntry(const nsAString& aKey) NS_WARN_UNUSED_RESULT + { + // XXXbz can't just use fallible_t() because our superclass has a + // private typedef by that name. + EntryType* ent = this->PutEntry(aKey, mozilla::fallible_t()); + if (!ent) { + return nullptr; + } + return &ent->mData; + } + +private: + static PLDHashOperator + KeyEnumerator(EntryType* aEntry, void* aClosure) + { + nsTArray& keys = *static_cast*>(aClosure); + keys.AppendElement(aEntry->GetKey()); + return PL_DHASH_NEXT; + } + + struct ValueEnumClosure { + Enumerator mEnumerator; + void* mClosure; + }; + + static PLDHashOperator + ValueEnumerator(EntryType* aEntry, void* aClosure) + { + ValueEnumClosure* enumClosure = static_cast(aClosure); + return enumClosure->mEnumerator(&aEntry->mData, enumClosure->mClosure); + } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MozMap_h diff --git a/dom/bindings/Nullable.h b/dom/bindings/Nullable.h index 80f2d3f3672c..6013468a96c0 100644 --- a/dom/bindings/Nullable.h +++ b/dom/bindings/Nullable.h @@ -9,6 +9,7 @@ #include "mozilla/Assertions.h" #include "nsTArrayForwardDeclare.h" +#include "mozilla/Move.h" namespace mozilla { namespace dom { @@ -33,6 +34,22 @@ public: , mValue(aValue) {} + explicit Nullable(Nullable&& aOther) + : mIsNull(aOther.mIsNull) + , mValue(mozilla::Move(aOther.mValue)) + {} + + Nullable(const Nullable& aOther) + : mIsNull(aOther.mIsNull) + , mValue(aOther.mValue) + {} + + void operator=(const Nullable& aOther) + { + mIsNull = aOther.mIsNull; + mValue = aOther.mValue; + } + void SetValue(T aValue) { mValue = aValue; mIsNull = false; diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index f7e7bd29db5a..20a81117936f 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -12,6 +12,7 @@ #include "js/RootingAPI.h" #include "js/TracingAPI.h" #include "mozilla/Attributes.h" +#include "mozilla/Move.h" #include "mozilla/dom/BindingDeclarations.h" #include "nsWrapperCache.h" @@ -29,6 +30,12 @@ protected: { } + explicit TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther) + : mObj(aOther.mObj) + { + aOther.mObj = nullptr; + } + public: inline void TraceSelf(JSTracer* trc) { @@ -62,6 +69,15 @@ struct TypedArray_base : public TypedArrayObjectStorage { mObj = nullptr; } + explicit TypedArray_base(TypedArray_base&& aOther) + : TypedArrayObjectStorage(Move(aOther)), + mData(aOther.mData), + mLength(aOther.mLength) + { + aOther.mData = nullptr; + aOther.mLength = 0; + } + private: T* mData; uint32_t mLength; @@ -123,6 +139,11 @@ struct TypedArray : public TypedArray_base { TypedArray_base() {} + explicit TypedArray(TypedArray&& aOther) + : TypedArray_base(Move(aOther)) + { + } + static inline JSObject* Create(JSContext* cx, nsWrapperCache* creator, uint32_t length, const T* data = nullptr) { diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index e01bb346149d..1162b0cedd6b 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -24,6 +24,7 @@ EXPORTS.mozilla.dom += [ 'Errors.msg', 'Exceptions.h', 'JSSlots.h', + 'MozMap.h', 'NonRefcountedDOMObject.h', 'Nullable.h', 'OwningNonNull.h', diff --git a/xpcom/glue/nsTHashtable.h b/xpcom/glue/nsTHashtable.h index d3fbc5dd2ab4..253459ccf4de 100644 --- a/xpcom/glue/nsTHashtable.h +++ b/xpcom/glue/nsTHashtable.h @@ -385,7 +385,7 @@ nsTHashtable::nsTHashtable( { // aOther shouldn't touch mTable after this, because we've stolen the table's // pointers but not overwitten them. - MOZ_MAKE_MEM_UNDEFINED(aOther.mTable, sizeof(aOther.mTable)); + MOZ_MAKE_MEM_UNDEFINED(&aOther.mTable, sizeof(aOther.mTable)); // Indicate that aOther is not initialized. This will make its destructor a // nop, which is what we want.