From d0bfdbf15034e37f921f8a91c1fa4c7ec737170a Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 13 Feb 2014 11:50:01 +0530 Subject: [PATCH 1/6] Initial unmodified dump of Gecko's union codegen. --- .../dom/bindings/codegen/CodegenRust.py | 486 ++++++++++++++++++ .../script/dom/bindings/codegen/GlobalGen.py | 5 +- 2 files changed, 488 insertions(+), 3 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index da0ad3dd8df..7b6f8860f76 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -2385,6 +2385,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 +3650,311 @@ 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, 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. + # 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 + 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, initialValue) = 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 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): @@ -6254,3 +6665,78 @@ 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) + + # 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) + + # 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() From d590a327bdf816d1838b4de0bcc36972a7cdc592 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 13 Feb 2014 15:45:39 +0530 Subject: [PATCH 2/6] Implement WebIDL union support. --- .../dom/bindings/codegen/CodegenRust.py | 275 ++++++++++-------- .../script/dom/htmlselectelement.rs | 5 + .../dom/webidls/HTMLSelectElement.webidl | 4 +- src/components/script/script.rs | 2 + src/test/html/content/test_union.html | 35 +++ 5 files changed, 194 insertions(+), 127 deletions(-) create mode 100644 src/test/html/content/test_union.html diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 7b6f8860f76..aefce8e7a93 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("({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext" % (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 = ({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 # 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 = ({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext;" % (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 = ({failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, &mut tryNext); failed}) || !tryNext;" % (unionArgumentObj, name)) names.append(name) if hasObjectTypes: other = CGWrapper(CGIndenter(other), "{\n", post="\n}") @@ -844,29 +850,33 @@ 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 failed = false;\n" + "let mut tryNext = false;\n") + throw = CGGeneric("if failed {\n" + " return 0;\n" "}\n" - "if (!done) {\n" - " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n" + "if !done {\n" + " //XXXjdm throw exception\n" + " //return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n" + " return 0;" "}" % ", ".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& >(${declName})" - else: - nonConstDecl = "const_cast<" + typeName + "& >(${declName})" - typeName = "const " + typeName + typeName = "Option<" + typeName + " >" + #if isOptional: + # nonConstDecl = "const_cast& >(${declName})" + #else: + # nonConstDecl = "const_cast<" + typeName + "& >(${declName})" + nonConstDecl = "${declName}" def handleNull(templateBody, setToNullVar, extraConditionForNull=""): - null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n" - " %s.SetNull();\n" - "}" % (extraConditionForNull, setToNullVar)) + null = CGGeneric("if %sRUST_JSVAL_IS_NULL(${val}) != 0 || %sRUST_JSVAL_IS_VOID(${val}) != 0 {\n" + " %s = None;\n" + "}" % (extraConditionForNull, extraConditionForNull, setToNullVar)) templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}") return CGList([null, templateBody], " else ") @@ -878,21 +888,28 @@ 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 + #if nullable: + #holderType = CGWrapper(holderType, pre="Option<", post=" >") + # constructHolder = CGGeneric("${holderName} = Some(None);") + #else: + 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 +920,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 +986,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 +1273,41 @@ for (uint32_t i = 0; i < length; ++i) { elif isClamp: conversionBehavior = "eClamp" - if type.nullable(): + if failureCode is None: + failureCode = 'return 0' + + if type.nullable(): #or isOptional: 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 @@ -3686,17 +3714,17 @@ def getUnionAccessorSignatureType(type, descriptorProvider): 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="*") + typeName = CGWrapper(typeName, pre="Option<", post=">") else: - typeName = CGWrapper(typeName, post="&") + typeName = CGWrapper(typeName, pre="&'a ") return typeName if type.isSpiderMonkeyInterface(): typeName = CGGeneric(type.name) if type.nullable(): - typeName = CGWrapper(typeName, post="*") + typeName = CGWrapper(typeName, pre="Option<", post=">") else: - typeName = CGWrapper(typeName, post="&") + typeName = CGWrapper(typeName, pre="&") return typeName if type.isString(): @@ -3740,23 +3768,33 @@ def getUnionTypeTemplateVars(type, descriptorProvider): 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 = """tryNext = true; -return true;""" + tryNextCode = """{ + *tryNext = true; + return true; +}""" if type.isGeckoInterface(): - tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) { + tryNextCode = ("""/*if (mUnion.mType != mUnion.eUninitialized) { mUnion.Destroy%s(); -}""" % name) + tryNextCode +}*/""" % name) + tryNextCode (template, declType, holderType, dealWithOptional, initialValue) = getJSToNativeConversionTemplate( 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 # arguments so the type can't be const. @@ -3766,17 +3804,15 @@ return true;""" 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" + setter = CGGeneric("pub fn SetToObject(obj: *JSObject) {\n" + " mUnion = Some(eObject(obj));\n" "}") else: jsConversion = string.Template(template).substitute( { "val": "value", "valPtr": "pvalue", - "declName": "SetAs" + name + "()", + "declName": "*self.mUnion", "holderName": "m" + name + "Holder" } ) @@ -3784,16 +3820,17 @@ return true;""" 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", + pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal, tryNext: &mut bool) -> bool {\n" + " *tryNext = false;\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 } @@ -3819,75 +3856,47 @@ class CGUnionStruct(CGThing): callDestructors.append(" case eNull:\n" " break;") enumValues.append("eNull") - methods.append(""" bool IsNull() const - { - return mType == eNull; + methods.append(""" pub fn IsNull(&self) -> bool { + match *self { + eNull => true, + _ => false + } }""") - destructorTemplate = """ void Destroy${name}() - { - MOZ_ASSERT(Is${name}(), "Wrong type!"); - mValue.m${name}.Destroy(); - mType = eUninitialized; + destructorTemplate = """ fn Destroy${name}(&mut self) { + assert!(Is${name}(), "Wrong type!"); + //mValue.m${name}.Destroy(); + //mType = eUninitialized; + *self.mUnion = None; }""" 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}; + enumValues.extend(mapTemplate("e${name}(${typeName})", templateVars)) + methodTemplate = """ pub fn Is${name}(&self) -> bool { + match *self { + e${name}(_) => true, + _ => false + } } - ${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 to T& and T is forward declared. - return (${externalType})mValue.m${name}.Value(); - } - ${structType}& SetAs${name}() - { - mType = e${name}; - return mValue.m${name}.SetValue(); + 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(""" -class ${structName} { -public: - ${structName}() : mType(eUninitialized) - { - } - ~${structName}() - { - switch (mType) { -${callDestructors} - case eUninitialized: - break; - } - } +pub enum ${structName} { + ${enumValues} +} +impl ${structName} { ${methods} - -private: - friend class ${structName}Argument; - -${destructors} - - enum Type { - eUninitialized, - ${enumValues} - }; - union Value { - ${values} - }; - - Type mType; - Value mValue; -}; - +} """).substitute( { "structName": self.type.__str__(), @@ -3912,9 +3921,9 @@ class CGUnionConversionStruct(CGThing): setters = [] 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; }""") @@ -3923,7 +3932,7 @@ class CGUnionConversionStruct(CGThing): structName = self.type.__str__() 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}; return mUnion.mValue.m${name}.SetValue(); @@ -3935,20 +3944,22 @@ class CGUnionConversionStruct(CGThing): private += "\n\n" private += " " + structName + "& mUnion;" return string.Template(""" -class ${structName}Argument { -public: - ${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion)) - { +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} - -private: -${private} -}; +} """).substitute({"structName": structName, + "innerType": ("Option<%s>" % structName) if self.type.nullable() else structName, "setters": "\n\n".join(setters), - "private": private }) def define(self): @@ -5580,6 +5591,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::*', @@ -5591,6 +5604,7 @@ class CGBindingRoot(CGThing): 'std::vec', 'std::str', 'std::num', + 'std::unstable::intrinsics::uninit', 'std::unstable::raw::Box', ], [], @@ -6713,6 +6727,9 @@ class GlobalGenRoots(): # 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) @@ -6735,6 +6752,14 @@ class GlobalGenRoots(): # 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) 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) -> 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 d85df63ba08..36fab3d7f82 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/test_union.html b/src/test/html/content/test_union.html new file mode 100644 index 00000000000..a2eb4204b0d --- /dev/null +++ b/src/test/html/content/test_union.html @@ -0,0 +1,35 @@ + + + + + + + From 9f05f70754f45939c05223563b5a5e5254bc5f45 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 19 Feb 2014 13:20:04 -0500 Subject: [PATCH 3/6] Clean up TrySetTo interface to avoid mutable outrefs. --- .../dom/bindings/codegen/CodegenRust.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index aefce8e7a93..330d414c84d 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -726,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}, &mut tryNext); failed}) || !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: @@ -737,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}, &mut tryNext); failed}) || !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. @@ -767,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}, &mut tryNext); failed}) || !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 @@ -837,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}, &mut tryNext); failed}) || !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}") @@ -851,9 +851,8 @@ for (uint32_t i = 0; i < length; ++i) { other = None templateBody = CGWrapper(templateBody, pre="let mut done = false;\n" - "let mut failed = false;\n" - "let mut tryNext = false;\n") - throw = CGGeneric("if failed {\n" + "let mut res = Ok(true);\n") + throw = CGGeneric("if res.is_err() {\n" " return 0;\n" "}\n" "if !done {\n" @@ -3784,8 +3783,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): typeName = "/*" + type.name + "*/" tryNextCode = """{ - *tryNext = true; - return true; + return Ok(true); }""" if type.isGeckoInterface(): tryNextCode = ("""/*if (mUnion.mType != mUnion.eUninitialized) { @@ -3818,10 +3816,9 @@ def getUnionTypeTemplateVars(type, descriptorProvider): ) jsConversion = CGWrapper(CGGeneric(jsConversion), post="\n" - "return true;") + "return Ok(true);") setter = CGWrapper(CGIndenter(jsConversion), - pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal, tryNext: &mut bool) -> bool {\n" - " *tryNext = false;\n", + pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal) -> Result {\n", post="\n" "}") From 4cdb4df70c60e198a87491d0325d487459d15ea4 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 19 Feb 2014 13:27:51 -0500 Subject: [PATCH 4/6] Remove commented bits that aren't necessary. --- .../script/dom/bindings/codegen/CodegenRust.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 330d414c84d..152c2bb7453 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -866,10 +866,6 @@ for (uint32_t i = 0; i < length; ++i) { argumentTypeName = typeName + "Argument" if nullable: typeName = "Option<" + typeName + " >" - #if isOptional: - # nonConstDecl = "const_cast& >(${declName})" - #else: - # nonConstDecl = "const_cast<" + typeName + "& >(${declName})" nonConstDecl = "${declName}" def handleNull(templateBody, setToNullVar, extraConditionForNull=""): @@ -896,10 +892,6 @@ for (uint32_t i = 0; i < length; ++i) { else: mutableDecl = nonConstDecl constructDecl = None - #if nullable: - #holderType = CGWrapper(holderType, pre="Option<", post=" >") - # constructHolder = CGGeneric("${holderName} = Some(None);") - #else: holderInit = "${declName}" if nullable: holderInit += ".get_mut_ref()" @@ -1275,7 +1267,7 @@ for (uint32_t i = 0; i < length; ++i) { if failureCode is None: failureCode = 'return 0' - if type.nullable(): #or isOptional: + 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): @@ -3785,10 +3777,6 @@ def getUnionTypeTemplateVars(type, descriptorProvider): tryNextCode = """{ return Ok(true); }""" - if type.isGeckoInterface(): - tryNextCode = ("""/*if (mUnion.mType != mUnion.eUninitialized) { - mUnion.Destroy%s(); -}*/""" % name) + tryNextCode (template, declType, holderType, dealWithOptional, initialValue) = getJSToNativeConversionTemplate( type, descriptorProvider, failureCode=tryNextCode, From 02d2ec5ccd4b4e52a1963e4fa844ce1e3e7b17ea Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 24 Feb 2014 17:18:47 -0500 Subject: [PATCH 5/6] Make JSVal conversions actually fallible. --- .../script/dom/bindings/conversions.rs | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) 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 { 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 { 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 { 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 { 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 { - 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 { 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 { 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 + } } } } From 232ca597089cff647fcdef2a3cfd2030393ba84b Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 24 Feb 2014 17:20:37 -0500 Subject: [PATCH 6/6] Address review comments. Fix union conversion result mistakes introduced in refactoring. --- .../dom/bindings/codegen/CodegenRust.py | 16 +++------ src/components/script/dom/bindings/utils.rs | 9 +++++ src/test/html/content/harness.js | 18 ++++++++++ src/test/html/content/test_exception.html | 7 +--- src/test/html/content/test_union.html | 35 +++++++------------ 5 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 152c2bb7453..ca04b873aab 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -856,9 +856,7 @@ for (uint32_t i = 0; i < length; ++i) { " return 0;\n" "}\n" "if !done {\n" - " //XXXjdm throw exception\n" - " //return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n" - " return 0;" + " return throw_not_in_union(cx, \"%s\");\n" "}" % ", ".join(names)) templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}") @@ -869,9 +867,9 @@ for (uint32_t i = 0; i < length; ++i) { nonConstDecl = "${declName}" def handleNull(templateBody, setToNullVar, extraConditionForNull=""): - null = CGGeneric("if %sRUST_JSVAL_IS_NULL(${val}) != 0 || %sRUST_JSVAL_IS_VOID(${val}) != 0 {\n" + null = CGGeneric("if %s(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0) {\n" " %s = None;\n" - "}" % (extraConditionForNull, extraConditionForNull, setToNullVar)) + "}" % (extraConditionForNull, setToNullVar)) templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}") return CGList([null, templateBody], " else ") @@ -3782,11 +3780,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): type, descriptorProvider, failureCode=tryNextCode, isDefinitelyObject=True, isOptional=type.nullable(), preSuccess="e" + name + "(", postSuccess=")") - # 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(): @@ -3804,7 +3798,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): ) jsConversion = CGWrapper(CGGeneric(jsConversion), post="\n" - "return Ok(true);") + "return Ok(false);") setter = CGWrapper(CGIndenter(jsConversion), pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal) -> Result {\n", post="\n" @@ -3850,8 +3844,6 @@ class CGUnionStruct(CGThing): destructorTemplate = """ fn Destroy${name}(&mut self) { assert!(Is${name}(), "Wrong type!"); - //mValue.m${name}.Destroy(); - //mType = eUninitialized; *self.mUnion = None; }""" destructors = mapTemplate(destructorTemplate, templateVars) 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(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(cx: *JSContext, f: || -> R) -> R { unsafe { 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 @@ diff --git a/src/test/html/content/test_union.html b/src/test/html/content/test_union.html index a2eb4204b0d..d5483ad8a3a 100644 --- a/src/test/html/content/test_union.html +++ b/src/test/html/content/test_union.html @@ -3,31 +3,22 @@