mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Fix for bug 756258 (Support union types in new DOM bindings). r=bz.
--HG-- extra : rebase_source : 5352801a4eb5085f73a5b6e78aa238c5c9d44179
This commit is contained in:
parent
1e00518240
commit
7efab19797
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
169
dom/bindings/parser/tests/test_union.py
Normal file
169
dom/bindings/parser/tests/test_union.py
Normal 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()
|
14
dom/bindings/parser/tests/test_union_any.py
Normal file
14
dom/bindings/parser/tests/test_union_any.py
Normal 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.")
|
53
dom/bindings/parser/tests/test_union_nullable.py
Normal file
53
dom/bindings/parser/tests/test_union_nullable.py
Normal 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.")
|
@ -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&);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user