Fix for bug 756258 (Support union types in new DOM bindings). r=bz.

--HG--
extra : rebase_source : 5352801a4eb5085f73a5b6e78aa238c5c9d44179
This commit is contained in:
Peter Van der Beken 2012-05-18 23:25:47 +02:00
parent 1e00518240
commit 7efab19797
12 changed files with 1289 additions and 91 deletions

View File

@ -863,6 +863,25 @@ public:
Sequence() : AutoFallibleTArray<T, 16>() {}
};
// Class for holding the type of members of a union. The union type has an enum
// to keep track of which of its UnionMembers has been constructed.
template<class T>
class UnionMember {
AlignedStorage2<T> storage;
public:
T& SetValue() {
new (storage.addr()) T();
return *storage.addr();
}
const T& Value() const {
return *storage.addr();
}
void Destroy() {
storage.addr()->~T();
}
};
} // namespace dom
} // namespace mozilla

View File

@ -6,6 +6,7 @@
import os
import string
import operator
from WebIDL import *
from Configuration import NoSuchDescriptorError
@ -310,6 +311,22 @@ class CGIncludeGuard(CGWrapper):
declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
declarePost='\n#endif // %s\n' % define)
def getTypes(descriptor):
"""
Get all argument and return types for all members of the descriptor
"""
members = [m for m in descriptor.interface.members]
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
types = []
for s in signatures:
assert len(s) == 2
(returnType, arguments) = s
types.append(returnType)
types.extend([a.type for a in arguments])
types.extend(a.type for a in members if a.isAttr())
return types
class CGHeaders(CGWrapper):
"""
Generates the appropriate include statements.
@ -340,18 +357,7 @@ class CGHeaders(CGWrapper):
# need to wrap or unwrap them.
bindingHeaders = set()
for d in descriptors:
members = [m for m in d.interface.members]
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
types = []
for s in signatures:
assert len(s) == 2
(returnType, arguments) = s
types.append(returnType)
types.extend([a.type for a in arguments])
attrs = [a for a in members if a.isAttr()]
types.extend([a.type for a in attrs])
types = getTypes(d)
for dictionary in dictionaries:
curDict = dictionary
while curDict:
@ -359,7 +365,10 @@ class CGHeaders(CGWrapper):
curDict = curDict.parent
for t in types:
if t.unroll().isInterface():
if t.unroll().isUnion():
# UnionConversions.h includes UnionTypes.h
bindingHeaders.add("mozilla/dom/UnionConversions.h")
elif t.unroll().isInterface():
if t.unroll().isSpiderMonkeyInterface():
bindingHeaders.add("jsfriendapi.h")
bindingHeaders.add("mozilla/dom/TypedArray.h")
@ -393,6 +402,92 @@ class CGHeaders(CGWrapper):
basename = os.path.basename(decl.filename())
return basename.replace('.webidl', 'Binding.h')
def SortedTuples(l):
"""
Sort a list of tuples based on the first item in the tuple
"""
return sorted(l, key=operator.itemgetter(0))
def SortedDictValues(d):
"""
Returns a list of values from the dict sorted by key.
"""
# Create a list of tuples containing key and value, sorted on key.
d = SortedTuples(d.items())
# We're only interested in the values.
return (i[1] for i in d)
def UnionTypes(descriptors):
"""
Returns a tuple containing a set of header filenames to include, a set of
tuples containing a type declaration and a boolean if the type is a struct
for member types of the unions and a CGList containing CGUnionStructs for
every union.
"""
# Now find all the things we'll need as arguments and return values because
# we need to wrap or unwrap them.
headers = set()
declarations = set()
unionStructs = dict()
for d in descriptors:
if d.interface.isExternal():
continue
for t in getTypes(d):
t = t.unroll()
if t.isUnion():
name = str(t)
if not name in unionStructs:
unionStructs[name] = CGUnionStruct(t, d)
for f in t.flatMemberTypes:
f = f.unroll()
if f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
headers.add("mozilla/dom/TypedArray.h")
else:
typeDesc = d.getDescriptor(f.inner.identifier.name)
if typeDesc is not None:
declarations.add((typeDesc.nativeType, False))
elif f.isDictionary():
declarations.add((f.inner.identifier.name, True))
return (headers, declarations, CGList(SortedDictValues(unionStructs), "\n"))
def UnionConversions(descriptors):
"""
Returns a CGThing to declare all union argument conversion helper structs.
"""
# Now find all the things we'll need as arguments because we
# need to unwrap them.
unionConversions = dict()
for d in descriptors:
if d.interface.isExternal():
continue
def addUnionTypes(type):
if type.isUnion():
type = type.unroll()
name = str(type)
if not name in unionConversions:
unionConversions[name] = CGUnionConversionStruct(type, d)
members = [m for m in d.interface.members]
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
for s in signatures:
assert len(s) == 2
(_, arguments) = s
for a in arguments:
addUnionTypes(a.type)
for m in members:
if m.isAttr() and not m.readonly:
addUnionTypes(m.type)
return CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
post="\n\n")
class Argument():
"""
A class for outputting the type and name of an argument
@ -1272,14 +1367,14 @@ class CastableObjectUnwrapper():
"protoID" : "prototypes::id::" + descriptor.name,
"source" : source,
"target" : target,
"codeOnFailure" : codeOnFailure }
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
def __str__(self):
return string.Template(
"""{
nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target});
if (NS_FAILED(rv)) {
${codeOnFailure}
${codeOnFailure}
}
}""").substitute(self.substitution)
@ -1307,7 +1402,7 @@ class CallbackObjectUnwrapper:
self.substitution = { "nativeType" : descriptor.nativeType,
"source" : source,
"target" : target,
"codeOnFailure" : codeOnFailure }
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define() }
def __str__(self):
if self.descriptor.workers:
@ -1320,7 +1415,7 @@ class CallbackObjectUnwrapper:
XPCCallContext ccx(JS_CALLER, cx);
if (!ccx.IsValid()) {
rv = NS_ERROR_XPC_BAD_CONVERT_JS;
${codeOnFailure}
${codeOnFailure}
}
const nsIID& iid = NS_GET_IID(${nativeType});
@ -1328,14 +1423,14 @@ nsRefPtr<nsXPCWrappedJS> wrappedJS;
rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
NULL, getter_AddRefs(wrappedJS));
if (NS_FAILED(rv) || !wrappedJS) {
${codeOnFailure}
${codeOnFailure}
}
// Use a temp nsCOMPtr for the null-check, because ${target} might be
// OwningNonNull, not an nsCOMPtr.
nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
if (!tmp) {
${codeOnFailure}
${codeOnFailure}
}
${target} = tmp.forget();""").substitute(self.substitution)
@ -1501,6 +1596,205 @@ for (uint32_t i = 0; i < length; ++i) {
descriptorProvider.workers)
return (templateBody, typeName, None, isOptional)
if type.isUnion():
if isMember:
raise TypeError("Can't handle unions as members, we have a "
"holderType")
nullable = type.nullable();
if nullable:
type = type.inner
unionArgumentObj = "${holderName}"
if isOptional or nullable:
unionArgumentObj += ".ref()"
memberTypes = type.flatMemberTypes
interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
if len(interfaceMemberTypes) > 0:
interfaceObject = []
for memberType in interfaceMemberTypes:
if type.isGeckoInterface():
name = memberType.inner.identifier.name
else:
name = memberType.name
interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
else:
interfaceObject = None
arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
if len(arrayObjectMemberTypes) > 0:
assert len(arrayObjectMemberTypes) == 1
memberType = arrayObjectMemberTypes[0]
name = memberType.name
arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
# XXX Now we're supposed to check for an array or a platform object
# that supports indexed properties... skip that last for now. It's a
# bit of a pain.
arrayObject = CGWrapper(CGIndenter(arrayObject),
pre="if (IsArrayLike(cx, &argObj)) {\n",
post="}")
else:
arrayObject = None
dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
if len(dateObjectMemberTypes) > 0:
assert len(dateObjectMemberTypes) == 1
memberType = dateObjectMemberTypes[0]
name = memberType.name
dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
"done = true;" % (unionArgumentObj, name))
dateObject = CGWrapper(CGIndenter(dateObject),
pre="if (JS_ObjectIsDate(cx, &argObj)) {\n",
post="\n}")
else:
dateObject = None
callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
if len(callbackMemberTypes) > 0:
assert len(callbackMemberTypes) == 1
memberType = callbackMemberTypes[0]
name = memberType.name
callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
else:
callbackObject = None
dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
if len(dictionaryMemberTypes) > 0:
raise TypeError("No support for unwrapping dictionaries as member "
"of a union")
else:
dictionaryObject = None
if callbackObject or dictionaryObject:
nonPlatformObject = CGList([callbackObject, dictionaryObject], "\n")
nonPlatformObject = CGWrapper(CGIndenter(nonPlatformObject),
pre="if (!IsPlatformObject(cx, &argObj)) {\n",
post="\n}")
else:
nonPlatformObject = None
objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
if len(objectMemberTypes) > 0:
object = CGGeneric("%s.SetToObject(&argObj);\n"
"done = true;" % unionArgumentObj)
else:
object = None
hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
if hasObjectTypes:
# If we try more specific object types first then we need to check
# whether that succeeded before converting to object.
if object and (interfaceObject or arrayObject or dateObject or nonPlatformObject):
object = CGWrapper(CGIndenter(object), pre="if (!done) {\n",
post=("\n}"))
if arrayObject or dateObject or nonPlatformObject:
# An object can be both an array object and not a platform
# object, but we shouldn't have both in the union's members
# because they are not distinguishable.
assert not (arrayObject and nonPlatformObject)
templateBody = CGList([arrayObject, dateObject, nonPlatformObject], " else ")
else:
templateBody = None
if interfaceObject:
if templateBody:
templateBody = CGList([templateBody, object], "\n")
templateBody = CGWrapper(CGIndenter(templateBody),
pre="if (!done) {\n", post=("\n}"))
templateBody = CGList([interfaceObject, templateBody], "\n")
else:
templateBody = CGList([templateBody, object], "\n")
if any([arrayObject, dateObject, nonPlatformObject, object]):
templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
templateBody = CGWrapper(CGIndenter(templateBody),
pre="if (${val}.isObject()) {\n",
post="\n}")
else:
templateBody = CGGeneric()
otherMemberTypes = filter(lambda t: t.isString() or t.isEnum(),
memberTypes)
otherMemberTypes.extend(t for t in memberTypes if t.isPrimitive())
if len(otherMemberTypes) > 0:
assert len(otherMemberTypes) == 1
memberType = otherMemberTypes[0]
if memberType.isEnum():
name = memberType.inner.identifier.name
else:
name = memberType.name
other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
if hasObjectTypes:
other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
if object:
join = " else "
else:
other = CGWrapper(other, pre="if (!done) ")
join = "\n"
templateBody = CGList([templateBody, other], join)
else:
other = None
templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
throw = CGGeneric("if (failed) {\n"
" return false;\n"
"}\n"
"if (!done) {\n"
" return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
"}" % toStringBool(descriptorProvider.workers))
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
typeName = type.name
argumentTypeName = typeName + "Argument"
if nullable:
typeName = "Nullable<" + typeName + " >"
if isOptional:
nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
else:
nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
typeName = "const " + typeName
def handleNull(templateBody, setToNullVar):
null = CGGeneric("if (${val}.isNullOrUndefined()) {\n"
" %s.SetNull();\n"
"}" % setToNullVar)
templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
return CGList([null, templateBody], " else ")
if type.hasNullableType:
templateBody = handleNull(templateBody, unionArgumentObj)
declType = CGGeneric(typeName)
holderType = CGGeneric(argumentTypeName)
if isOptional:
mutableDecl = nonConstDecl + ".Value()"
declType = CGWrapper(declType, pre="const Optional<", post=" >")
holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
constructDecl = CGGeneric(nonConstDecl + ".Construct();")
if nullable:
constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
else:
constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
else:
mutableDecl = nonConstDecl
constructDecl = None
if nullable:
holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
else:
constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
holderType = None
templateBody = CGList([constructHolder, templateBody], "\n")
if nullable:
templateBody = handleNull(templateBody, mutableDecl)
templateBody = CGList([constructDecl, templateBody], "\n")
return templateBody.define(), declType, holderType, False
if type.isGeckoInterface():
descriptor = descriptorProvider.getDescriptor(
type.unroll().inner.identifier.name)
@ -1641,10 +1935,10 @@ for (uint32_t i = 0; i < length; ++i) {
declType = "NonNull<" + name + ">"
template = (
"if (!JS_Is%s(&${val}.toObject(), cx)) {\n"
" %s" # No newline here because onFailure() handles that
"%s" # No newline here because onFailure() handles that
"}\n"
"%s.%s(cx, &${val}.toObject());\n" %
(jsname, onFailure(failureCode, descriptorProvider.workers).define(),
(jsname, CGIndenter(onFailure(failureCode, descriptorProvider.workers)).define(),
constructLoc, constructMethod))
nullableTarget = ""
if type.nullable():
@ -2203,7 +2497,9 @@ def wrapForType(type, descriptorProvider, templateValues):
def typeNeedsCx(type):
return (type is not None and
(type.isCallback() or type.isAny() or type.isObject()))
(type.isCallback() or type.isAny() or type.isObject() or
(type.isUnion() and
any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes))))
# Returns a CGThing containing the type of the return value, or None
# if there is no need for a return value.
@ -2906,6 +3202,309 @@ class CGEnum(CGThing):
""" % (len(self.enum.values()) + 1,
",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()]))
def getUnionAccessorSignatureType(type, descriptorProvider):
"""
Returns the types that are used in the getter and setter signatures for
union types
"""
if type.isArray():
raise TypeError("Can't handle array arguments yet")
if type.isSequence():
nullable = type.nullable();
if nullable:
type = type.inner.inner
else:
type = type.inner
(elementTemplate, elementDeclType,
elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
type, descriptorProvider, isSequenceMember=True)
typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >&")
if nullable:
typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
return typeName
if type.isUnion():
typeName = CGGeneric(type.name)
if type.nullable():
typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
return typeName
if type.isGeckoInterface():
descriptor = descriptorProvider.getDescriptor(
type.unroll().inner.identifier.name)
typeName = CGGeneric(descriptor.nativeType)
# Allow null pointers for nullable types and old-binding classes
if type.nullable() or type.unroll().inner.isExternal():
typeName = CGWrapper(typeName, post="*")
else:
typeName = CGWrapper(typeName, post="&")
return typeName
if type.isSpiderMonkeyInterface():
typeName = CGGeneric(type.name)
if type.nullable():
typeName = CGWrapper(typeName, post="*")
else:
typeName = CGWrapper(typeName, post="&")
return typeName
if type.isString():
return CGGeneric("const nsAString&")
if type.isEnum():
if type.nullable():
raise TypeError("We don't support nullable enumerated arguments or "
"union members yet")
return CGGeneric(type.inner.identifier.name)
if type.isCallback():
return CGGeneric("JSObject*")
if type.isAny():
return CGGeneric("JS::Value")
if type.isObject():
typeName = CGGeneric("JSObject")
if type.nullable():
typeName = CGWrapper(typeName, post="*")
else:
typeName = CGWrapper(typeName, post="&")
return typeName
if not type.isPrimitive():
raise TypeError("Need native type for argument type '%s'" % str(type))
typeName = CGGeneric(builtinNames[type.tag()])
if type.nullable():
typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
return typeName
def getUnionTypeTemplateVars(type, descriptorProvider):
# For dictionaries and sequences we need to pass None as the failureCode
# for getJSToNativeConversionTemplate.
if type.isDictionary() or type.isSequence():
raise TypeError("Can't handle dictionaries or sequences in unions")
if type.isGeckoInterface():
name = type.inner.identifier.name
elif type.isEnum():
name = type.inner.identifier.name
elif type.isArray() or type.isSequence():
name = str(type)
else:
name = type.name
tryNextCode = """tryNext = true;
return true;"""
if type.isGeckoInterface():
tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) {
mUnion.Destroy%s();
}""" % name) + tryNextCode
(template, declType, holderType,
dealWithOptional) = getJSToNativeConversionTemplate(
type, descriptorProvider, failureCode=tryNextCode,
isDefinitelyObject=True)
# This is ugly, but UnionMember needs to call a constructor with no
# arguments so the type can't be const.
structType = declType.define()
if structType.startswith("const "):
structType = structType[6:]
externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
if type.isObject():
setter = CGGeneric("void SetToObject(JSObject* obj)\n"
"{\n"
" mUnion.mValue.mObject.SetValue() = obj;\n"
" mUnion.mType = mUnion.eObject;\n"
"}")
else:
jsConversion = string.Template(template).substitute(
{
"val": "value",
"valPtr": "pvalue",
"declName": "SetAs" + name + "()",
"holderName": "m" + name + "Holder"
}
)
jsConversion = CGWrapper(CGGeneric(jsConversion),
post="\n"
"return true;")
setter = CGWrapper(CGIndenter(jsConversion),
pre="bool TrySetTo" + name + "(JSContext* cx, const JS::Value& value, JS::Value* pvalue, bool& tryNext)\n"
"{\n"
" tryNext = false;\n",
post="\n"
"}")
return {
"name": name,
"structType": structType,
"externalType": externalType,
"setter": CGIndenter(setter).define(),
"holderType": holderType.define() if holderType else None
}
def mapTemplate(template, templateVarArray):
return map(lambda v: string.Template(template).substitute(v),
templateVarArray)
class CGUnionStruct(CGThing):
def __init__(self, type, descriptorProvider):
CGThing.__init__(self)
self.type = type.unroll()
self.descriptorProvider = descriptorProvider
def declare(self):
templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
self.type.flatMemberTypes)
callDestructors = []
enumValues = []
methods = []
if self.type.hasNullableType:
callDestructors.append(" case eNull:\n"
" break;")
enumValues.append("eNull")
methods.append(""" bool IsNull() const
{
return mType == eNull;
}""")
destructorTemplate = """ void Destroy${name}()
{
MOZ_ASSERT(Is${name}(), "Wrong type!");
mValue.m${name}.Destroy();
mType = eUninitialized;
}"""
destructors = mapTemplate(destructorTemplate, templateVars)
callDestructors.extend(mapTemplate(" case e${name}:\n"
" Destroy${name}();\n"
" break;", templateVars))
enumValues.extend(mapTemplate("e${name}", templateVars))
methodTemplate = """ bool Is${name}() const
{
return mType == e${name};
}
${externalType} GetAs${name}() const
{
MOZ_ASSERT(Is${name}(), "Wrong type!");
// The cast to ${externalType} is needed to work around a bug in Apple's
// clang compiler, for some reason it doesn't call |S::operator T&| when
// casting S<T> to T& and T is forward declared.
return (${externalType})mValue.m${name}.Value();
}
${structType}& SetAs${name}()
{
mType = e${name};
return mValue.m${name}.SetValue();
}"""
methods.extend(mapTemplate(methodTemplate, templateVars))
values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
return string.Template("""
class ${structName} {
public:
${structName}() : mType(eUninitialized)
{
}
~${structName}()
{
switch (mType) {
${callDestructors}
case eUninitialized:
break;
}
}
${methods}
private:
friend class ${structName}Argument;
${destructors}
enum Type {
eUninitialized,
${enumValues}
};
union Value {
${values}
};
Type mType;
Value mValue;
};
""").substitute(
{
"structName": self.type.__str__(),
"callDestructors": "\n".join(callDestructors),
"destructors": "\n".join(destructors),
"methods": "\n\n".join(methods),
"enumValues": ",\n ".join(enumValues),
"values": "\n ".join(values),
})
def define(self):
return """
"""
class CGUnionConversionStruct(CGThing):
def __init__(self, type, descriptorProvider):
CGThing.__init__(self)
self.type = type.unroll()
self.descriptorProvider = descriptorProvider
def declare(self):
setters = []
if self.type.hasNullableType:
setters.append(""" bool SetNull()
{
mUnion.mType = mUnion.eNull;
return true;
}""")
templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
self.type.flatMemberTypes)
structName = self.type.__str__()
setters.extend(mapTemplate("${setter}", templateVars))
private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}()
{
mUnion.mType = mUnion.e${name};
return mUnion.mValue.m${name}.SetValue();
}""", templateVars))
private += "\n\n"
holders = filter(lambda v: v["holderType"] is not None, templateVars)
if len(holders) > 0:
private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders))
private += "\n\n"
private += " " + structName + "& mUnion;"
return string.Template("""
class ${structName}Argument {
public:
${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion))
{
}
${setters}
private:
${private}
};
""").substitute({"structName": structName,
"setters": "\n\n".join(setters),
"private": private
})
def define(self):
return """
"""
class ClassItem:
""" Use with CGClass """
def __init__(self, name, visibility):
@ -3915,3 +4514,69 @@ struct PrototypeIDMap;
# Done.
return curr
@staticmethod
def UnionTypes(config):
(includes, declarations, unions) = UnionTypes(config.getDescriptors())
includes.add("mozilla/dom/BindingUtils.h")
# Wrap all of that in our namespaces.
curr = CGNamespace.build(['mozilla', 'dom'], unions)
curr = CGWrapper(curr, post='\n')
namespaces = []
stack = [CGList([])]
for (clazz, isStruct) in SortedTuples(declarations):
elements = clazz.split("::")
clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
i = 0
if len(elements) > 0:
common = min(len(namespaces), len(elements))
while i < common and namespaces[i] == elements[i]:
i += 1
# pop all the namespaces that should be closed
namespaces = namespaces[:i]
# add all the namespaces that should be opened
for j, namespace in enumerate(elements[i:]):
namespaces.append(namespace)
# every CGNamespace that we add holds a CGList
list = CGList([])
# add the new namespace to the list on top of the stack
stack[i + j].append(CGNamespace(namespace, list))
# set the top of the namespace stack to the list of the new
# namespace
stack[i + j + 1:] = [list]
stack[len(elements)].append(clazz)
curr = CGList([stack[0], curr], "\n")
curr = CGHeaders([], [], includes, [], curr)
# Add include guards.
curr = CGIncludeGuard('UnionTypes', curr)
# Done.
return curr
@staticmethod
def UnionConversions(config):
unions = UnionConversions(config.getDescriptors())
# Wrap all of that in our namespaces.
curr = CGNamespace.build(['mozilla', 'dom'], unions)
curr = CGWrapper(curr, post='\n')
curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h"], [], curr)
# Add include guards.
curr = CGIncludeGuard('UnionConversions', curr)
# Done.
return curr

View File

@ -77,5 +77,8 @@ def main():
generate_file(config, 'RegisterBindings', 'declare')
generate_file(config, 'RegisterBindings', 'define')
generate_file(config, 'UnionTypes', 'declare')
generate_file(config, 'UnionConversions', 'declare')
if __name__ == '__main__':
main()

View File

@ -45,6 +45,8 @@ globalgen_targets := \
PrototypeList.h \
RegisterBindings.h \
RegisterBindings.cpp \
UnionTypes.h \
UnionConversions.h \
$(NULL)
CPPSRCS = \
@ -66,6 +68,8 @@ EXPORTS_$(binding_include_path) = \
Nullable.h \
TypedArray.h \
BindingUtils.h \
UnionTypes.h \
UnionConversions.h \
$(exported_binding_headers) \
$(NULL)

View File

@ -155,6 +155,9 @@ class IDLObject(object):
def isDictionary(self):
return False;
def isUnion(self):
return False
def getUserData(self, key, default):
return self.userData.get(key, default)
@ -799,7 +802,8 @@ class IDLType(IDLObject):
'interface',
'dictionary',
'enum',
'callback'
'callback',
'union'
)
def __init__(self, location, name):
@ -808,7 +812,7 @@ class IDLType(IDLObject):
self.builtin = False
def __eq__(self, other):
return other and self.name == other.name and self.builtin == other.builtin
return other and self.builtin == other.builtin and self.name == other.name
def __ne__(self, other):
return not self == other
@ -1008,6 +1012,9 @@ class IDLNullableType(IDLType):
def isEnum(self):
return self.inner.isEnum()
def isUnion(self):
return self.inner.isUnion()
def tag(self):
return self.inner.tag()
@ -1020,6 +1027,10 @@ class IDLNullableType(IDLType):
def complete(self, scope):
self.inner = self.inner.complete(scope)
if self.inner.isUnion() and self.inner.hasNullableType:
raise WebIDLError("The inner type of a nullable type must not be a "
"union type that itself has a nullable type as a "
"member type", self.location)
self.name = self.inner.name
return self
@ -1027,7 +1038,7 @@ class IDLNullableType(IDLType):
return self.inner.unroll()
def isDistinguishableFrom(self, other):
if other.nullable():
if other.nullable() or (other.isUnion() and other.hasNullableType):
# Can't tell which type null should become
return False
return self.inner.isDistinguishableFrom(other)
@ -1097,6 +1108,93 @@ class IDLSequenceType(IDLType):
other.isDictionary() or other.isDate() or
other.isNonCallbackInterface())
class IDLUnionType(IDLType):
def __init__(self, location, memberTypes):
IDLType.__init__(self, location, "")
self.memberTypes = memberTypes
self.hasNullableType = False
self.flatMemberTypes = None
self.builtin = False
def __eq__(self, other):
return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
def isVoid(self):
return False
def isUnion(self):
return True
def tag(self):
return IDLType.Tags.union
def resolveType(self, parentScope):
assert isinstance(parentScope, IDLScope)
for t in self.memberTypes:
t.resolveType(parentScope)
def isComplete(self):
return self.flatMemberTypes is not None
def complete(self, scope):
def typeName(type):
if isinstance(type, IDLNullableType):
return typeName(type.inner) + "OrNull"
if isinstance(type, IDLWrapperType):
return typeName(type._identifier.object())
if isinstance(type, IDLObjectWithIdentifier):
return typeName(type.identifier)
if isinstance(type, IDLType) and (type.isArray() or type.isSequence()):
return str(type)
return type.name
for (i, type) in enumerate(self.memberTypes):
if not type.isComplete():
self.memberTypes[i] = type.complete(scope)
self.name = "Or".join(typeName(type) for type in self.memberTypes)
self.flatMemberTypes = list(self.memberTypes)
i = 0
while i < len(self.flatMemberTypes):
if self.flatMemberTypes[i].nullable():
if self.hasNullableType:
raise WebIDLError("Can't have more than one nullable types in a union",
nullableType.location,
extraLocation=self.flatMemberTypes[i].location)
self.hasNullableType = True
nullableType = self.flatMemberTypes[i]
self.flatMemberTypes[i] = self.flatMemberTypes[i].inner
continue
if self.flatMemberTypes[i].isUnion():
self.flatMemberTypes[i:i + 1] = self.flatMemberTypes[i].memberTypes
continue
i += 1
for (i, t) in enumerate(self.flatMemberTypes[:-1]):
for u in self.flatMemberTypes[i + 1:]:
if not t.isDistinguishableFrom(u):
raise WebIDLError("Flat member types of a union should be "
"distinguishable, " + str(t) + " is not "
"distinguishable from " + str(u),
t.location, extraLocation=u.location)
return self
def isDistinguishableFrom(self, other):
if self.hasNullableType and other.nullable():
# Can't tell which type null should become
return False
if other.isUnion():
otherTypes = other.unroll().memberTypes
else:
otherTypes = [other]
# For every type in otherTypes, check that it's distinguishable from
# every type in our types
for u in otherTypes:
if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
return False
return True
class IDLArrayType(IDLType):
def __init__(self, location, parameterType):
assert not parameterType.isVoid()
@ -1640,7 +1738,7 @@ class IDLNullValue(IDLObject):
self.value = None
def coerceToType(self, type, location):
if not isinstance(type, IDLNullableType):
if not isinstance(type, IDLNullableType) and not (type.isUnion() and type.hasNullableType):
raise WebIDLError("Cannot coerce null value to type %s." % type,
location)
@ -1733,11 +1831,29 @@ class IDLAttribute(IDLInterfaceMember):
assert not isinstance(t, IDLUnresolvedType)
assert not isinstance(t.name, IDLUnresolvedIdentifier)
if t.isDictionary():
raise WebIDLError("An attribute cannot be of a dictionary type",
self.location)
self.type = t
if self.type.isDictionary():
raise WebIDLError("An attribute cannot be of a dictionary type",
self.location)
if self.type.isSequence():
raise WebIDLError("An attribute cannot be of a sequence type",
self.location)
if self.type.isUnion():
for f in self.type.flatMemberTypes:
if f.isDictionary():
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 dictionary "
"type", self.location)
if f.isSequence():
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 sequence "
"type", self.location)
def validate(self):
pass
@ -2241,7 +2357,8 @@ class Tokenizer(object):
"=": "EQUALS",
"<": "LT",
">": "GT",
"ArrayBuffer": "ARRAYBUFFER"
"ArrayBuffer": "ARRAYBUFFER",
"or": "OR"
}
tokens.extend(keywords.values())
@ -2581,7 +2698,7 @@ class Parser(Tokenizer):
def p_Attribute(self, p):
"""
Attribute : Inherit ReadOnly ATTRIBUTE AttributeType IDENTIFIER SEMICOLON
Attribute : Inherit ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
"""
location = self.getLocation(p, 3)
inherit = p[1]
@ -2906,7 +3023,7 @@ class Parser(Tokenizer):
def p_ExceptionField(self, p):
"""
ExceptionField : AttributeType IDENTIFIER SEMICOLON
ExceptionField : Type IDENTIFIER SEMICOLON
"""
pass
@ -3010,21 +3127,89 @@ class Parser(Tokenizer):
"""
pass
def p_TypeAttributeType(self, p):
def p_TypeSingleType(self, p):
"""
Type : AttributeType
Type : SingleType
"""
p[0] = p[1]
def p_TypeSequenceType(self, p):
def p_TypeUnionType(self, p):
"""
Type : SequenceType
Type : UnionType TypeSuffix
"""
p[0] = self.handleModifiers(p[1], p[2])
def p_SingleTypeNonAnyType(self, p):
"""
SingleType : NonAnyType
"""
p[0] = p[1]
def p_SequenceType(self, p):
def p_SingleTypeAnyType(self, p):
"""
SequenceType : SEQUENCE LT Type GT Null
SingleType : ANY TypeSuffixStartingWithArray
"""
p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.any], p[2])
def p_UnionType(self, p):
"""
UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
"""
types = [p[2], p[4]]
types.extend(p[5])
p[0] = IDLUnionType(self.getLocation(p, 1), types)
def p_UnionMemberTypeNonAnyType(self, p):
"""
UnionMemberType : NonAnyType
"""
p[0] = p[1]
def p_UnionMemberTypeArrayOfAny(self, p):
"""
UnionMemberTypeArrayOfAny : ANY LBRACKET RBRACKET
"""
p[0] = IDLArrayType(self.getLocation(p, 2),
BuiltinTypes[IDLBuiltinType.Types.any])
def p_UnionMemberType(self, p):
"""
UnionMemberType : UnionType TypeSuffix
| UnionMemberTypeArrayOfAny TypeSuffix
"""
p[0] = self.handleModifiers(p[1], p[2])
def p_UnionMemberTypes(self, p):
"""
UnionMemberTypes : OR UnionMemberType UnionMemberTypes
"""
p[0] = [p[2]]
p[0].extend(p[3])
def p_UnionMemberTypesEmpty(self, p):
"""
UnionMemberTypes :
"""
p[0] = []
def p_NonAnyType(self, p):
"""
NonAnyType : PrimitiveOrStringType TypeSuffix
| ARRAYBUFFER TypeSuffix
| OBJECT TypeSuffix
"""
if p[1] == "object":
type = BuiltinTypes[IDLBuiltinType.Types.object]
elif p[1] == "ArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
else:
type = BuiltinTypes[p[1]]
p[0] = self.handleModifiers(type, p[2])
def p_NonAnyTypeSequenceType(self, p):
"""
NonAnyType : SEQUENCE LT Type GT Null
"""
innerType = p[3]
type = IDLSequenceType(self.getLocation(p, 1), innerType)
@ -3032,36 +3217,9 @@ class Parser(Tokenizer):
type = IDLNullableType(self.getLocation(p, 5), type)
p[0] = type
def p_AttributeTypePrimitive(self, p):
def p_NonAnyTypeScopedName(self, p):
"""
AttributeType : PrimitiveOrStringType TypeSuffix
| ARRAYBUFFER TypeSuffix
| OBJECT TypeSuffix
| ANY TypeSuffixStartingWithArray
"""
if p[1] == "object":
type = BuiltinTypes[IDLBuiltinType.Types.object]
elif p[1] == "any":
type = BuiltinTypes[IDLBuiltinType.Types.any]
elif p[1] == "ArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
else:
type = BuiltinTypes[p[1]]
for (modifier, modifierLocation) in p[2]:
assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
modifier == IDLMethod.TypeSuffixModifier.Brackets
if modifier == IDLMethod.TypeSuffixModifier.QMark:
type = IDLNullableType(modifierLocation, type)
elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
type = IDLArrayType(modifierLocation, type)
p[0] = type
def p_AttributeTypeScopedName(self, p):
"""
AttributeType : ScopedName TypeSuffix
NonAnyType : ScopedName TypeSuffix
"""
assert isinstance(p[1], IDLUnresolvedIdentifier)
@ -3074,34 +3232,17 @@ class Parser(Tokenizer):
type = obj
else:
type = IDLWrapperType(self.getLocation(p, 1), p[1])
for (modifier, modifierLocation) in p[2]:
assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
modifier == IDLMethod.TypeSuffixModifier.Brackets
if modifier == IDLMethod.TypeSuffixModifier.QMark:
type = IDLNullableType(modifierLocation, type)
elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
type = IDLArrayType(modifierLocation, type)
p[0] = type
p[0] = self.handleModifiers(type, p[2])
return
except:
pass
type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
p[0] = self.handleModifiers(type, p[2])
for (modifier, modifierLocation) in p[2]:
assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
modifier == IDLMethod.TypeSuffixModifier.Brackets
if modifier == IDLMethod.TypeSuffixModifier.QMark:
type = IDLNullableType(modifierLocation, type)
elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
type = IDLArrayType(modifierLocation, type)
p[0] = type
def p_AttributeTypeDate(self, p):
def p_NonAnyTypeDate(self, p):
"""
AttributeType : DATE TypeSuffix
NonAnyType : DATE TypeSuffix
"""
assert False
pass
@ -3344,6 +3485,19 @@ class Parser(Tokenizer):
typedef = IDLTypedefType(BuiltinLocation("<builtin type>"), builtin, name)
typedef.resolve(scope)
@ staticmethod
def handleModifiers(type, modifiers):
for (modifier, modifierLocation) in modifiers:
assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
modifier == IDLMethod.TypeSuffixModifier.Brackets
if modifier == IDLMethod.TypeSuffixModifier.QMark:
type = IDLNullableType(modifierLocation, type)
elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
type = IDLArrayType(modifierLocation, type)
return type
def parse(self, t, filename=None):
self.lexer.input(t)

View File

@ -2,8 +2,8 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
interface AttrSquenceType {
attribute sequence<bool> foo;
interface AttrSequenceType {
attribute sequence<object> foo;
};
""")
@ -11,4 +11,57 @@ def WebIDLTest(parser, harness):
except:
threw = True
harness.ok(threw, "Should have thrown.")
harness.ok(threw, "Attribute type must not be a sequence type")
parser.reset()
threw = False
try:
parser.parse("""
interface AttrUnionWithSequenceType {
attribute (sequence<object> or DOMString) foo;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Attribute type must not be a union with a sequence member type")
parser.reset()
threw = False
try:
parser.parse("""
interface AttrNullableUnionWithSequenceType {
attribute (sequence<object>? or DOMString) foo;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Attribute type must not be a union with a nullable sequence "
"member type")
parser.reset()
threw = False
try:
parser.parse("""
interface AttrUnionWithUnionWithSequenceType {
attribute ((sequence<object> or DOMString) or AttrUnionWithUnionWithSequenceType) foo;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Attribute type must not be a union type with a union member "
"type that has a sequence member type")

View File

@ -11,6 +11,8 @@ def WebIDLTest(parser, harness):
// Bit of a pain to get things that have dictionary types
void passDict(Dict arg);
void passFoo(Foo arg);
void passNullableUnion((object? or DOMString) arg);
void passNullable(Foo? arg);
};
""")
results = parser.finish()
@ -19,6 +21,8 @@ def WebIDLTest(parser, harness):
harness.ok(iface.isInterface(), "Should have interface")
dictMethod = iface.members[0]
ifaceMethod = iface.members[1]
nullableUnionMethod = iface.members[2]
nullableIfaceMethod = iface.members[3]
dictType = firstArgType(dictMethod)
ifaceType = firstArgType(ifaceMethod)
@ -32,6 +36,20 @@ def WebIDLTest(parser, harness):
harness.ok(not ifaceType.isDistinguishableFrom(dictType),
"Callback interface not distinguishable from dictionary")
nullableUnionType = firstArgType(nullableUnionMethod)
nullableIfaceType = firstArgType(nullableIfaceMethod)
harness.ok(nullableUnionType.isUnion(), "Should have union type");
harness.ok(nullableIfaceType.isInterface(), "Should have interface type");
harness.ok(nullableIfaceType.nullable(), "Should have nullable type");
harness.ok(not nullableUnionType.isDistinguishableFrom(nullableIfaceType),
"Nullable type not distinguishable from union with nullable "
"member type")
harness.ok(not nullableIfaceType.isDistinguishableFrom(nullableUnionType),
"Union with nullable member type not distinguishable from "
"nullable type")
parser = parser.reset()
parser.parse("""
interface TestIface {

View File

@ -0,0 +1,169 @@
import WebIDL
import itertools
import string
# We'd like to use itertools.chain but it's 2.6 or higher.
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
# We'd like to use itertools.combinations but it's 2.6 or higher.
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
return
indices = range(r)
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != i + n - r:
break
else:
return
indices[i] += 1
for j in range(i+1, r):
indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices)
# We'd like to use itertools.combinations_with_replacement but it's 2.7 or
# higher.
def combinations_with_replacement(iterable, r):
# combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
pool = tuple(iterable)
n = len(pool)
if not n and r:
return
indices = [0] * r
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != n - 1:
break
else:
return
indices[i:] = [indices[i] + 1] * (r - i)
yield tuple(pool[i] for i in indices)
def WebIDLTest(parser, harness):
types = ["float",
"double",
"short",
"unsigned short",
"long",
"unsigned long",
"long long",
"unsigned long long",
"boolean",
"byte",
"octet",
"DOMString",
#"sequence<float>",
"object",
"ArrayBuffer",
#"Date",
"TestInterface1",
"TestInterface2"]
testPre = """
interface TestInterface1 {
};
interface TestInterface2 {
};
"""
interface = testPre + """
interface PrepareForTest {
"""
for (i, type) in enumerate(types):
interface += string.Template("""
readonly attribute ${type} attr${i};
""").substitute(i=i, type=type)
interface += """
};
"""
parser.parse(interface)
results = parser.finish()
iface = results[2]
parser = parser.reset()
def typesAreDistinguishable(t):
return all(u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
def typesAreNotDistinguishable(t):
return any(not u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
def unionTypeName(t):
if len(t) > 2:
t[0:2] = [unionTypeName(t[0:2])]
return "(" + " or ".join(t) + ")"
# typeCombinations is an iterable of tuples containing the name of the type
# as a string and the parsed IDL type.
def unionTypes(typeCombinations, predicate):
for c in typeCombinations:
if predicate(t[1] for t in c):
yield unionTypeName([t[0] for t in c])
# We limit invalid union types with a union member type to the subset of 3
# types with one invalid combination.
# typeCombinations is an iterable of tuples containing the name of the type
# as a string and the parsed IDL type.
def invalidUnionWithUnion(typeCombinations):
for c in typeCombinations:
if (typesAreNotDistinguishable((c[0][1], c[1][1])) and
typesAreDistinguishable((c[1][1], c[2][1])) and
typesAreDistinguishable((c[0][1], c[2][1]))):
yield unionTypeName([t[0] for t in c])
# Create a list of tuples containing the name of the type as a string and
# the parsed IDL type.
types = zip(types, (a.type for a in iface.members))
validUnionTypes = chain(unionTypes(combinations(types, 2), typesAreDistinguishable),
unionTypes(combinations(types, 3), typesAreDistinguishable))
invalidUnionTypes = chain(unionTypes(combinations_with_replacement(types, 2), typesAreNotDistinguishable),
invalidUnionWithUnion(combinations(types, 3)))
interface = testPre + """
interface TestUnion {
"""
for (i, type) in enumerate(validUnionTypes):
interface += string.Template("""
void method${i}(${type} arg);
${type} returnMethod${i}();
attribute ${type} attr${i};
void arrayMethod${i}(${type}[] arg);
${type}[] arrayReturnMethod${i}();
attribute ${type}[] arrayAttr${i};
void optionalMethod${i}(${type}? arg);
""").substitute(i=i, type=type)
interface += """
};
"""
parser.parse(interface)
results = parser.finish()
parser = parser.reset()
for invalid in invalidUnionTypes:
interface = testPre + string.Template("""
interface TestUnion {
void method(${type} arg);
};
""").substitute(type=invalid)
threw = False
try:
parser.parse(interface)
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()

View File

@ -0,0 +1,14 @@
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
interface AnyNotInUnion {
void foo((any or DOMString) arg);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@ -0,0 +1,53 @@
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
interface OneNullableInUnion {
void foo((object? or DOMString?) arg);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Two nullable member types of a union should have thrown.")
parser.reset()
threw = False
try:
parser.parse("""
interface NullableInNullableUnion {
void foo((object? or DOMString)? arg);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"A nullable union type with a nullable member type should have "
"thrown.")
parser.reset()
threw = False
try:
parser.parse("""
interface NullableInUnionNullableUnionHelper {
};
interface NullableInUnionNullableUnion {
void foo(((object? or DOMString) or NullableInUnionNullableUnionHelper)? arg);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"A nullable union type with a nullable member type should have "
"thrown.")

View File

@ -14,6 +14,7 @@
#include "nsCOMPtr.h"
// We don't export TestCodeGenBinding.h, but it's right in our parent dir.
#include "../TestCodeGenBinding.h"
#include "mozilla/dom/UnionTypes.h"
namespace mozilla {
namespace dom {
@ -300,6 +301,7 @@ public:
void PassOptionalAny(JSContext*, const Optional<JS::Value>&, ErrorResult&);
JS::Value ReceiveAny(JSContext*, ErrorResult&);
// object types
void PassObject(JSContext*, JSObject&, ErrorResult&);
void PassNullableObject(JSContext*, JSObject*, ErrorResult&);
void PassOptionalObject(JSContext*, const Optional<NonNull<JSObject> >&, ErrorResult&);
@ -308,6 +310,33 @@ public:
JSObject* ReceiveObject(JSContext*, ErrorResult&);
JSObject* ReceiveNullableObject(JSContext*, ErrorResult&);
// Union types
void PassUnion(JSContext*, const ObjectOrLong& arg, ErrorResult&);
void PassUnionWithNullable(JSContext*, const ObjectOrNullOrLong& arg, ErrorResult&)
{
ObjectOrLong returnValue;
if (arg.IsNull()) {
} else if (arg.IsObject()) {
JSObject& obj = (JSObject&)arg.GetAsObject();
JS_GetClass(&obj);
//returnValue.SetAsObject(&obj);
} else {
int32_t i = arg.GetAsLong();
i += 1;
}
}
void PassNullableUnion(JSContext*, const Nullable<ObjectOrLong>&, ErrorResult&);
void PassOptionalUnion(JSContext*, const Optional<ObjectOrLong>&, ErrorResult&);
void PassOptionalNullableUnion(JSContext*, const Optional<Nullable<ObjectOrLong> >&, ErrorResult&);
void PassOptionalNullableUnionWithDefaultValue(JSContext*, const Nullable<ObjectOrLong>&, ErrorResult&);
//void PassUnionWithInterfaces(const TestInterfaceOrTestExternalInterface& arg, ErrorResult&);
//void PassUnionWithInterfacesAndNullable(const TestInterfaceOrNullOrTestExternalInterface& arg, ErrorResult&);
void PassUnionWithArrayBuffer(const ArrayBufferOrLong&, ErrorResult&);
void PassUnionWithString(JSContext*, const StringOrObject&, ErrorResult&);
//void PassUnionWithEnum(JSContext*, const TestEnumOrObject&, ErrorResult&);
void PassUnionWithCallback(JSContext*, const TestCallbackOrLong&, ErrorResult&);
void PassUnionWithObject(JSContext*, const ObjectOrLong&, ErrorResult&);
// binaryNames tests
void MethodRenamedTo(ErrorResult&);
void MethodRenamedTo(int8_t, ErrorResult&);

View File

@ -238,6 +238,23 @@ interface TestInterface {
object receiveObject();
object? receiveNullableObject();
// Union types
void passUnion((object or long) arg);
void passUnionWithNullable((object? or long) arg);
void passNullableUnion((object or long)? arg);
void passOptionalUnion(optional (object or long) arg);
void passOptionalNullableUnion(optional (object or long)? arg);
void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
//void passUnionWithInterfaces((TestInterface or TestExternalInterface) arg);
//void passUnionWithInterfacesAndNullable((TestInterface? or TestExternalInterface) arg);
//void passUnionWithSequence((sequence<object> or long) arg);
void passUnionWithArrayBuffer((ArrayBuffer or long) arg);
void passUnionWithString((DOMString or object) arg);
//void passUnionWithEnum((TestEnum or object) arg);
void passUnionWithCallback((TestCallback or long) arg);
void passUnionWithObject((object or long) arg);
//void passUnionWithDict((Dict or long) arg);
// binaryNames tests
void methodRenamedFrom();
void methodRenamedFrom(byte argument);