Implement WebIDL union support.

This commit is contained in:
Josh Matthews 2014-02-13 15:45:39 +05:30
parent d0bfdbf150
commit d590a327bd
5 changed files with 194 additions and 127 deletions

View file

@ -92,9 +92,13 @@ class CastableObjectUnwrapper():
codeOnFailure is the code to run if unwrapping fails. codeOnFailure is the code to run if unwrapping fails.
""" """
def __init__(self, descriptor, source, target, codeOnFailure, isOptional=False): def __init__(self, descriptor, source, target, codeOnFailure, isOptional=False,
preUnwrapped=None, postUnwrapped=None):
assert descriptor.castable assert descriptor.castable
unwrappedVal = "val"
if preUnwrapped or postUnwrapped:
unwrappedVal = preUnwrapped + unwrappedVal + postUnwrapped
self.substitution = { "type" : descriptor.nativeType, self.substitution = { "type" : descriptor.nativeType,
"depth": descriptor.interface.inheritanceDepth(), "depth": descriptor.interface.inheritanceDepth(),
"prototype": "PrototypeList::id::" + descriptor.name, "prototype": "PrototypeList::id::" + descriptor.name,
@ -102,7 +106,7 @@ class CastableObjectUnwrapper():
"source" : source, "source" : source,
"target" : target, "target" : target,
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define(), "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define(),
"unwrapped_val" : "Some(val)" if isOptional else "val", "unwrapped_val" : ("Some(%s)" % unwrappedVal) if isOptional else unwrappedVal,
"unwrapFn": "unwrap_jsmanaged" if 'JS' in descriptor.nativeType else "unwrap_object"} "unwrapFn": "unwrap_jsmanaged" if 'JS' in descriptor.nativeType else "unwrap_object"}
if descriptor.hasXPConnectImpls: if descriptor.hasXPConnectImpls:
# We don't use xpc_qsUnwrapThis because it will always throw on # We don't use xpc_qsUnwrapThis because it will always throw on
@ -447,7 +451,9 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
isClamp=False, isClamp=False,
exceptionCode=None, exceptionCode=None,
isCallbackReturnValue=False, isCallbackReturnValue=False,
sourceDescription="value"): sourceDescription="value",
preSuccess=None,
postSuccess=None):
""" """
Get a template for converting a JS value to a native object based on the Get a template for converting a JS value to a native object based on the
given type and descriptor. If failureCode is given, then we're actually given type and descriptor. If failureCode is given, then we're actually
@ -706,8 +712,8 @@ for (uint32_t i = 0; i < length; ++i) {
(isinstance(defaultValue, IDLNullValue) and nullable)) (isinstance(defaultValue, IDLNullValue) and nullable))
unionArgumentObj = "${holderName}" unionArgumentObj = "${holderName}"
if isOptional or nullable: #if isOptional or nullable:
unionArgumentObj += ".ref()" # unionArgumentObj += ".get_mut_ref()"
memberTypes = type.flatMemberTypes memberTypes = type.flatMemberTypes
names = [] names = []
@ -720,7 +726,7 @@ for (uint32_t i = 0; i < length; ++i) {
name = memberType.inner.identifier.name name = memberType.inner.identifier.name
else: else:
name = memberType.name name = memberType.name
interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name))) interfaceObject.append(CGGeneric("({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext" % (unionArgumentObj, name)))
names.append(name) names.append(name)
interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True) interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
else: else:
@ -731,7 +737,7 @@ for (uint32_t i = 0; i < length; ++i) {
assert len(arrayObjectMemberTypes) == 1 assert len(arrayObjectMemberTypes) == 1
memberType = arrayObjectMemberTypes[0] memberType = arrayObjectMemberTypes[0]
name = memberType.name name = memberType.name
arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name)) arrayObject = CGGeneric("done = ({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext;" % (unionArgumentObj, name))
# XXX Now we're supposed to check for an array or a platform object # 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 # that supports indexed properties... skip that last for now. It's a
# bit of a pain. # bit of a pain.
@ -761,7 +767,7 @@ for (uint32_t i = 0; i < length; ++i) {
assert len(callbackMemberTypes) == 1 assert len(callbackMemberTypes) == 1
memberType = callbackMemberTypes[0] memberType = callbackMemberTypes[0]
name = memberType.name name = memberType.name
callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name)) callbackObject = CGGeneric("done = ({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext;" % (unionArgumentObj, name))
names.append(name) names.append(name)
else: else:
callbackObject = None callbackObject = None
@ -816,7 +822,7 @@ for (uint32_t i = 0; i < length; ++i) {
if any([arrayObject, dateObject, nonPlatformObject, object]): if any([arrayObject, dateObject, nonPlatformObject, object]):
templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();")) templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
templateBody = CGWrapper(CGIndenter(templateBody), templateBody = CGWrapper(CGIndenter(templateBody),
pre="if (${val}.isObject()) {\n", pre="if JSVAL_IS_OBJECT(${val}) {\n",
post="\n}") post="\n}")
else: else:
templateBody = CGGeneric() templateBody = CGGeneric()
@ -831,7 +837,7 @@ for (uint32_t i = 0; i < length; ++i) {
name = memberType.inner.identifier.name name = memberType.inner.identifier.name
else: else:
name = memberType.name name = memberType.name
other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name)) other = CGGeneric("done = ({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext;" % (unionArgumentObj, name))
names.append(name) names.append(name)
if hasObjectTypes: if hasObjectTypes:
other = CGWrapper(CGIndenter(other), "{\n", post="\n}") other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
@ -844,29 +850,33 @@ for (uint32_t i = 0; i < length; ++i) {
else: else:
other = None other = None
templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n") templateBody = CGWrapper(templateBody, pre="let mut done = false;\n"
throw = CGGeneric("if (failed) {\n" "let mut failed = false;\n"
" return false;\n" "let mut tryNext = false;\n")
throw = CGGeneric("if failed {\n"
" return 0;\n"
"}\n" "}\n"
"if (!done) {\n" "if !done {\n"
" return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n" " //XXXjdm throw exception\n"
" //return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
" return 0;"
"}" % ", ".join(names)) "}" % ", ".join(names))
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}") templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
typeName = type.name typeName = type.name
argumentTypeName = typeName + "Argument" argumentTypeName = typeName + "Argument"
if nullable: if nullable:
typeName = "Nullable<" + typeName + " >" typeName = "Option<" + typeName + " >"
if isOptional: #if isOptional:
nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})" # nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
else: #else:
nonConstDecl = "const_cast<" + typeName + "& >(${declName})" # nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
typeName = "const " + typeName nonConstDecl = "${declName}"
def handleNull(templateBody, setToNullVar, extraConditionForNull=""): def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n" null = CGGeneric("if %sRUST_JSVAL_IS_NULL(${val}) != 0 || %sRUST_JSVAL_IS_VOID(${val}) != 0 {\n"
" %s.SetNull();\n" " %s = None;\n"
"}" % (extraConditionForNull, setToNullVar)) "}" % (extraConditionForNull, extraConditionForNull, setToNullVar))
templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}") templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
return CGList([null, templateBody], " else ") return CGList([null, templateBody], " else ")
@ -878,21 +888,28 @@ for (uint32_t i = 0; i < length; ++i) {
if isOptional: if isOptional:
mutableDecl = nonConstDecl + ".Value()" mutableDecl = nonConstDecl + ".Value()"
declType = CGWrapper(declType, pre="const Optional<", post=" >") declType = CGWrapper(declType, pre="const Optional<", post=" >")
holderType = CGWrapper(holderType, pre="Maybe<", post=" >") holderType = CGWrapper(holderType, pre="Option<", post=" >")
constructDecl = CGGeneric(nonConstDecl + ".Construct();") constructDecl = CGGeneric(nonConstDecl + ".Construct();")
if nullable: if nullable:
constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl) constructHolder = CGGeneric("${holderName} = Some(%s.SetValue());" % mutableDecl)
else: else:
constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());") constructHolder = CGGeneric("${holderName} = Some(${declName}.Value());")
else: else:
mutableDecl = nonConstDecl mutableDecl = nonConstDecl
constructDecl = None constructDecl = None
#if nullable:
#holderType = CGWrapper(holderType, pre="Option<", post=" >")
# constructHolder = CGGeneric("${holderName} = Some(None);")
#else:
holderInit = "${declName}"
if nullable: if nullable:
holderType = CGWrapper(holderType, pre="Maybe<", post=" >") holderInit += ".get_mut_ref()"
constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
else: else:
constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});") holderInit = "&mut " + holderInit
holderType = None constructHolder = CGWrapper(holderType, pre="let mut ${holderName} = ", post="::new(" + holderInit + ");")
if nullable:
constructHolder = CGWrapper(constructHolder, pre="${declName} = Some(uninit());\n")
holderType = None
templateBody = CGList([constructHolder, templateBody], "\n") templateBody = CGList([constructHolder, templateBody], "\n")
if nullable: if nullable:
@ -903,9 +920,10 @@ for (uint32_t i = 0; i < length; ++i) {
valueMissing = "" valueMissing = ""
templateBody = handleNull(templateBody, mutableDecl, templateBody = handleNull(templateBody, mutableDecl,
extraConditionForNull=valueMissing) extraConditionForNull=valueMissing)
templateBody = CGList([constructDecl, templateBody], "\n") templateBody = CGWrapper(CGIndenter(CGList([constructDecl, templateBody], "\n")),
pre="{\n", post="\n}")
return templateBody.define(), declType, holderType, False, None return templateBody.define(), declType, holderType, False, "uninit()" if not nullable else None
if type.isGeckoInterface(): if type.isGeckoInterface():
assert not isEnforceRange and not isClamp assert not isEnforceRange and not isClamp
@ -968,7 +986,8 @@ for (uint32_t i = 0; i < length; ++i) {
"JSVAL_TO_OBJECT(${val})", "JSVAL_TO_OBJECT(${val})",
"${declName}", "${declName}",
failureCode, failureCode,
isOptional or argIsPointer or type.nullable())) isOptional or argIsPointer or type.nullable(),
preUnwrapped=preSuccess, postUnwrapped=postSuccess))
else: else:
templateBody += str(FailureFatalCastableObjectUnwrapper( templateBody += str(FailureFatalCastableObjectUnwrapper(
descriptor, descriptor,
@ -1254,32 +1273,41 @@ for (uint32_t i = 0; i < length; ++i) {
elif isClamp: elif isClamp:
conversionBehavior = "eClamp" conversionBehavior = "eClamp"
if type.nullable(): if failureCode is None:
failureCode = 'return 0'
if type.nullable(): #or isOptional:
dataLoc = "${declName}.SetValue()" dataLoc = "${declName}.SetValue()"
nullCondition = "(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0)" nullCondition = "(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0)"
if defaultValue is not None and isinstance(defaultValue, IDLNullValue): if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
nullCondition = "!(${haveValue}) || " + nullCondition nullCondition = "!(${haveValue}) || " + nullCondition
successVal = "val_"
if preSuccess or postSuccess:
successVal = preSuccess + successVal + postSuccess
#XXXjdm support conversionBehavior here #XXXjdm support conversionBehavior here
template = ( template = (
"if (%s) {\n" "if (%s) {\n"
" ${declName} = None;\n" " ${declName} = None;\n"
"} else {\n" "} else {\n"
" match JSValConvertible::from_jsval(${val}) {\n" " match JSValConvertible::from_jsval(${val}) {\n"
" Some(val_) => ${declName} = Some(val_),\n" " Some(val_) => ${declName} = Some(%s),\n"
" None => return 0\n" " None => %s\n"
" }\n" " }\n"
"}" % nullCondition) "}" % (nullCondition, successVal, failureCode))
declType = CGGeneric("Option<" + typeName + ">") declType = CGGeneric("Option<" + typeName + ">")
else: else:
assert(defaultValue is None or assert(defaultValue is None or
not isinstance(defaultValue, IDLNullValue)) not isinstance(defaultValue, IDLNullValue))
dataLoc = "${declName}" dataLoc = "${declName}"
#XXXjdm conversionBehavior should be used #XXXjdm conversionBehavior should be used
successVal = "v"
if preSuccess or postSuccess:
successVal = preSuccess + successVal + postSuccess
template = ( template = (
"match JSValConvertible::from_jsval(${val}) {\n" "match JSValConvertible::from_jsval(${val}) {\n"
" None => return 0,\n" " None => %s,\n"
" Some(v) => %s = v\n" " Some(v) => %s = %s\n"
"}" % (dataLoc,)) "}" % (failureCode, dataLoc, successVal))
declType = CGGeneric(typeName) declType = CGGeneric(typeName)
if (defaultValue is not None and if (defaultValue is not None and
# We already handled IDLNullValue, so just deal with the other ones # We already handled IDLNullValue, so just deal with the other ones
@ -3686,17 +3714,17 @@ def getUnionAccessorSignatureType(type, descriptorProvider):
typeName = CGGeneric(descriptor.nativeType) typeName = CGGeneric(descriptor.nativeType)
# Allow null pointers for nullable types and old-binding classes # Allow null pointers for nullable types and old-binding classes
if type.nullable() or type.unroll().inner.isExternal(): if type.nullable() or type.unroll().inner.isExternal():
typeName = CGWrapper(typeName, post="*") typeName = CGWrapper(typeName, pre="Option<", post=">")
else: else:
typeName = CGWrapper(typeName, post="&") typeName = CGWrapper(typeName, pre="&'a ")
return typeName return typeName
if type.isSpiderMonkeyInterface(): if type.isSpiderMonkeyInterface():
typeName = CGGeneric(type.name) typeName = CGGeneric(type.name)
if type.nullable(): if type.nullable():
typeName = CGWrapper(typeName, post="*") typeName = CGWrapper(typeName, pre="Option<", post=">")
else: else:
typeName = CGWrapper(typeName, post="&") typeName = CGWrapper(typeName, pre="&")
return typeName return typeName
if type.isString(): if type.isString():
@ -3740,23 +3768,33 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
if type.isGeckoInterface(): if type.isGeckoInterface():
name = type.inner.identifier.name name = type.inner.identifier.name
typeName = descriptorProvider.getDescriptor(name).nativeType
elif type.isEnum(): elif type.isEnum():
name = type.inner.identifier.name name = type.inner.identifier.name
typeName = name
elif type.isArray() or type.isSequence(): elif type.isArray() or type.isSequence():
name = str(type) name = str(type)
#XXXjdm dunno about typeName here
typeName = "/*" + type.name + "*/"
elif type.isPrimitive():
name = type.name
typeName = builtinNames[type.tag()]
else: else:
name = type.name name = type.name
typeName = "/*" + type.name + "*/"
tryNextCode = """tryNext = true; tryNextCode = """{
return true;""" *tryNext = true;
return true;
}"""
if type.isGeckoInterface(): if type.isGeckoInterface():
tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) { tryNextCode = ("""/*if (mUnion.mType != mUnion.eUninitialized) {
mUnion.Destroy%s(); mUnion.Destroy%s();
}""" % name) + tryNextCode }*/""" % name) + tryNextCode
(template, declType, holderType, (template, declType, holderType,
dealWithOptional, initialValue) = getJSToNativeConversionTemplate( dealWithOptional, initialValue) = getJSToNativeConversionTemplate(
type, descriptorProvider, failureCode=tryNextCode, type, descriptorProvider, failureCode=tryNextCode,
isDefinitelyObject=True) isDefinitelyObject=True, isOptional=type.nullable(), preSuccess="e" + name + "(", postSuccess=")")
# This is ugly, but UnionMember needs to call a constructor with no # This is ugly, but UnionMember needs to call a constructor with no
# arguments so the type can't be const. # arguments so the type can't be const.
@ -3766,17 +3804,15 @@ return true;"""
externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
if type.isObject(): if type.isObject():
setter = CGGeneric("void SetToObject(JSObject* obj)\n" setter = CGGeneric("pub fn SetToObject(obj: *JSObject) {\n"
"{\n" " mUnion = Some(eObject(obj));\n"
" mUnion.mValue.mObject.SetValue() = obj;\n"
" mUnion.mType = mUnion.eObject;\n"
"}") "}")
else: else:
jsConversion = string.Template(template).substitute( jsConversion = string.Template(template).substitute(
{ {
"val": "value", "val": "value",
"valPtr": "pvalue", "valPtr": "pvalue",
"declName": "SetAs" + name + "()", "declName": "*self.mUnion",
"holderName": "m" + name + "Holder" "holderName": "m" + name + "Holder"
} }
) )
@ -3784,16 +3820,17 @@ return true;"""
post="\n" post="\n"
"return true;") "return true;")
setter = CGWrapper(CGIndenter(jsConversion), setter = CGWrapper(CGIndenter(jsConversion),
pre="bool TrySetTo" + name + "(JSContext* cx, const JS::Value& value, JS::Value* pvalue, bool& tryNext)\n" pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal, tryNext: &mut bool) -> bool {\n"
"{\n" " *tryNext = false;\n",
" tryNext = false;\n",
post="\n" post="\n"
"}") "}")
return { return {
"name": name, "name": name,
"typeName": typeName,
"structType": structType, "structType": structType,
"externalType": externalType, "externalType": externalType,
"optRef": 'ref ' if externalType[0] == '&' else '',
"setter": CGIndenter(setter).define(), "setter": CGIndenter(setter).define(),
"holderType": holderType.define() if holderType else None "holderType": holderType.define() if holderType else None
} }
@ -3819,75 +3856,47 @@ class CGUnionStruct(CGThing):
callDestructors.append(" case eNull:\n" callDestructors.append(" case eNull:\n"
" break;") " break;")
enumValues.append("eNull") enumValues.append("eNull")
methods.append(""" bool IsNull() const methods.append(""" pub fn IsNull(&self) -> bool {
{ match *self {
return mType == eNull; eNull => true,
_ => false
}
}""") }""")
destructorTemplate = """ void Destroy${name}() destructorTemplate = """ fn Destroy${name}(&mut self) {
{ assert!(Is${name}(), "Wrong type!");
MOZ_ASSERT(Is${name}(), "Wrong type!"); //mValue.m${name}.Destroy();
mValue.m${name}.Destroy(); //mType = eUninitialized;
mType = eUninitialized; *self.mUnion = None;
}""" }"""
destructors = mapTemplate(destructorTemplate, templateVars) destructors = mapTemplate(destructorTemplate, templateVars)
callDestructors.extend(mapTemplate(" case e${name}:\n" callDestructors.extend(mapTemplate(" case e${name}:\n"
" Destroy${name}();\n" " Destroy${name}();\n"
" break;", templateVars)) " break;", templateVars))
enumValues.extend(mapTemplate("e${name}", templateVars)) enumValues.extend(mapTemplate("e${name}(${typeName})", templateVars))
methodTemplate = """ bool Is${name}() const methodTemplate = """ pub fn Is${name}(&self) -> bool {
{ match *self {
return mType == e${name}; e${name}(_) => true,
_ => false
}
} }
${externalType} GetAs${name}() const pub fn GetAs${name}<'a>(&'a self) -> ${externalType} {
{ assert!(self.Is${name}());
MOZ_ASSERT(Is${name}(), "Wrong type!"); match *self {
// The cast to ${externalType} is needed to work around a bug in Apple's e${name}(${optRef}inner) => inner,
// clang compiler, for some reason it doesn't call |S::operator T&| when _ => unreachable!()
// 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)) methods.extend(mapTemplate(methodTemplate, templateVars))
values = mapTemplate("UnionMember<${structType} > m${name};", templateVars) values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
return string.Template(""" return string.Template("""
class ${structName} { pub enum ${structName} {
public: ${enumValues}
${structName}() : mType(eUninitialized) }
{
}
~${structName}()
{
switch (mType) {
${callDestructors}
case eUninitialized:
break;
}
}
impl ${structName} {
${methods} ${methods}
}
private:
friend class ${structName}Argument;
${destructors}
enum Type {
eUninitialized,
${enumValues}
};
union Value {
${values}
};
Type mType;
Value mValue;
};
""").substitute( """).substitute(
{ {
"structName": self.type.__str__(), "structName": self.type.__str__(),
@ -3912,9 +3921,9 @@ class CGUnionConversionStruct(CGThing):
setters = [] setters = []
if self.type.hasNullableType: if self.type.hasNullableType:
setters.append(""" bool SetNull() setters.append(""" pub fn SetNull(&mut self) -> bool
{ {
mUnion.mType = mUnion.eNull; mUnion = Some(eNull);
return true; return true;
}""") }""")
@ -3923,7 +3932,7 @@ class CGUnionConversionStruct(CGThing):
structName = self.type.__str__() structName = self.type.__str__()
setters.extend(mapTemplate("${setter}", templateVars)) setters.extend(mapTemplate("${setter}", templateVars))
private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}() private = "\n".join(mapTemplate(""" fn SetAs${name}() -> &${structType}
{ {
mUnion.mType = mUnion.e${name}; mUnion.mType = mUnion.e${name};
return mUnion.mValue.m${name}.SetValue(); return mUnion.mValue.m${name}.SetValue();
@ -3935,20 +3944,22 @@ class CGUnionConversionStruct(CGThing):
private += "\n\n" private += "\n\n"
private += " " + structName + "& mUnion;" private += " " + structName + "& mUnion;"
return string.Template(""" return string.Template("""
class ${structName}Argument { pub struct ${structName}Argument<'a> {
public: mUnion: &'a mut ${innerType}
${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion)) }
{
impl<'a> ${structName}Argument<'a> {
pub fn new(union: &'a mut ${innerType}) -> ${structName}Argument<'a> {
${structName}Argument {
mUnion: union
}
} }
${setters} ${setters}
}
private:
${private}
};
""").substitute({"structName": structName, """).substitute({"structName": structName,
"innerType": ("Option<%s>" % structName) if self.type.nullable() else structName,
"setters": "\n\n".join(setters), "setters": "\n\n".join(setters),
"private": private
}) })
def define(self): def define(self):
@ -5580,6 +5591,8 @@ class CGBindingRoot(CGThing):
'dom::bindings::callback::*', 'dom::bindings::callback::*',
'dom::bindings::conversions::*', 'dom::bindings::conversions::*',
'dom::bindings::codegen::*', #XXXjdm 'dom::bindings::codegen::*', #XXXjdm
'dom::bindings::codegen::UnionTypes::*', #XXXjdm
'dom::bindings::codegen::UnionConversions::*', #XXXjdm
'script_task::{JSPageInfo, page_from_context}', 'script_task::{JSPageInfo, page_from_context}',
'dom::bindings::proxyhandler', 'dom::bindings::proxyhandler',
'dom::bindings::proxyhandler::*', 'dom::bindings::proxyhandler::*',
@ -5591,6 +5604,7 @@ class CGBindingRoot(CGThing):
'std::vec', 'std::vec',
'std::str', 'std::str',
'std::num', 'std::num',
'std::unstable::intrinsics::uninit',
'std::unstable::raw::Box', 'std::unstable::raw::Box',
], ],
[], [],
@ -6713,6 +6727,9 @@ class GlobalGenRoots():
# Add include guards. # Add include guards.
#curr = CGIncludeGuard('UnionTypes', curr) #curr = CGIncludeGuard('UnionTypes', curr)
curr = CGImports([], [], ['dom::bindings::js::JS',
'dom::types::*'], [], curr)
# Add the auto-generated comment. # Add the auto-generated comment.
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
@ -6735,6 +6752,14 @@ class GlobalGenRoots():
# Add include guards. # Add include guards.
#curr = CGIncludeGuard('UnionConversions', curr) #curr = CGIncludeGuard('UnionConversions', curr)
curr = CGImports([], [], ['dom::bindings::utils::unwrap_jsmanaged',
'dom::bindings::codegen::UnionTypes::*',
'dom::bindings::codegen::PrototypeList',
'dom::bindings::conversions::JSValConvertible',
'js::*',
'js::jsapi::*',
'js::glue::*'], [], curr)
# Add the auto-generated comment. # Add the auto-generated comment.
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)

View file

@ -4,6 +4,7 @@
use dom::bindings::codegen::HTMLSelectElementBinding; use dom::bindings::codegen::HTMLSelectElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementDerived; use dom::bindings::codegen::InheritTypes::HTMLSelectElementDerived;
use dom::bindings::codegen::UnionTypes::{HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement};
use dom::bindings::js::JS; use dom::bindings::js::JS;
use dom::bindings::utils::ErrorResult; use dom::bindings::utils::ErrorResult;
use dom::document::Document; use dom::document::Document;
@ -175,4 +176,8 @@ impl HTMLSelectElement {
pub fn SetCustomValidity(&mut self, _error: DOMString) { pub fn SetCustomValidity(&mut self, _error: DOMString) {
} }
pub fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) -> ErrorResult {
Ok(())
}
} }

View file

@ -32,8 +32,8 @@ interface HTMLSelectElement : HTMLElement {
attribute unsigned long length; attribute unsigned long length;
getter Element? item(unsigned long index); getter Element? item(unsigned long index);
HTMLOptionElement? namedItem(DOMString name); HTMLOptionElement? namedItem(DOMString name);
/*[Throws] [Throws]
void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);*/ void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
void remove(long index); void remove(long index);
[Throws] [Throws]
setter creator void (unsigned long index, HTMLOptionElement? option); setter creator void (unsigned long index, HTMLOptionElement? option);

View file

@ -40,6 +40,8 @@ pub mod dom {
pub mod PrototypeList; pub mod PrototypeList;
pub mod RegisterBindings; pub mod RegisterBindings;
pub mod BindingDeclarations; pub mod BindingDeclarations;
pub mod UnionConversions;
pub mod UnionTypes;
} }
} }

View file

@ -0,0 +1,35 @@
<html>
<head>
<script src="harness.js"></script>
<select id="sel"></select>
<script>
var sel = document.getElementById('sel');
var opt = document.createElement('option');
sel.add(opt);
var optgroup = document.createElement('optgroup');
sel.add(optgroup);
var div = document.createElement('div');
sel.add(opt, div);
sel.add(optgroup, div);
sel.add(opt, 5);
sel.add(optgroup, 5);
is(true, true, "should not have thrown any exceptions");
try {
self.add(div);
is(true, false, "should have thrown");
} catch (x) {
is(true, true, "should have thrown");
}
try {
self.add(optgroup, "");
is(true, false, "should have thrown");
} catch (x) {
is(true, true, "should have thrown");
}
finish();
</script>
</head>
</html>