Fix for bug 742195 (Implement the extended attributes for null and undefined handling on strings in Paris bindings). r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Thu, 12 Jul 2012 15:55:30 +0200
changeset 105299 d5f32f0e1c05005f5e2a5bfeb6cd85353add3823
parent 105298 8ba35e8d4457e22ac453627d30e144a59110973e
child 105300 a3b7774cb5be105704f09a5f36169acf7c86354b
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersbz
bugs742195
milestone17.0a1
Fix for bug 742195 (Implement the extended attributes for null and undefined handling on strings in Paris bindings). r=bz.
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1541,17 +1541,19 @@ if (!tmp) {
 }
 ${target} = tmp.forget();""").substitute(self.substitution)
 
 def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
                                     isDefinitelyObject=False,
                                     isMember=False,
                                     isOptional=False,
                                     invalidEnumValueFatal=True,
-                                    defaultValue=None):
+                                    defaultValue=None,
+                                    treatNullAs="Default",
+                                    treatUndefinedAs="Default"):
     """
     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
     testing whether we can convert the argument to the desired type.  That
     means that failures to convert due to the JS value being the wrong type of
     value need to use failureCode instead of throwing exceptions.  Failures to
     convert that are due to JS exceptions (from toString or valueOf methods) or
     out of memory conditions need to throw exceptions no matter what
@@ -2125,24 +2127,32 @@ for (uint32_t i = 0; i < length; ++i) {
             holderType = CGGeneric(holderType)
         # We handle all the optional stuff ourselves; no need for caller to do it.
         return (template, CGGeneric(declType), holderType, False)
 
     if type.isString():
         # XXXbz Need to figure out string behavior based on extended args?  Also, how to
         # detect them?
 
-        # For nullable strings that are not otherwise annotated, null
-        # and undefined become null strings.
+        treatAs = {
+            "Default": "eStringify",
+            "EmptyString": "eEmpty",
+            "Null": "eNull"
+        }
         if type.nullable():
-            nullBehavior = "eNull"
-            undefinedBehavior = "eNull"
-        else:
-            nullBehavior = "eStringify"
-            undefinedBehavior = "eStringify"
+            # For nullable strings null becomes a null string.
+            treatNullAs = "Null"
+            # For nullable strings undefined becomes a null string unless
+            # specified otherwise.
+            if treatUndefinedAs == "Default":
+                treatUndefinedAs = "Null"
+        nullBehavior = treatAs[treatNullAs]
+        if treatUndefinedAs == "Missing":
+            raise TypeError("We don't support [TreatUndefinedAs=Missing]")
+        undefinedBehavior = treatAs[treatUndefinedAs]
 
         def getConversionCode(varName):
             conversionCode = (
                 "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
                 "  return false;\n"
                 "}" % (nullBehavior, undefinedBehavior, varName))
             if defaultValue is None:
                 return conversionCode
@@ -2480,17 +2490,19 @@ class CGArgumentConverter(CGThing):
         self.invalidEnumValueFatal = invalidEnumValueFatal
 
     def define(self):
         return instantiateJSToNativeConversionTemplate(
             getJSToNativeConversionTemplate(self.argument.type,
                                             self.descriptorProvider,
                                             isOptional=(self.argcAndIndex is not None),
                                             invalidEnumValueFatal=self.invalidEnumValueFatal,
-                                            defaultValue=self.argument.defaultValue),
+                                            defaultValue=self.argument.defaultValue,
+                                            treatNullAs=self.argument.treatNullAs,
+                                            treatUndefinedAs=self.argument.treatUndefinedAs),
             self.replacementVariables,
             self.argcAndIndex).define()
 
 def getWrapTemplateForType(type, descriptorProvider, result, successCode,
                            isCreator):
     """
     Reflect a C++ value stored in "result", of IDL type "type" into JS.  The
     "successCode" is the code to run once we have successfully done the
@@ -3257,16 +3269,18 @@ class CGGetterCall(CGPerSignatureCall):
                                     attr, getter=True)
 
 class FakeArgument():
     def __init__(self, type):
         self.type = type
         self.optional = False
         self.variadic = False
         self.defaultValue = None
+        self.treatNullAs = "Default"
+        self.treatUndefinedAs = "Default"
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
         CGPerSignatureCall.__init__(self, None, [], [FakeArgument(argType)],
@@ -4353,17 +4367,19 @@ class CGProxySpecialOperation(CGPerSigna
         # CGPerSignatureCall won't do any argument conversion of its own.
         CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
                                     False, descriptor, operation,
                                     len(arguments))
 
         if operation.isSetter() or operation.isCreator():
             # arguments[0] is the index or name of the item that we're setting.
             argument = arguments[1]
-            template = getJSToNativeConversionTemplate(argument.type, descriptor)
+            template = getJSToNativeConversionTemplate(argument.type, descriptor,
+                                                       treatNullAs=argument.treatNullAs,
+                                                       treatUndefinedAs=argument.treatUndefinedAs)
             templateValues = {
                 "declName": argument.identifier.name,
                 "holderName": argument.identifier.name + "_holder",
                 "val": "desc->value",
                 "valPtr": "&desc->value"
             }
             self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
         elif operation.isGetter():
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1896,31 +1896,61 @@ class IDLArgument(IDLObjectWithIdentifie
         assert isinstance(type, IDLType)
         self.type = type
 
         self.optional = optional
         self.defaultValue = defaultValue
         self.variadic = variadic
         self.dictionaryMember = dictionaryMember
         self._isComplete = False
+        self.treatNullAs = "Default"
+        self.treatUndefinedAs = "Default"
 
         assert not variadic or optional
 
     def addExtendedAttributes(self, attrs):
-        if self.dictionaryMember:
-            for (attr, value) in attrs:
-                if attr == "TreatUndefinedAs":
+        for (attr, value) in attrs:
+            if attr == "TreatNullAs":
+                if not self.type.isString() or self.type.nullable():
+                    raise WebIDLError("[TreatNullAs] is only allowed on "
+                                      "arguments whose type is DOMString",
+                                      [self.location])
+                if self.dictionaryMember:
+                    raise WebIDLError("[TreatNullAs] is not allowed for "
+                                      "dictionary members", [self.location])
+                if value != 'EmptyString':
+                    raise WebIDLError("[TreatNullAs] must take the identifier "
+                                      "EmptyString", [self.location])
+                self.treatNullAs = value
+            elif attr == "TreatUndefinedAs":
+                if not self.type.isString():
+                    raise WebIDLError("[TreatUndefinedAs] is only allowed on "
+                                      "arguments whose type is DOMString or "
+                                      "DOMString?", [self.location])
+                if self.dictionaryMember:
                     raise WebIDLError("[TreatUndefinedAs] is not allowed for "
                                       "dictionary members", [self.location])
-                elif attr == "TreatNullAs":
-                    raise WebIDLError("[TreatNullAs] is not allowed for "
-                                      "dictionary members", [self.location])
-
-        # But actually, we can't handle this at all, so far.
-        assert len(attrs) == 0
+                if value == 'Null':
+                    if not self.type.nullable():
+                        raise WebIDLError("[TreatUndefinedAs=Null] is only "
+                                          "allowed on arguments whose type is "
+                                          "DOMString?", [self.location])
+                elif value == 'Missing':
+                    if not self.optional:
+                        raise WebIDLError("[TreatUndefinedAs=Missing] is only "
+                                          "allowed on optional arguments",
+                                          [self.location])
+                elif value != 'EmptyString':
+                    raise WebIDLError("[TreatUndefinedAs] must take the "
+                                      "identifiers EmptyString or Null or "
+                                      "Missing", [self.location])
+                self.treatUndefinedAs = value
+            else:
+                raise WebIDLError("Unhandled extended attribute on an argument",
+                                  [self.location])
 
     def isComplete(self):
         return self._isComplete
 
     def complete(self, scope):
         if self._isComplete:
             return