diff options
author | bors-servo <release+servo@mozilla.com> | 2014-02-24 20:01:39 -0500 |
---|---|---|
committer | bors-servo <release+servo@mozilla.com> | 2014-02-24 20:01:39 -0500 |
commit | df993fdaf3c1c031154389a0832914d133bc722a (patch) | |
tree | 490a4e28d74ee67ccba2a7b116da3311cbe1e66f /src | |
parent | 998710d907964da4d39d80075dc45171ce3adabe (diff) | |
parent | 232ca597089cff647fcdef2a3cfd2030393ba84b (diff) | |
download | servo-df993fdaf3c1c031154389a0832914d133bc722a.tar.gz servo-df993fdaf3c1c031154389a0832914d133bc722a.zip |
auto merge of #1689 : jdm/servo/unions, r=kmcallister
Seven hours on a train without internet will do this to you. Fixes #541.
Diffstat (limited to 'src')
-rw-r--r-- | src/components/script/dom/bindings/codegen/CodegenRust.py | 566 | ||||
-rw-r--r-- | src/components/script/dom/bindings/codegen/GlobalGen.py | 5 | ||||
-rw-r--r-- | src/components/script/dom/bindings/conversions.rs | 52 | ||||
-rw-r--r-- | src/components/script/dom/bindings/utils.rs | 9 | ||||
-rw-r--r-- | src/components/script/dom/htmlselectelement.rs | 5 | ||||
-rw-r--r-- | src/components/script/dom/webidls/HTMLSelectElement.webidl | 4 | ||||
-rw-r--r-- | src/components/script/script.rs | 2 | ||||
-rw-r--r-- | src/test/html/content/harness.js | 18 | ||||
-rw-r--r-- | src/test/html/content/test_exception.html | 7 | ||||
-rw-r--r-- | src/test/html/content/test_union.html | 26 |
10 files changed, 631 insertions, 63 deletions
diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index da0ad3dd8df..ca04b873aab 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -92,9 +92,13 @@ class CastableObjectUnwrapper(): 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 + unwrappedVal = "val" + if preUnwrapped or postUnwrapped: + unwrappedVal = preUnwrapped + unwrappedVal + postUnwrapped self.substitution = { "type" : descriptor.nativeType, "depth": descriptor.interface.inheritanceDepth(), "prototype": "PrototypeList::id::" + descriptor.name, @@ -102,7 +106,7 @@ class CastableObjectUnwrapper(): "source" : source, "target" : target, "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"} if descriptor.hasXPConnectImpls: # We don't use xpc_qsUnwrapThis because it will always throw on @@ -447,7 +451,9 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, isClamp=False, exceptionCode=None, 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 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)) unionArgumentObj = "${holderName}" - if isOptional or nullable: - unionArgumentObj += ".ref()" + #if isOptional or nullable: + # unionArgumentObj += ".get_mut_ref()" memberTypes = type.flatMemberTypes names = [] @@ -720,7 +726,7 @@ for (uint32_t i = 0; i < length; ++i) { name = memberType.inner.identifier.name else: name = memberType.name - interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name))) + interfaceObject.append(CGGeneric("{res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()}" % (unionArgumentObj, name))) names.append(name) interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True) else: @@ -731,7 +737,7 @@ for (uint32_t i = 0; i < length; ++i) { assert len(arrayObjectMemberTypes) == 1 memberType = arrayObjectMemberTypes[0] name = memberType.name - arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name)) + arrayObject = CGGeneric("done = {res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()};" % (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. @@ -761,7 +767,7 @@ for (uint32_t i = 0; i < length; ++i) { assert len(callbackMemberTypes) == 1 memberType = callbackMemberTypes[0] name = memberType.name - callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name)) + callbackObject = CGGeneric("done = {res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()};" % (unionArgumentObj, name)) names.append(name) else: callbackObject = None @@ -816,7 +822,7 @@ for (uint32_t i = 0; i < length; ++i) { if any([arrayObject, dateObject, nonPlatformObject, object]): templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();")) templateBody = CGWrapper(CGIndenter(templateBody), - pre="if (${val}.isObject()) {\n", + pre="if JSVAL_IS_OBJECT(${val}) {\n", post="\n}") else: templateBody = CGGeneric() @@ -831,7 +837,7 @@ for (uint32_t i = 0; i < length; ++i) { name = memberType.inner.identifier.name else: name = memberType.name - other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name)) + other = CGGeneric("done = {res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()};" % (unionArgumentObj, name)) names.append(name) if hasObjectTypes: other = CGWrapper(CGIndenter(other), "{\n", post="\n}") @@ -844,28 +850,25 @@ for (uint32_t i = 0; i < length; ++i) { else: other = None - templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n") - throw = CGGeneric("if (failed) {\n" - " return false;\n" + templateBody = CGWrapper(templateBody, pre="let mut done = false;\n" + "let mut res = Ok(true);\n") + throw = CGGeneric("if res.is_err() {\n" + " return 0;\n" "}\n" - "if (!done) {\n" - " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n" + "if !done {\n" + " return throw_not_in_union(cx, \"%s\");\n" "}" % ", ".join(names)) 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 + typeName = "Option<" + typeName + " >" + nonConstDecl = "${declName}" def handleNull(templateBody, setToNullVar, extraConditionForNull=""): - null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n" - " %s.SetNull();\n" + null = CGGeneric("if %s(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0) {\n" + " %s = None;\n" "}" % (extraConditionForNull, setToNullVar)) templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}") return CGList([null, templateBody], " else ") @@ -878,21 +881,24 @@ for (uint32_t i = 0; i < length; ++i) { if isOptional: mutableDecl = nonConstDecl + ".Value()" declType = CGWrapper(declType, pre="const Optional<", post=" >") - holderType = CGWrapper(holderType, pre="Maybe<", post=" >") + holderType = CGWrapper(holderType, pre="Option<", post=" >") constructDecl = CGGeneric(nonConstDecl + ".Construct();") if nullable: - constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl) + constructHolder = CGGeneric("${holderName} = Some(%s.SetValue());" % mutableDecl) else: - constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());") + constructHolder = CGGeneric("${holderName} = Some(${declName}.Value());") else: mutableDecl = nonConstDecl constructDecl = None + holderInit = "${declName}" if nullable: - holderType = CGWrapper(holderType, pre="Maybe<", post=" >") - constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl) + holderInit += ".get_mut_ref()" else: - constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});") - holderType = None + holderInit = "&mut " + holderInit + 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") if nullable: @@ -903,9 +909,10 @@ for (uint32_t i = 0; i < length; ++i) { valueMissing = "" templateBody = handleNull(templateBody, mutableDecl, 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(): assert not isEnforceRange and not isClamp @@ -968,7 +975,8 @@ for (uint32_t i = 0; i < length; ++i) { "JSVAL_TO_OBJECT(${val})", "${declName}", failureCode, - isOptional or argIsPointer or type.nullable())) + isOptional or argIsPointer or type.nullable(), + preUnwrapped=preSuccess, postUnwrapped=postSuccess)) else: templateBody += str(FailureFatalCastableObjectUnwrapper( descriptor, @@ -1254,32 +1262,41 @@ for (uint32_t i = 0; i < length; ++i) { elif isClamp: conversionBehavior = "eClamp" + if failureCode is None: + failureCode = 'return 0' + if type.nullable(): dataLoc = "${declName}.SetValue()" nullCondition = "(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0)" if defaultValue is not None and isinstance(defaultValue, IDLNullValue): nullCondition = "!(${haveValue}) || " + nullCondition + successVal = "val_" + if preSuccess or postSuccess: + successVal = preSuccess + successVal + postSuccess #XXXjdm support conversionBehavior here template = ( "if (%s) {\n" " ${declName} = None;\n" "} else {\n" " match JSValConvertible::from_jsval(${val}) {\n" - " Some(val_) => ${declName} = Some(val_),\n" - " None => return 0\n" + " Some(val_) => ${declName} = Some(%s),\n" + " None => %s\n" " }\n" - "}" % nullCondition) + "}" % (nullCondition, successVal, failureCode)) declType = CGGeneric("Option<" + typeName + ">") else: assert(defaultValue is None or not isinstance(defaultValue, IDLNullValue)) dataLoc = "${declName}" #XXXjdm conversionBehavior should be used + successVal = "v" + if preSuccess or postSuccess: + successVal = preSuccess + successVal + postSuccess template = ( "match JSValConvertible::from_jsval(${val}) {\n" - " None => return 0,\n" - " Some(v) => %s = v\n" - "}" % (dataLoc,)) + " None => %s,\n" + " Some(v) => %s = %s\n" + "}" % (failureCode, dataLoc, successVal)) declType = CGGeneric(typeName) if (defaultValue is not None and # We already handled IDLNullValue, so just deal with the other ones @@ -2385,6 +2402,112 @@ class CGGeneric(CGThing): def define(self): return self.defineText +def getTypes(descriptor): + """ + Get all argument and return types for all members of the descriptor + """ + members = [m for m in descriptor.interface.members] + if descriptor.interface.ctor(): + members.append(descriptor.interface.ctor()) + 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 + +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] + if d.interface.ctor(): + members.append(d.interface.ctor()) + 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 @@ -3544,6 +3667,282 @@ class CGEnum(CGThing): """ % (",\n ".join(map(getEnumValueName, self.enum.values())), ",\n ".join(['EnumEntry {value: &"' + val + '", length: ' + 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, pre="Option<", post=">") + else: + typeName = CGWrapper(typeName, pre="&'a ") + return typeName + + if type.isSpiderMonkeyInterface(): + typeName = CGGeneric(type.name) + if type.nullable(): + typeName = CGWrapper(typeName, pre="Option<", post=">") + else: + typeName = CGWrapper(typeName, pre="&") + 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. + # Also, for dictionaries we would need to handle conversion of + # null/undefined to the dictionary correctly. + if type.isDictionary() or type.isSequence(): + raise TypeError("Can't handle dictionaries or sequences in unions") + + if type.isGeckoInterface(): + name = type.inner.identifier.name + typeName = descriptorProvider.getDescriptor(name).nativeType + elif type.isEnum(): + name = type.inner.identifier.name + typeName = name + elif type.isArray() or type.isSequence(): + name = str(type) + #XXXjdm dunno about typeName here + typeName = "/*" + type.name + "*/" + elif type.isPrimitive(): + name = type.name + typeName = builtinNames[type.tag()] + else: + name = type.name + typeName = "/*" + type.name + "*/" + + tryNextCode = """{ + return Ok(true); +}""" + (template, declType, holderType, + dealWithOptional, initialValue) = getJSToNativeConversionTemplate( + type, descriptorProvider, failureCode=tryNextCode, + isDefinitelyObject=True, isOptional=type.nullable(), preSuccess="e" + name + "(", postSuccess=")") + + structType = declType.define() + externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() + + if type.isObject(): + setter = CGGeneric("pub fn SetToObject(obj: *JSObject) {\n" + " mUnion = Some(eObject(obj));\n" + "}") + else: + jsConversion = string.Template(template).substitute( + { + "val": "value", + "valPtr": "pvalue", + "declName": "*self.mUnion", + "holderName": "m" + name + "Holder" + } + ) + jsConversion = CGWrapper(CGGeneric(jsConversion), + post="\n" + "return Ok(false);") + setter = CGWrapper(CGIndenter(jsConversion), + pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal) -> Result<bool,()> {\n", + post="\n" + "}") + + return { + "name": name, + "typeName": typeName, + "structType": structType, + "externalType": externalType, + "optRef": 'ref ' if externalType[0] == '&' else '', + "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(""" pub fn IsNull(&self) -> bool { + match *self { + eNull => true, + _ => false + } + }""") + + destructorTemplate = """ fn Destroy${name}(&mut self) { + assert!(Is${name}(), "Wrong type!"); + *self.mUnion = None; + }""" + destructors = mapTemplate(destructorTemplate, templateVars) + callDestructors.extend(mapTemplate(" case e${name}:\n" + " Destroy${name}();\n" + " break;", templateVars)) + enumValues.extend(mapTemplate("e${name}(${typeName})", templateVars)) + methodTemplate = """ pub fn Is${name}(&self) -> bool { + match *self { + e${name}(_) => true, + _ => false + } + } + pub fn GetAs${name}<'a>(&'a self) -> ${externalType} { + assert!(self.Is${name}()); + match *self { + e${name}(${optRef}inner) => inner, + _ => unreachable!() + } + }""" + methods.extend(mapTemplate(methodTemplate, templateVars)) + values = mapTemplate("UnionMember<${structType} > m${name};", templateVars) + return string.Template(""" +pub enum ${structName} { + ${enumValues} +} + +impl ${structName} { +${methods} +} +""").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(""" pub fn SetNull(&mut self) -> bool + { + mUnion = Some(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(""" fn SetAs${name}() -> &${structType} + { + 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(""" +pub struct ${structName}Argument<'a> { + mUnion: &'a mut ${innerType} +} + +impl<'a> ${structName}Argument<'a> { + pub fn new(union: &'a mut ${innerType}) -> ${structName}Argument<'a> { + ${structName}Argument { + mUnion: union + } + } + +${setters} +} +""").substitute({"structName": structName, + "innerType": ("Option<%s>" % structName) if self.type.nullable() else structName, + "setters": "\n\n".join(setters), + }) + + def define(self): + return """ +""" + class ClassItem: """ Use with CGClass """ def __init__(self, name, visibility): @@ -5169,6 +5568,8 @@ class CGBindingRoot(CGThing): 'dom::bindings::callback::*', 'dom::bindings::conversions::*', 'dom::bindings::codegen::*', #XXXjdm + 'dom::bindings::codegen::UnionTypes::*', #XXXjdm + 'dom::bindings::codegen::UnionConversions::*', #XXXjdm 'script_task::{JSPageInfo, page_from_context}', 'dom::bindings::proxyhandler', 'dom::bindings::proxyhandler::*', @@ -5180,6 +5581,7 @@ class CGBindingRoot(CGThing): 'std::vec', 'std::str', 'std::num', + 'std::unstable::intrinsics::uninit', 'std::unstable::raw::Box', ], [], @@ -6254,3 +6656,89 @@ class GlobalGenRoots(): curr = CGList(allprotos) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 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, public=True) + curr = unions + + curr = CGWrapper(curr, post='\n') + + namespaces = [] + stack = [CGList([])] + for (clazz, isStruct) in SortedTuples(declarations): + elements = clazz.split("::") + elements.pop() + #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) + + curr = CGImports([], [], ['dom::bindings::js::JS', + 'dom::types::*'], [], curr) + + # Add the auto-generated comment. + curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) + + # Done. + return curr + + @staticmethod + def UnionConversions(config): + + unions = UnionConversions(config.getDescriptors()) + curr = unions + + # 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) + + 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. + curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) + + # Done. + return curr diff --git a/src/components/script/dom/bindings/codegen/GlobalGen.py b/src/components/script/dom/bindings/codegen/GlobalGen.py index ffbf31a4b48..bd8b3d70793 100644 --- a/src/components/script/dom/bindings/codegen/GlobalGen.py +++ b/src/components/script/dom/bindings/codegen/GlobalGen.py @@ -89,9 +89,8 @@ def main(): # Generate the module declarations. generate_file(config, 'BindingDeclarations', 'declare+define') - #XXXjdm No union support yet - #generate_file(config, 'UnionTypes', 'declare') - #generate_file(config, 'UnionConversions', 'declare') + generate_file(config, 'UnionTypes', 'declare+define') + generate_file(config, 'UnionConversions', 'declare+define') if __name__ == '__main__': main() diff --git a/src/components/script/dom/bindings/conversions.rs b/src/components/script/dom/bindings/conversions.rs index 06745217365..decb047ab9f 100644 --- a/src/components/script/dom/bindings/conversions.rs +++ b/src/components/script/dom/bindings/conversions.rs @@ -4,7 +4,9 @@ use js::jsapi::JSVal; use js::{JSVAL_FALSE, JSVAL_TRUE}; -use js::glue::{RUST_UINT_TO_JSVAL, RUST_JSVAL_TO_INT, RUST_DOUBLE_TO_JSVAL, RUST_JSVAL_TO_DOUBLE}; +use js::glue::{RUST_UINT_TO_JSVAL, RUST_JSVAL_TO_INT, RUST_DOUBLE_TO_JSVAL}; +use js::glue::{RUST_JSVAL_TO_DOUBLE, RUST_JSVAL_IS_INT, RUST_JSVAL_IS_DOUBLE}; +use js::glue::{RUST_JSVAL_IS_BOOLEAN, RUST_JSVAL_TO_BOOLEAN}; pub trait JSValConvertible { fn to_jsval(&self) -> JSVal; @@ -21,7 +23,11 @@ impl JSValConvertible for i64 { fn from_jsval(val: JSVal) -> Option<i64> { unsafe { - Some(RUST_JSVAL_TO_DOUBLE(val) as i64) + if RUST_JSVAL_IS_INT(val) != 0 { + Some(RUST_JSVAL_TO_DOUBLE(val) as i64) + } else { + None + } } } } @@ -35,7 +41,11 @@ impl JSValConvertible for u32 { fn from_jsval(val: JSVal) -> Option<u32> { unsafe { - Some(RUST_JSVAL_TO_INT(val) as u32) + if RUST_JSVAL_IS_INT(val) != 0 { + Some(RUST_JSVAL_TO_INT(val) as u32) + } else { + None + } } } } @@ -49,7 +59,11 @@ impl JSValConvertible for i32 { fn from_jsval(val: JSVal) -> Option<i32> { unsafe { - Some(RUST_JSVAL_TO_INT(val) as i32) + if RUST_JSVAL_IS_INT(val) != 0 { + Some(RUST_JSVAL_TO_INT(val) as i32) + } else { + None + } } } } @@ -63,7 +77,11 @@ impl JSValConvertible for u16 { fn from_jsval(val: JSVal) -> Option<u16> { unsafe { - Some(RUST_JSVAL_TO_INT(val) as u16) + if RUST_JSVAL_IS_INT(val) != 0 { + Some(RUST_JSVAL_TO_INT(val) as u16) + } else { + None + } } } } @@ -78,12 +96,12 @@ impl JSValConvertible for bool { } fn from_jsval(val: JSVal) -> Option<bool> { - if val == JSVAL_TRUE { - Some(true) - } else if val == JSVAL_FALSE { - Some(false) - } else { - None + unsafe { + if RUST_JSVAL_IS_BOOLEAN(val) != 0 { + Some(RUST_JSVAL_TO_BOOLEAN(val) != 0) + } else { + None + } } } } @@ -97,7 +115,11 @@ impl JSValConvertible for f32 { fn from_jsval(val: JSVal) -> Option<f32> { unsafe { - Some(RUST_JSVAL_TO_DOUBLE(val) as f32) + if RUST_JSVAL_IS_DOUBLE(val) != 0 { + Some(RUST_JSVAL_TO_DOUBLE(val) as f32) + } else { + None + } } } } @@ -111,7 +133,11 @@ impl JSValConvertible for f64 { fn from_jsval(val: JSVal) -> Option<f64> { unsafe { - Some(RUST_JSVAL_TO_DOUBLE(val) as f64) + if RUST_JSVAL_IS_DOUBLE(val) != 0 { + Some(RUST_JSVAL_TO_DOUBLE(val) as f64) + } else { + None + } } } } diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 595d131c80c..80787d93416 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -879,6 +879,15 @@ pub fn throw_method_failed_with_details<T>(cx: *JSContext, return 0; } +pub fn throw_not_in_union(cx: *JSContext, names: &'static str) -> JSBool { + assert!(unsafe { JS_IsExceptionPending(cx) } == 0); + let message = format!("argument could not be converted to any of: {}", names); + message.with_c_str(|string| { + unsafe { ReportError(cx, string) }; + }); + return 0; +} + /// Execute arbitrary code with the JS GC enabled, then disable it afterwards. pub fn with_gc_enabled<R>(cx: *JSContext, f: || -> R) -> R { unsafe { diff --git a/src/components/script/dom/htmlselectelement.rs b/src/components/script/dom/htmlselectelement.rs index 92925b4ce7c..1b7e452a992 100644 --- a/src/components/script/dom/htmlselectelement.rs +++ b/src/components/script/dom/htmlselectelement.rs @@ -4,6 +4,7 @@ use dom::bindings::codegen::HTMLSelectElementBinding; use dom::bindings::codegen::InheritTypes::HTMLSelectElementDerived; +use dom::bindings::codegen::UnionTypes::{HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement}; use dom::bindings::js::JS; use dom::bindings::utils::ErrorResult; use dom::document::Document; @@ -175,4 +176,8 @@ impl HTMLSelectElement { pub fn SetCustomValidity(&mut self, _error: DOMString) { } + + pub fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) -> ErrorResult { + Ok(()) + } } diff --git a/src/components/script/dom/webidls/HTMLSelectElement.webidl b/src/components/script/dom/webidls/HTMLSelectElement.webidl index b99bf149280..bb843fd3f92 100644 --- a/src/components/script/dom/webidls/HTMLSelectElement.webidl +++ b/src/components/script/dom/webidls/HTMLSelectElement.webidl @@ -32,8 +32,8 @@ interface HTMLSelectElement : HTMLElement { attribute unsigned long length; getter Element? item(unsigned long index); HTMLOptionElement? namedItem(DOMString name); - /*[Throws] - void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);*/ + [Throws] + void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null); void remove(long index); [Throws] setter creator void (unsigned long index, HTMLOptionElement? option); diff --git a/src/components/script/script.rs b/src/components/script/script.rs index 8cfbc737255..1d23dfc3402 100644 --- a/src/components/script/script.rs +++ b/src/components/script/script.rs @@ -40,6 +40,8 @@ pub mod dom { pub mod PrototypeList; pub mod RegisterBindings; pub mod BindingDeclarations; + pub mod UnionConversions; + pub mod UnionTypes; } } diff --git a/src/test/html/content/harness.js b/src/test/html/content/harness.js index ade4536a879..72aa2712469 100644 --- a/src/test/html/content/harness.js +++ b/src/test/html/content/harness.js @@ -37,6 +37,24 @@ function is_function(val, name) { starts_with(String(val), "function " + name + "("); } +function should_throw(f) { + try { + f(); + _fail("operation should have thrown but did not"); + } catch (x) { + _pass("operation successfully threw an exception", x.toString()); + } +} + +function should_not_throw(f) { + try { + f(); + _pass("operation did not throw an exception"); + } catch (x) { + _fail("operation should have not thrown", x.toString()); + } +} + var _test_complete = false; var _test_timeout = 10000; //10 seconds function finish() { diff --git a/src/test/html/content/test_exception.html b/src/test/html/content/test_exception.html index ed96fe22867..b2cfda177d4 100644 --- a/src/test/html/content/test_exception.html +++ b/src/test/html/content/test_exception.html @@ -1,11 +1,6 @@ <!doctype html> <script src="harness.js"></script> <script> -try { - document.createElement("1foo"); - is(true, false, "No exception thrown"); -} catch (e) { - is(true, true, "Exception caught"); -} +should_throw(function() { document.createElement("1foo") }); finish(); </script> diff --git a/src/test/html/content/test_union.html b/src/test/html/content/test_union.html new file mode 100644 index 00000000000..d5483ad8a3a --- /dev/null +++ b/src/test/html/content/test_union.html @@ -0,0 +1,26 @@ +<html> +<head> +<script src="harness.js"></script> +<select id="sel"></select> +<script> + var div = document.createElement('div'); + var optgroup = document.createElement('optgroup'); + var sel = document.getElementById('sel'); + + should_not_throw(function() { + var opt = document.createElement('option'); + sel.add(opt); + sel.add(optgroup); + sel.add(opt, div); + sel.add(optgroup, div); + sel.add(opt, 5); + sel.add(optgroup, 5); + }); + + should_throw(function() { sel.add(div) }); + should_throw(function() { sel.add(optgroup, function() {}) }); + + finish(); +</script> +</head> +</html> |