summaryrefslogtreecommitdiffstats
path: root/dom/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings')
-rw-r--r--dom/bindings/BindingUtils.cpp10
-rw-r--r--dom/bindings/BindingUtils.h101
-rw-r--r--dom/bindings/Codegen.py340
-rw-r--r--dom/bindings/Configuration.py2
-rw-r--r--dom/bindings/MozMap.h121
-rw-r--r--dom/bindings/Record.h91
-rw-r--r--dom/bindings/moz.build2
-rw-r--r--dom/bindings/parser/WebIDL.py152
8 files changed, 463 insertions, 356 deletions
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index 323feca52..8d2bdaac6 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2558,7 +2558,7 @@ NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
template<typename T> static void
-NormalizeUSVStringInternal(JSContext* aCx, T& aString)
+NormalizeUSVStringInternal(T& aString)
{
char16_t* start = aString.BeginWriting();
// Must use const here because we can't pass char** to UTF16CharEnumerator as
@@ -2575,15 +2575,15 @@ NormalizeUSVStringInternal(JSContext* aCx, T& aString)
}
void
-NormalizeUSVString(JSContext* aCx, nsAString& aString)
+NormalizeUSVString(nsAString& aString)
{
- NormalizeUSVStringInternal(aCx, aString);
+ NormalizeUSVStringInternal(aString);
}
void
-NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString)
+NormalizeUSVString(binding_detail::FakeString& aString)
{
- NormalizeUSVStringInternal(aCx, aString);
+ NormalizeUSVStringInternal(aString);
}
bool
diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h
index 24b47a545..a3ec70f47 100644
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -49,7 +49,7 @@ namespace mozilla {
enum UseCounter : int16_t;
namespace dom {
-template<typename DataType> class MozMap;
+template<typename KeyType, typename ValueType> class Record;
nsresult
UnwrapArgImpl(JS::Handle<JSObject*> src, const nsIID& iid, void** ppArg);
@@ -2127,11 +2127,30 @@ ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
return AssignJSString(cx, result, s);
}
+template<typename T>
+static inline bool
+ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v, T& result)
+{
+ return ConvertJSValueToString(cx, v, eStringify, eStringify, result);
+}
+
void
-NormalizeUSVString(JSContext* aCx, nsAString& aString);
+NormalizeUSVString(nsAString& aString);
void
-NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString);
+NormalizeUSVString(binding_detail::FakeString& aString);
+
+template<typename T>
+static inline bool
+ConvertJSValueToUSVString(JSContext* cx, JS::Handle<JS::Value> v, T& result)
+{
+ if (!ConvertJSValueToString(cx, v, eStringify, eStringify, result)) {
+ return false;
+ }
+
+ NormalizeUSVString(result);
+ return true;
+}
template<typename T>
inline bool
@@ -2158,6 +2177,13 @@ bool
ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
bool nullable, nsACString& result);
+inline bool
+ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
+ nsACString& result)
+{
+ return ConvertJSValueToByteString(cx, v, false, result);
+}
+
template<typename T>
void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
template<typename T>
@@ -2293,31 +2319,26 @@ public:
}
};
-template<typename T>
-static void
-TraceMozMapValue(T* aValue, void* aClosure)
-{
- JSTracer* trc = static_cast<JSTracer*>(aClosure);
- // Act like it's a one-element sequence to leverage all that infrastructure.
- SequenceTracer<T>::TraceSequence(trc, aValue, aValue + 1);
-}
-
-template<typename T>
-void TraceMozMap(JSTracer* trc, MozMap<T>& map)
+template<typename K, typename V>
+void TraceRecord(JSTracer* trc, Record<K, V>& record)
{
- map.EnumerateValues(TraceMozMapValue<T>, trc);
+ for (auto& entry : record.Entries()) {
+ // Act like it's a one-element sequence to leverage all that infrastructure.
+ SequenceTracer<V>::TraceSequence(trc, &entry.mValue, &entry.mValue + 1);
+ }
}
-// sequence<MozMap>
-template<typename T>
-class SequenceTracer<MozMap<T>, false, false, false>
+// sequence<record>
+template<typename K, typename V>
+class SequenceTracer<Record<K, V>, false, false, false>
{
explicit SequenceTracer() = delete; // Should never be instantiated
public:
- static void TraceSequence(JSTracer* trc, MozMap<T>* seqp, MozMap<T>* end) {
+ static void TraceSequence(JSTracer* trc, Record<K, V>* seqp,
+ Record<K, V>* end) {
for (; seqp != end; ++seqp) {
- seqp->EnumerateValues(TraceMozMapValue<T>, trc);
+ TraceRecord(trc, *seqp);
}
}
};
@@ -2395,51 +2416,51 @@ public:
SequenceType mSequenceType;
};
-// Rooter class for MozMap; this is what we mostly use in the codegen.
-template<typename T>
-class MOZ_RAII MozMapRooter final : private JS::CustomAutoRooter
+// Rooter class for Record; this is what we mostly use in the codegen.
+template<typename K, typename V>
+class MOZ_RAII RecordRooter final : private JS::CustomAutoRooter
{
public:
- MozMapRooter(JSContext *aCx, MozMap<T>* aMozMap
+ RecordRooter(JSContext *aCx, Record<K, V>* aRecord
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
- mMozMap(aMozMap),
- mMozMapType(eMozMap)
+ mRecord(aRecord),
+ mRecordType(eRecord)
{
}
- MozMapRooter(JSContext *aCx, Nullable<MozMap<T>>* aMozMap
+ RecordRooter(JSContext *aCx, Nullable<Record<K, V>>* aRecord
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
- mNullableMozMap(aMozMap),
- mMozMapType(eNullableMozMap)
+ mNullableRecord(aRecord),
+ mRecordType(eNullableRecord)
{
}
private:
- enum MozMapType {
- eMozMap,
- eNullableMozMap
+ enum RecordType {
+ eRecord,
+ eNullableRecord
};
virtual void trace(JSTracer *trc) override
{
- if (mMozMapType == eMozMap) {
- TraceMozMap(trc, *mMozMap);
+ if (mRecordType == eRecord) {
+ TraceRecord(trc, *mRecord);
} else {
- MOZ_ASSERT(mMozMapType == eNullableMozMap);
- if (!mNullableMozMap->IsNull()) {
- TraceMozMap(trc, mNullableMozMap->Value());
+ MOZ_ASSERT(mRecordType == eNullableRecord);
+ if (!mNullableRecord->IsNull()) {
+ TraceRecord(trc, mNullableRecord->Value());
}
}
}
union {
- MozMap<T>* mMozMap;
- Nullable<MozMap<T>>* mNullableMozMap;
+ Record<K, V>* mRecord;
+ Nullable<Record<K, V>>* mNullableRecord;
};
- MozMapType mMozMapType;
+ RecordType mRecordType;
};
template<typename T>
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index cb93e4897..74acb5918 100644
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -84,7 +84,7 @@ def idlTypeNeedsCycleCollection(type):
return True
elif type.isUnion():
return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
- elif type.isMozMap():
+ elif type.isRecord():
if idlTypeNeedsCycleCollection(type.inner):
raise TypeError("Cycle collection for type %s is not supported" % type)
return False
@@ -996,6 +996,8 @@ class CGElseChain(CGThing):
class CGTemplatedType(CGWrapper):
def __init__(self, templateName, child, isConst=False, isReference=False):
+ if isinstance(child, list):
+ child = CGList(child, ", ")
const = "const " if isConst else ""
pre = "%s%s<" % (const, templateName)
ref = "&" if isReference else ""
@@ -1171,12 +1173,12 @@ class CGHeaders(CGWrapper):
declareIncludes.add(filename)
elif unrolled.isPrimitive():
bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
- elif unrolled.isMozMap():
+ elif unrolled.isRecord():
if dictionary or jsImplementedDescriptors:
- declareIncludes.add("mozilla/dom/MozMap.h")
+ declareIncludes.add("mozilla/dom/Record.h")
else:
- bindingHeaders.add("mozilla/dom/MozMap.h")
- # Also add headers for the type the MozMap is
+ bindingHeaders.add("mozilla/dom/Record.h")
+ # Also add headers for the type the record is
# parametrized over, if needed.
addHeadersForType((t.inner, dictionary))
@@ -1388,8 +1390,8 @@ def UnionTypes(unionTypes, config):
# the right header to be able to Release() in our inlined
# code.
headers.add(CGHeaders.getDeclarationFilename(f.callback))
- elif f.isMozMap():
- headers.add("mozilla/dom/MozMap.h")
+ elif f.isRecord():
+ headers.add("mozilla/dom/Record.h")
# And add headers for the type we're parametrized over
addHeadersForType(f.inner)
@@ -1448,9 +1450,9 @@ def UnionConversions(unionTypes, 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
+ elif f.isRecord():
+ headers.add("mozilla/dom/Record.h")
+ # And the internal type of the record
addHeadersForType(f.inner)
# We plan to include UnionTypes.h no matter what, so it's
@@ -4290,6 +4292,9 @@ class JSToNativeConversionInfo():
for whether we have a JS::Value. Only used when
defaultValue is not None or when True is passed for
checkForValue to instantiateJSToNativeConversion.
+ This expression may not be already-parenthesized, so if
+ you use it with && or || make sure to put parens
+ around it.
${passedToJSImpl} replaced by an expression that evaluates to a boolean
for whether this value is being passed to a JS-
implemented interface.
@@ -4355,7 +4360,9 @@ def handleDefaultStringValue(defaultValue, method):
passing as the second argument of handleDefault; in particular it does not
end with a ';'
"""
- assert defaultValue.type.isDOMString() or defaultValue.type.isByteString()
+ assert (defaultValue.type.isDOMString() or
+ defaultValue.type.isUSVString() or
+ defaultValue.type.isByteString())
return ("static const %(char_t)s data[] = { %(data)s };\n"
"%(method)s(data, ArrayLength(data) - 1)") % {
'char_t': "char" if defaultValue.type.isByteString() else "char16_t",
@@ -4365,6 +4372,17 @@ def handleDefaultStringValue(defaultValue, method):
}
+def recordKeyType(recordType):
+ assert recordType.keyType.isString()
+ if recordType.keyType.isByteString():
+ return "nsCString"
+ return "nsString"
+
+
+def recordKeyDeclType(recordType):
+ return CGGeneric(recordKeyType(recordType))
+
+
# If this function is modified, modify CGNativeMember.getArg and
# CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
# and holdertype we end up using, because it needs to be able to return the code
@@ -4559,7 +4577,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
declArgs = "cx"
else:
assert (isMember in
- ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap"))
+ ("Sequence", "Variadic", "Dictionary", "OwningUnion", "Record"))
# We'll get traced by the sequence or dictionary or union tracer
declType = CGGeneric("JSObject*")
declArgs = None
@@ -4725,39 +4743,41 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
dealWithOptional=isOptional,
holderArgs=holderArgs)
- if type.isMozMap():
+ if type.isRecord():
assert not isEnforceRange and not isClamp
if failureCode is None:
- notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
+ notRecord = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
"%s" % (firstCap(sourceDescription), exceptionCode))
else:
- notMozMap = failureCode
+ notRecord = failureCode
nullable = type.nullable()
# Be very careful not to change "type": we need it later
if nullable:
- valueType = type.inner.inner
+ recordType = type.inner
else:
- valueType = type.inner
+ recordType = type
+ valueType = recordType.inner
valueInfo = getJSToNativeConversionInfo(
- valueType, descriptorProvider, isMember="MozMap",
+ valueType, descriptorProvider, isMember="Record",
exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
isCallbackReturnValue=isCallbackReturnValue,
sourceDescription="value in %s" % sourceDescription,
nestingLevel=incrementNestingLevel())
if valueInfo.dealWithOptional:
- raise TypeError("Shouldn't have optional things in MozMap")
+ raise TypeError("Shouldn't have optional things in record")
if valueInfo.holderType is not None:
- raise TypeError("Shouldn't need holders for MozMap")
+ raise TypeError("Shouldn't need holders for record")
- typeName = CGTemplatedType("MozMap", valueInfo.declType)
- mozMapType = typeName.define()
+ declType = CGTemplatedType("Record", [recordKeyDeclType(recordType),
+ valueInfo.declType])
+ typeName = declType.define()
if nullable:
- typeName = CGTemplatedType("Nullable", typeName)
- mozMapRef = "${declName}.SetValue()"
+ declType = CGTemplatedType("Nullable", declType)
+ recordRef = "${declName}.SetValue()"
else:
- mozMapRef = "${declName}"
+ recordRef = "${declName}"
valueConversion = string.Template(valueInfo.template).substitute({
"val": "temp",
@@ -4770,68 +4790,122 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
"passedToJSImpl": "${passedToJSImpl}"
})
+ keyType = recordKeyType(recordType)
+ if recordType.keyType.isByteString():
+ keyConversionFunction = "ConvertJSValueToByteString"
+ hashKeyType = "nsCStringHashKey"
+ else:
+ hashKeyType = "nsStringHashKey"
+ if recordType.keyType.isDOMString():
+ keyConversionFunction = "ConvertJSValueToString"
+ else:
+ assert recordType.keyType.isUSVString()
+ keyConversionFunction = "ConvertJSValueToUSVString"
+
templateBody = fill(
"""
- ${mozMapType} &mozMap = ${mozMapRef};
+ auto& recordEntries = ${recordRef}.Entries();
- JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject());
- JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
- if (!JS_Enumerate(cx, mozMapObj, &ids)) {
+ JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
+ JS::AutoIdVector ids(cx);
+ if (!js::GetPropertyKeys(cx, recordObj,
+ JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
+ $*{exceptionCode}
+ }
+ if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
+ JS_ReportOutOfMemory(cx);
$*{exceptionCode}
}
JS::Rooted<JS::Value> propNameValue(cx);
JS::Rooted<JS::Value> temp(cx);
JS::Rooted<jsid> curId(cx);
+ JS::Rooted<JS::Value> idVal(cx);
+ // Use a hashset to keep track of ids seen, to avoid
+ // introducing nasty O(N^2) behavior scanning for them all the
+ // time. Ideally we'd use a data structure with O(1) lookup
+ // _and_ ordering for the MozMap, but we don't have one lying
+ // around.
+ nsTHashtable<${hashKeyType}> idsSeen;
for (size_t i = 0; i < ids.length(); ++i) {
- // Make sure we get the value before converting the name, since
- // getting the value can trigger GC but our name is a dependent
- // string.
curId = ids[i];
- binding_detail::FakeString propName;
- bool isSymbol;
- if (!ConvertIdToString(cx, curId, propName, isSymbol) ||
- (!isSymbol && !JS_GetPropertyById(cx, mozMapObj, curId, &temp))) {
+
+ JS::Rooted<JS::PropertyDescriptor> desc(cx);
+ if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
+ &desc)) {
$*{exceptionCode}
}
- if (isSymbol) {
+
+ if (!desc.object() /* == undefined in spec terms */ ||
+ !desc.enumerable()) {
continue;
}
- ${valueType}* slotPtr = mozMap.AddEntry(propName);
- if (!slotPtr) {
- JS_ReportOutOfMemory(cx);
+ idVal = js::IdToValue(curId);
+ ${keyType} propName;
+ // This will just throw if idVal is a Symbol, like the spec says
+ // to do.
+ if (!${keyConversionFunction}(cx, idVal, propName)) {
$*{exceptionCode}
}
- ${valueType}& slot = *slotPtr;
+
+ if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
+ $*{exceptionCode}
+ }
+
+ ${typeName}::EntryType* entry;
+ if (idsSeen.Contains(propName)) {
+ // Find the existing entry.
+ auto idx = recordEntries.IndexOf(propName);
+ MOZ_ASSERT(idx != recordEntries.NoIndex,
+ "Why is it not found?");
+ // Now blow it away to make it look like it was just added
+ // to the array, because it's not obvious that it's
+ // safe to write to its already-initialized mValue via our
+ // normal codegen conversions. For example, the value
+ // could be a union and this would change its type, but
+ // codegen assumes we won't do that.
+ entry = recordEntries.ReconstructElementAt(idx);
+ } else {
+ // Safe to do an infallible append here, because we did a
+ // SetCapacity above to the right capacity.
+ entry = recordEntries.AppendElement();
+ idsSeen.PutEntry(propName);
+ }
+ entry->mKey = propName;
+ ${valueType}& slot = entry->mValue;
$*{valueConversion}
}
""",
exceptionCode=exceptionCode,
- mozMapType=mozMapType,
- mozMapRef=mozMapRef,
+ recordRef=recordRef,
+ hashKeyType=hashKeyType,
+ keyType=keyType,
+ keyConversionFunction=keyConversionFunction,
+ typeName=typeName,
valueType=valueInfo.declType.define(),
valueConversion=valueConversion)
templateBody = wrapObjectTemplate(templateBody, type,
"${declName}.SetNull();\n",
- notMozMap)
+ notRecord)
- declType = typeName
declArgs = None
holderType = None
holderArgs = None
- # MozMap arguments that might contain traceable things need
+ # record arguments that might contain traceable things need
# to get traced
if not isMember and isCallbackReturnValue:
# Go ahead and just convert directly into our actual return value
declType = CGWrapper(declType, post="&")
declArgs = "aRetVal"
elif not isMember and typeNeedsRooting(valueType):
- holderType = CGTemplatedType("MozMapRooter", valueInfo.declType)
- # If our MozMap is nullable, this will set the Nullable to be
+ holderType = CGTemplatedType("RecordRooter",
+ [recordKeyDeclType(recordType),
+ valueInfo.declType])
+ # If our record is nullable, this will set the Nullable to be
# not-null, but that's ok because we make an explicit SetNull() call
# on it as needed if our JS value is actually null.
- holderArgs = "cx, &%s" % mozMapRef
+ holderArgs = "cx, &%s" % recordRef
return JSToNativeConversionInfo(templateBody, declType=declType,
declArgs=declArgs,
@@ -4914,16 +4988,16 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
setDictionary = None
- mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
- if len(mozMapMemberTypes) > 0:
- assert len(mozMapMemberTypes) == 1
- name = getUnionMemberName(mozMapMemberTypes[0])
- mozMapObject = CGGeneric(
+ recordMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
+ if len(recordMemberTypes) > 0:
+ assert len(recordMemberTypes) == 1
+ name = getUnionMemberName(recordMemberTypes[0])
+ recordObject = CGGeneric(
"done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
(unionArgumentObj, name))
names.append(name)
else:
- mozMapObject = None
+ recordObject = None
objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
if len(objectMemberTypes) > 0:
@@ -4939,10 +5013,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
object = None
- hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or mozMapObject
+ hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or recordObject
if hasObjectTypes:
# "object" is not distinguishable from other types
- assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or mozMapObject)
+ assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or recordObject)
if sequenceObject or dateObject or callbackObject:
# An object can be both an sequence object and a callback or
# dictionary, but we shouldn't have both in the union's members
@@ -4962,9 +5036,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if dateObject:
templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))
- if mozMapObject:
+ if recordObject:
templateBody = CGList([templateBody,
- CGIfWrapper(mozMapObject, "!done")])
+ CGIfWrapper(recordObject, "!done")])
templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
else:
@@ -5144,7 +5218,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if isinstance(defaultValue, IDLNullValue):
extraConditionForNull = "!(${haveValue}) || "
else:
- extraConditionForNull = "${haveValue} && "
+ extraConditionForNull = "(${haveValue}) && "
else:
extraConditionForNull = ""
templateBody = handleNull(templateBody, declLoc,
@@ -5525,7 +5599,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
def getConversionCode(varName):
normalizeCode = ""
if type.isUSVString():
- normalizeCode = "NormalizeUSVString(cx, %s);\n" % varName
+ normalizeCode = "NormalizeUSVString(%s);\n" % varName
conversionCode = fill("""
if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
@@ -5688,7 +5762,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
haveCallable = "${val}.isObject() && " + haveCallable
if defaultValue is not None:
assert(isinstance(defaultValue, IDLNullValue))
- haveCallable = "${haveValue} && " + haveCallable
+ haveCallable = "(${haveValue}) && " + haveCallable
template = (
("if (%s) {\n" % haveCallable) +
conversion +
@@ -5700,7 +5774,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
haveObject = "${val}.isObject()"
if defaultValue is not None:
assert(isinstance(defaultValue, IDLNullValue))
- haveObject = "${haveValue} && " + haveObject
+ haveObject = "(${haveValue}) && " + haveObject
template = CGIfElseWrapper(haveObject,
CGGeneric(conversion),
CGGeneric("${declName} = nullptr;\n")).define()
@@ -5724,7 +5798,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not isEnforceRange and not isClamp
declArgs = None
- if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"):
+ if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
# Rooting is handled by the sequence and dictionary tracers.
declType = "JS::Value"
else:
@@ -5768,8 +5842,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
if type.isDictionary():
- # There are no nullable dictionaries
- assert not type.nullable() or isCallbackReturnValue
+ # There are no nullable dictionary arguments or dictionary members
+ assert(not type.nullable() or isCallbackReturnValue or
+ (isMember and isMember != "Dictionary"))
# All optional dictionaries always have default values, so we
# should be able to assume not isOptional here.
assert not isOptional
@@ -6256,7 +6331,7 @@ def getMaybeWrapValueFuncForType(type):
sequenceWrapLevel = 0
-mozMapWrapLevel = 0
+recordWrapLevel = 0
def getWrapTemplateForType(type, descriptorProvider, result, successCode,
@@ -6361,7 +6436,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
if type is None or type.isVoid():
return (setUndefined(), True)
- if (type.isSequence() or type.isMozMap()) and type.nullable():
+ if (type.isSequence() or type.isRecord()) and type.nullable():
# These are both wrapped in Nullable<>
recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode,
@@ -6434,14 +6509,14 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return (code, False)
- if type.isMozMap():
- # Now do non-nullable MozMap. Our success code is just to break to
+ if type.isRecord():
+ # Now do non-nullable record. Our success code is just to break to
# where we define the property on the object. Note that we bump the
- # mozMapWrapLevel around this call so that nested MozMap conversions
+ # recordWrapLevel around this call so that nested record conversions
# will use different temp value names.
- global mozMapWrapLevel
- valueName = "mozMapValue%d" % mozMapWrapLevel
- mozMapWrapLevel += 1
+ global recordWrapLevel
+ valueName = "recordValue%d" % recordWrapLevel
+ recordWrapLevel += 1
innerTemplate = wrapForType(
type.inner, descriptorProvider,
{
@@ -6454,12 +6529,22 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'obj': "returnObj",
'typedArraysAreStructs': typedArraysAreStructs
})
- mozMapWrapLevel -= 1
+ recordWrapLevel -= 1
+ if type.keyType.isByteString():
+ # There is no length-taking JS_DefineProperty. So to keep
+ # things sane with embedded nulls, we want to byte-inflate
+ # to an nsAString. The only byte-inflation function we
+ # have around is AppendASCIItoUTF16, which luckily doesn't
+ # assert anything about the input being ASCII.
+ expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
+ keyName = "expandedKey"
+ else:
+ expandedKeyDecl = ""
+ keyName = "entry.mKey"
+
code = fill(
"""
- nsTArray<nsString> keys;
- ${result}.GetKeys(keys);
JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
if (!returnObj) {
$*{exceptionCode}
@@ -6467,15 +6552,17 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
// Scope for 'tmp'
{
JS::Rooted<JS::Value> tmp(cx);
- for (size_t idx = 0; idx < keys.Length(); ++idx) {
- auto& ${valueName} = ${result}.Get(keys[idx]);
+ for (auto& entry : ${result}.Entries()) {
+ auto& ${valueName} = entry.mValue;
// Control block to let us common up the JS_DefineUCProperty calls when there
// are different ways to succeed at wrapping the value.
do {
$*{innerTemplate}
} while (0);
- if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(),
- keys[idx].Length(), tmp,
+ $*{expandedKeyDecl}
+ if (!JS_DefineUCProperty(cx, returnObj,
+ ${keyName}.BeginReading(),
+ ${keyName}.Length(), tmp,
JSPROP_ENUMERATE)) {
$*{exceptionCode}
}
@@ -6487,6 +6574,8 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
exceptionCode=exceptionCode,
valueName=valueName,
innerTemplate=innerTemplate,
+ expandedKeyDecl=expandedKeyDecl,
+ keyName=keyName,
set=setObject("*returnObj"))
return (code, False)
@@ -6770,7 +6859,7 @@ def typeMatchesLambda(type, func):
return False
if type.nullable():
return typeMatchesLambda(type.inner, func)
- if type.isSequence() or type.isMozMap():
+ if type.isSequence() or type.isRecord():
return typeMatchesLambda(type.inner, func)
if type.isUnion():
return any(typeMatchesLambda(t, func) for t in
@@ -6866,20 +6955,21 @@ def getRetvalDeclarationForType(returnType, descriptorProvider,
if nullable:
result = CGTemplatedType("Nullable", result)
return result, "ref", rooter, None, None
- if returnType.isMozMap():
+ if returnType.isRecord():
nullable = returnType.nullable()
if nullable:
returnType = returnType.inner
result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner,
descriptorProvider,
- isMember="MozMap")
+ isMember="Record")
# While we have our inner type, set up our rooter, if needed
if not isMember and typeNeedsRooting(returnType):
- rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" %
- result.define())
+ rooter = CGGeneric("RecordRooter<%s> resultRooter(cx, &result);\n" %
+ ("nsString, " + result.define()))
else:
rooter = None
- result = CGTemplatedType("MozMap", result)
+ result = CGTemplatedType("Record", [recordKeyDeclType(returnType),
+ result])
if nullable:
result = CGTemplatedType("Nullable", result)
return result, "ref", rooter, None, None
@@ -6976,7 +7066,7 @@ class CGCallGenerator(CGThing):
return True
if a.type.isSequence():
return True
- if a.type.isMozMap():
+ if a.type.isRecord():
return True
# isObject() types are always a JS::Rooted, whether
# nullable or not, and it turns out a const JS::Rooted
@@ -7138,7 +7228,7 @@ class MethodNotNewObjectError(Exception):
# nested sequences we don't use the same variable name to iterate over
# different sequences.
sequenceWrapLevel = 0
-mapWrapLevel = 0
+recordWrapLevel = 0
def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
@@ -7199,29 +7289,27 @@ def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
return wrapCode
- if type.isMozMap():
- origValue = value
+ if type.isRecord():
origType = type
if type.nullable():
type = type.inner
- value = "%s.Value()" % value
- global mapWrapLevel
- key = "mapName%d" % mapWrapLevel
- mapWrapLevel += 1
+ recordRef = "%s.Value()" % value
+ else:
+ recordRef = value
+ global recordWrapLevel
+ entryRef = "mapEntry%d" % recordWrapLevel
+ recordWrapLevel += 1
wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
- "%s.Get(%sKeys[%sIndex])" % (value, key, key))
- mapWrapLevel -= 1
+ "%s.mValue" % entryRef)
+ recordWrapLevel -= 1
if not wrapElement:
return None
wrapCode = CGWrapper(CGIndenter(wrapElement),
- pre=("""
- nsTArray<nsString> %sKeys;
- %s.GetKeys(%sKeys);
- for (uint32_t %sIndex = 0; %sIndex < %sKeys.Length(); ++%sIndex) {
- """ % (key, value, key, key, key, key, key)),
+ pre=("for (auto& %s : %s.Entries()) {\n" %
+ (entryRef, recordRef)),
post="}\n")
if origType.nullable():
- wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
+ wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
return wrapCode
if type.isDictionary():
@@ -8110,11 +8198,11 @@ class CGMethodCall(CGThing):
if distinguishingType(s).isSequence())
# Now append all the overloads that take a dictionary or callback
- # interface or MozMap. There should be only one of these!
+ # interface or record. There should be only one of these!
genericObjectSigs = [
s for s in possibleSignatures
if (distinguishingType(s).isDictionary() or
- distinguishingType(s).isMozMap() or
+ distinguishingType(s).isRecord() or
distinguishingType(s).isCallbackInterface())]
assert len(genericObjectSigs) <= 1
objectSigs.extend(genericObjectSigs)
@@ -9408,7 +9496,7 @@ class CGMemberJITInfo(CGThing):
return "JSVAL_TYPE_UNDEFINED"
if t.isSequence():
return "JSVAL_TYPE_OBJECT"
- if t.isMozMap():
+ if t.isRecord():
return "JSVAL_TYPE_OBJECT"
if t.isGeckoInterface():
return "JSVAL_TYPE_OBJECT"
@@ -9669,17 +9757,22 @@ def getUnionAccessorSignatureType(type, descriptorProvider):
# Flat member types have already unwrapped nullables.
assert not type.nullable()
- if type.isSequence() or type.isMozMap():
+ if type.isSequence() or type.isRecord():
if type.isSequence():
wrapperType = "Sequence"
else:
- wrapperType = "MozMap"
+ wrapperType = "Record"
# We don't use the returned template here, so it's OK to just pass no
# sourceDescription.
elementInfo = getJSToNativeConversionInfo(type.inner,
descriptorProvider,
isMember=wrapperType)
- return CGTemplatedType(wrapperType, elementInfo.declType,
+ if wrapperType == "Sequence":
+ innerType = elementInfo.declType
+ else:
+ innerType = [recordKeyDeclType(type), elementInfo.declType]
+
+ return CGTemplatedType(wrapperType, innerType,
isConst=True, isReference=True)
# Nested unions are unwrapped automatically into our flatMemberTypes.
@@ -10040,10 +10133,10 @@ class CGUnionStruct(CGThing):
CGCase("e" + vars["name"],
CGGeneric("DoTraceSequence(trc, mValue.m%s.Value());\n" %
vars["name"])))
- elif t.isMozMap():
+ elif t.isRecord():
traceCases.append(
CGCase("e" + vars["name"],
- CGGeneric("TraceMozMap(trc, mValue.m%s.Value());\n" %
+ CGGeneric("TraceRecord(trc, mValue.m%s.Value());\n" %
vars["name"])))
else:
assert t.isSpiderMonkeyInterface()
@@ -13172,8 +13265,8 @@ class CGDictionary(CGThing):
trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
if type.nullable():
trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
- elif type.isMozMap():
- # If you implement this, add a MozMap<object> to
+ elif type.isRecord():
+ # If you implement this, add a record<DOMString, object> to
# TestInterfaceJSDictionary and test it in test_bug1036214.html
# to make sure we end up with the correct security properties.
assert False
@@ -13583,7 +13676,7 @@ class ForwardDeclarationBuilder:
# since we don't know which one we might want
self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
- elif t.isMozMap():
+ elif t.isRecord():
self.forwardDeclareForType(t.inner, config)
# Don't need to do anything for void, primitive, string, any or object.
# There may be some other cases we are missing.
@@ -14089,9 +14182,9 @@ class CGNativeMember(ClassMethod):
else:
returnCode = "aRetVal.SwapElements(${declName});\n"
return "void", "", returnCode
- if type.isMozMap():
- # If we want to handle MozMap-of-MozMap return values, we're
- # going to need to fix example codegen to not produce MozMap<void>
+ if type.isRecord():
+ # If we want to handle record-of-record return values, we're
+ # going to need to fix example codegen to not produce record<void>
# for the relevant argument...
assert not isMember
# In this case we convert directly into our outparam to start with
@@ -14139,13 +14232,14 @@ class CGNativeMember(ClassMethod):
if nullable:
type = CGTemplatedType("Nullable", type)
args.append(Argument("%s&" % type.define(), "aRetVal"))
- elif returnType.isMozMap():
+ elif returnType.isRecord():
nullable = returnType.nullable()
if nullable:
returnType = returnType.inner
# And now the actual underlying type
elementDecl = self.getReturnType(returnType.inner, True)
- type = CGTemplatedType("MozMap", CGGeneric(elementDecl))
+ type = CGTemplatedType("Record", [recordKeyDeclType(returnType),
+ CGGeneric(elementDecl)])
if nullable:
type = CGTemplatedType("Nullable", type)
args.append(Argument("%s&" % type.define(), "aRetVal"))
@@ -14206,7 +14300,7 @@ class CGNativeMember(ClassMethod):
Nullable as needed.
isMember can be false or one of the strings "Sequence", "Variadic",
- "MozMap"
+ "Record"
"""
if type.isSequence():
nullable = type.nullable()
@@ -14217,13 +14311,13 @@ class CGNativeMember(ClassMethod):
decl = CGTemplatedType("Sequence", argType)
return decl.define(), True, True
- if type.isMozMap():
+ if type.isRecord():
nullable = type.nullable()
if nullable:
type = type.inner
elementType = type.inner
- argType = self.getArgType(elementType, False, "MozMap")[0]
- decl = CGTemplatedType("MozMap", argType)
+ argType = self.getArgType(elementType, False, "Record")[0]
+ decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
return decl.define(), True, True
if type.isUnion():
diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py
index 5c96580a1..f80c19c33 100644
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -115,7 +115,7 @@ class Configuration(DescriptorProvider):
for (t, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
while True:
- if t.isMozMap():
+ if t.isRecord():
t = t.inner
elif t.unroll() != t:
t = t.unroll()
diff --git a/dom/bindings/MozMap.h b/dom/bindings/MozMap.h
deleted file mode 100644
index 1e920c098..000000000
--- a/dom/bindings/MozMap.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/* -*- 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/Attributes.h"
-#include "mozilla/Move.h"
-
-namespace mozilla {
-namespace dom {
-
-namespace binding_detail {
-template<typename DataType>
-class MozMapEntry : public nsStringHashKey
-{
-public:
- explicit MozMapEntry(const nsAString* aKeyTypePointer)
- : nsStringHashKey(aKeyTypePointer)
- {
- }
-
- // Move constructor so we can do MozMaps of MozMaps.
- MozMapEntry(MozMapEntry<DataType>&& aOther)
- : nsStringHashKey(aOther),
- mData(Move(aOther.mData))
- {
- }
-
- DataType mData;
-};
-
-} // namespace binding_detail
-
-template<typename DataType>
-class MozMap : protected nsTHashtable<binding_detail::MozMapEntry<DataType>>
-{
-public:
- typedef typename binding_detail::MozMapEntry<DataType> EntryType;
- typedef nsTHashtable<EntryType> Base;
- typedef MozMap<DataType> 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;
- }
-
- DataType& Get(const nsAString& aKey)
- {
- 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<nsString>& aKeys) const {
- for (auto iter = this->ConstIter(); !iter.Done(); iter.Next()) {
- aKeys.AppendElement(iter.Get()->GetKey());
- }
- }
-
- // 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 void (* Enumerator)(DataType* aValue, void* aClosure);
- void EnumerateValues(Enumerator aEnumerator, void *aClosure)
- {
- for (auto iter = this->Iter(); !iter.Done(); iter.Next()) {
- aEnumerator(&iter.Get()->mData, aClosure);
- }
- }
-
- MOZ_MUST_USE
- DataType* AddEntry(const nsAString& aKey)
- {
- EntryType* ent = this->PutEntry(aKey, fallible);
- if (!ent) {
- return nullptr;
- }
- return &ent->mData;
- }
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_MozMap_h
diff --git a/dom/bindings/Record.h b/dom/bindings/Record.h
new file mode 100644
index 000000000..d6ab31699
--- /dev/null
+++ b/dom/bindings/Record.h
@@ -0,0 +1,91 @@
+/* -*- 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 record arguments. Basically an array under the hood.
+ */
+
+#ifndef mozilla_dom_Record_h
+#define mozilla_dom_Record_h
+
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace binding_detail {
+template<typename KeyType, typename ValueType>
+class RecordEntry
+{
+public:
+ RecordEntry()
+ {
+ }
+
+ // Move constructor so we can do Records of Records.
+ RecordEntry(RecordEntry<KeyType, ValueType>&& aOther)
+ : mKey(Move(aOther.mKey)),
+ mValue(Move(aOther.mValue))
+ {
+ }
+
+ KeyType mKey;
+ ValueType mValue;
+};
+
+} // namespace binding_detail
+
+template<typename KeyType, typename ValueType>
+class Record
+{
+public:
+ typedef typename binding_detail::RecordEntry<KeyType, ValueType> EntryType;
+ typedef Record<KeyType, ValueType> SelfType;
+
+ Record()
+ {
+ }
+
+ // Move constructor so we can do Record of Record.
+ Record(SelfType&& aOther) :
+ mEntries(Move(aOther.mEntries))
+ {
+ }
+
+ const nsTArray<EntryType>& Entries() const
+ {
+ return mEntries;
+ }
+
+ nsTArray<EntryType>& Entries()
+ {
+ return mEntries;
+ }
+
+private:
+ nsTArray<EntryType> mEntries;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+template<typename K, typename V>
+class nsDefaultComparator<mozilla::dom::binding_detail::RecordEntry<K, V>, K>
+{
+public:
+ bool Equals(const mozilla::dom::binding_detail::RecordEntry<K, V>& aEntry,
+ const K& aKey) const
+ {
+ return aEntry.mKey == aKey;
+ }
+};
+
+#endif // mozilla_dom_Record_h
diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build
index f1ce9e276..7e1358e9c 100644
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -32,10 +32,10 @@ EXPORTS.mozilla.dom += [
'FakeString.h',
'IterableIterator.h',
'JSSlots.h',
- 'MozMap.h',
'NonRefcountedDOMObject.h',
'Nullable.h',
'PrimitiveConversions.h',
+ 'Record.h',
'RootedDictionary.h',
'SimpleGlobalObject.h',
'StructuredClone.h',
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
index f668d7d62..8c32a8738 100644
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1867,7 +1867,7 @@ class IDLDictionary(IDLObjectWithScope):
if (memberType.nullable() or
memberType.isSequence() or
- memberType.isMozMap()):
+ memberType.isRecord()):
return typeContainsDictionary(memberType.inner, dictionary)
if memberType.isDictionary():
@@ -1988,7 +1988,7 @@ class IDLType(IDLObject):
'callback',
'union',
'sequence',
- 'mozmap'
+ 'record'
)
def __init__(self, location, name):
@@ -2038,7 +2038,7 @@ class IDLType(IDLObject):
def isSequence(self):
return False
- def isMozMap(self):
+ def isRecord(self):
return False
def isArrayBuffer(self):
@@ -2263,8 +2263,8 @@ class IDLNullableType(IDLParameterizedType):
def isSequence(self):
return self.inner.isSequence()
- def isMozMap(self):
- return self.inner.isMozMap()
+ def isRecord(self):
+ return self.inner.isRecord()
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@@ -2321,8 +2321,10 @@ class IDLNullableType(IDLParameterizedType):
return self
def isDistinguishableFrom(self, other):
- if (other.nullable() or (other.isUnion() and other.hasNullableType) or
- other.isDictionary()):
+ if (other.nullable() or
+ other.isDictionary() or
+ (other.isUnion() and
+ (other.hasNullableType or other.hasDictionaryType()))):
# Can't tell which type null should become
return False
return self.inner.isDistinguishableFrom(other)
@@ -2397,34 +2399,38 @@ class IDLSequenceType(IDLParameterizedType):
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isDate() or other.isInterface() or
other.isDictionary() or
- other.isCallback() or other.isMozMap())
+ other.isCallback() or other.isRecord())
-class IDLMozMapType(IDLParameterizedType):
- def __init__(self, location, parameterType):
- assert not parameterType.isVoid()
+class IDLRecordType(IDLParameterizedType):
+ def __init__(self, location, keyType, valueType):
+ assert keyType.isString()
+ assert keyType.isComplete()
+ assert not valueType.isVoid()
+
+ IDLParameterizedType.__init__(self, location, valueType.name, valueType)
+ self.keyType = keyType
- IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
# Need to set self.name up front if our inner type is already complete,
# since in that case our .complete() won't be called.
if self.inner.isComplete():
- self.name = self.inner.name + "MozMap"
+ self.name = self.keyType.name + self.inner.name + "Record"
def __eq__(self, other):
- return isinstance(other, IDLMozMapType) and self.inner == other.inner
+ return isinstance(other, IDLRecordType) and self.inner == other.inner
def __str__(self):
- return self.inner.__str__() + "MozMap"
+ return self.keyType.__str__() + self.inner.__str__() + "Record"
- def isMozMap(self):
+ def isRecord(self):
return True
def tag(self):
- return IDLType.Tags.mozmap
+ return IDLType.Tags.record
def complete(self, scope):
self.inner = self.inner.complete(scope)
- self.name = self.inner.name + "MozMap"
+ self.name = self.keyType.name + self.inner.name + "Record"
return self
def unroll(self):
@@ -2614,8 +2620,8 @@ class IDLTypedefType(IDLType):
def isSequence(self):
return self.inner.isSequence()
- def isMozMap(self):
- return self.inner.isMozMap()
+ def isRecord(self):
+ return self.inner.isRecord()
def isDictionary(self):
return self.inner.isDictionary()
@@ -2798,7 +2804,7 @@ class IDLWrapperType(IDLType):
if self.isEnum():
return (other.isPrimitive() or other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord() or other.isDate())
if self.isDictionary() and other.nullable():
return False
if (other.isPrimitive() or other.isString() or other.isEnum() or
@@ -2820,7 +2826,7 @@ class IDLWrapperType(IDLType):
(self.isNonCallbackInterface() or
other.isNonCallbackInterface()))
if (other.isDictionary() or other.isCallback() or
- other.isMozMap()):
+ other.isRecord()):
return self.isNonCallbackInterface()
# Not much else |other| can be
@@ -3030,17 +3036,17 @@ class IDLBuiltinType(IDLType):
return (other.isNumeric() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord() or other.isDate())
if self.isNumeric():
return (other.isBoolean() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord() or other.isDate())
if self.isString():
return (other.isPrimitive() or other.isInterface() or
other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord() or other.isDate())
if self.isAny():
# Can't tell "any" apart from anything
return False
@@ -3050,7 +3056,7 @@ class IDLBuiltinType(IDLType):
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isInterface() or other.isCallback() or
other.isDictionary() or other.isSequence() or
- other.isMozMap())
+ other.isRecord())
if self.isVoid():
return not other.isVoid()
# Not much else we could be!
@@ -3058,7 +3064,7 @@ class IDLBuiltinType(IDLType):
# Like interfaces, but we know we're not a callback
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate() or
+ other.isSequence() or other.isRecord() or other.isDate() or
(other.isInterface() and (
# ArrayBuffer is distinguishable from everything
# that's not an ArrayBuffer or a callback interface
@@ -3843,6 +3849,9 @@ class IDLConst(IDLInterfaceMember):
if type.isDictionary():
raise WebIDLError("A constant cannot be of a dictionary type",
[self.location])
+ if type.isRecord():
+ raise WebIDLError("A constant cannot be of a record type",
+ [self.location])
self.type = type
self.value = value
@@ -3954,8 +3963,8 @@ class IDLAttribute(IDLInterfaceMember):
if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("A non-cached attribute cannot be of a sequence "
"type", [self.location])
- if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
- raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+ if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a record "
"type", [self.location])
if self.type.isUnion():
for f in self.type.unroll().flatMemberTypes:
@@ -3971,11 +3980,11 @@ class IDLAttribute(IDLInterfaceMember):
"one of its member types's member "
"types, and so on) is a sequence "
"type", [self.location, f.location])
- if f.isMozMap():
+ if f.isRecord():
raise WebIDLError("An attribute cannot be of a union "
"type if one of its member types (or "
"one of its member types's member "
- "types, and so on) is a MozMap "
+ "types, and so on) is a record "
"type", [self.location, f.location])
if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
raise WebIDLError("An attribute with [PutForwards] must have an "
@@ -3989,7 +3998,7 @@ class IDLAttribute(IDLInterfaceMember):
def typeContainsChromeOnlyDictionaryMember(type):
if (type.nullable() or
type.isSequence() or
- type.isMozMap()):
+ type.isRecord()):
return typeContainsChromeOnlyDictionaryMember(type.inner)
if type.isUnion():
@@ -4035,10 +4044,10 @@ class IDLAttribute(IDLInterfaceMember):
[self.location, location])
if self.getExtendedAttribute("Frozen"):
if (not self.type.isSequence() and not self.type.isDictionary() and
- not self.type.isMozMap()):
+ not self.type.isRecord()):
raise WebIDLError("[Frozen] is only allowed on "
"sequence-valued, dictionary-valued, and "
- "MozMap-valued attributes",
+ "record-valued attributes",
[self.location])
if not self.type.unroll().isExposedInAllOf(self.exposureSet):
raise WebIDLError("Attribute returns a type that is not exposed "
@@ -5147,7 +5156,7 @@ class Tokenizer(object):
"Promise": "PROMISE",
"required": "REQUIRED",
"sequence": "SEQUENCE",
- "MozMap": "MOZMAP",
+ "record": "RECORD",
"short": "SHORT",
"unsigned": "UNSIGNED",
"void": "VOID",
@@ -6276,7 +6285,7 @@ class Parser(Tokenizer):
| OCTET
| OPTIONAL
| SEQUENCE
- | MOZMAP
+ | RECORD
| SETTER
| SHORT
| STATIC
@@ -6355,7 +6364,7 @@ class Parser(Tokenizer):
def p_NonAnyType(self, p):
"""
- NonAnyType : PrimitiveOrStringType Null
+ NonAnyType : PrimitiveType Null
| ARRAYBUFFER Null
| SHAREDARRAYBUFFER Null
| OBJECT Null
@@ -6371,6 +6380,12 @@ class Parser(Tokenizer):
p[0] = self.handleNullable(type, p[2])
+ def p_NonAnyTypeStringType(self, p):
+ """
+ NonAnyType : StringType Null
+ """
+ p[0] = self.handleNullable(p[1], p[2])
+
def p_NonAnyTypeSequenceType(self, p):
"""
NonAnyType : SEQUENCE LT Type GT Null
@@ -6391,13 +6406,14 @@ class Parser(Tokenizer):
type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
p[0] = self.handleNullable(type, p[5])
- def p_NonAnyTypeMozMapType(self, p):
+ def p_NonAnyTypeRecordType(self, p):
"""
- NonAnyType : MOZMAP LT Type GT Null
+ NonAnyType : RECORD LT StringType COMMA Type GT Null
"""
- innerType = p[3]
- type = IDLMozMapType(self.getLocation(p, 1), innerType)
- p[0] = self.handleNullable(type, p[5])
+ keyType = p[3]
+ valueType = p[5]
+ type = IDLRecordType(self.getLocation(p, 1), keyType, valueType)
+ p[0] = self.handleNullable(type, p[7])
def p_NonAnyTypeScopedName(self, p):
"""
@@ -6440,7 +6456,7 @@ class Parser(Tokenizer):
def p_ConstType(self, p):
"""
- ConstType : PrimitiveOrStringType Null
+ ConstType : PrimitiveType Null
"""
type = BuiltinTypes[p[1]]
p[0] = self.handleNullable(type, p[2])
@@ -6454,69 +6470,75 @@ class Parser(Tokenizer):
type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
p[0] = self.handleNullable(type, p[2])
- def p_PrimitiveOrStringTypeUint(self, p):
+ def p_PrimitiveTypeUint(self, p):
"""
- PrimitiveOrStringType : UnsignedIntegerType
+ PrimitiveType : UnsignedIntegerType
"""
p[0] = p[1]
- def p_PrimitiveOrStringTypeBoolean(self, p):
+ def p_PrimitiveTypeBoolean(self, p):
"""
- PrimitiveOrStringType : BOOLEAN
+ PrimitiveType : BOOLEAN
"""
p[0] = IDLBuiltinType.Types.boolean
- def p_PrimitiveOrStringTypeByte(self, p):
+ def p_PrimitiveTypeByte(self, p):
"""
- PrimitiveOrStringType : BYTE
+ PrimitiveType : BYTE
"""
p[0] = IDLBuiltinType.Types.byte
- def p_PrimitiveOrStringTypeOctet(self, p):
+ def p_PrimitiveTypeOctet(self, p):
"""
- PrimitiveOrStringType : OCTET
+ PrimitiveType : OCTET
"""
p[0] = IDLBuiltinType.Types.octet
- def p_PrimitiveOrStringTypeFloat(self, p):
+ def p_PrimitiveTypeFloat(self, p):
"""
- PrimitiveOrStringType : FLOAT
+ PrimitiveType : FLOAT
"""
p[0] = IDLBuiltinType.Types.float
- def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ def p_PrimitiveTypeUnrestictedFloat(self, p):
"""
- PrimitiveOrStringType : UNRESTRICTED FLOAT
+ PrimitiveType : UNRESTRICTED FLOAT
"""
p[0] = IDLBuiltinType.Types.unrestricted_float
- def p_PrimitiveOrStringTypeDouble(self, p):
+ def p_PrimitiveTypeDouble(self, p):
"""
- PrimitiveOrStringType : DOUBLE
+ PrimitiveType : DOUBLE
"""
p[0] = IDLBuiltinType.Types.double
- def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ def p_PrimitiveTypeUnrestictedDouble(self, p):
"""
- PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ PrimitiveType : UNRESTRICTED DOUBLE
"""
p[0] = IDLBuiltinType.Types.unrestricted_double
- def p_PrimitiveOrStringTypeDOMString(self, p):
+ def p_StringType(self, p):
+ """
+ StringType : BuiltinStringType
+ """
+ p[0] = BuiltinTypes[p[1]]
+
+ def p_BuiltinStringTypeDOMString(self, p):
"""
- PrimitiveOrStringType : DOMSTRING
+ BuiltinStringType : DOMSTRING
"""
p[0] = IDLBuiltinType.Types.domstring
- def p_PrimitiveOrStringTypeBytestring(self, p):
+ def p_BuiltinStringTypeBytestring(self, p):
"""
- PrimitiveOrStringType : BYTESTRING
+ BuiltinStringType : BYTESTRING
"""
p[0] = IDLBuiltinType.Types.bytestring
- def p_PrimitiveOrStringTypeUSVString(self, p):
+ def p_BuiltinStringTypeUSVString(self, p):
"""
- PrimitiveOrStringType : USVSTRING
+ BuiltinStringType : USVSTRING
"""
p[0] = IDLBuiltinType.Types.usvstring