Basic support for nullable primitive arguments
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 08 Feb 2012 11:54:05 -0500
changeset 86460 16141e260c6120a000bf9ebddf3fb4a3da6b03ae
parent 86459 64089ddcb92dbbb546086e52f23fa5e8fbf5baad
child 86461 6cf8fbd470bacafccf8ff7c0b812d51c4fee5f39
push id97
push userbzbarsky@mozilla.com
push dateWed, 08 Feb 2012 16:57:49 +0000
milestone13.0a1
Basic support for nullable primitive arguments
dom/bindings/Codegen.py
dom/bindings/Utils.h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -478,21 +478,34 @@ def getArgumentConversionTemplate(type):
 #    return false;
 #  }"""
 
     if not type.isPrimitive():
         return """
   // XXXbz Need conversion for argument type '%s'""" % type
 
     tag = type.tag()
+    replacements = dict()
+    if type.nullable():
+        replacements["declareArg"] = (
+            "  Nullable<${typeName}> ${name};\n"
+            "  if (${argVal}.isNullOrUndefined()) {\n"
+            "    ${name}.SetNull();\n"
+            "  } else"
+            )
+        replacements["finalValueSetter"] = "${name}.SetValue"
+    else:
+        replacements["declareArg"] = "  ${typeName} ${name};\n"
+        replacements["finalValueSetter"] = "${name} = "
+
+    replacements["intermediateCast"] = ""
+        
     if tag == IDLType.Tags.bool:
-        return ("  JSBool ${name};\n"
-                "  if (!JS_ValueToBoolean(cx, ${argVal}, &${name})) {\n"
-                "    return false;\n"
-                "}\n")
+        replacements["jstype"] = "JSBool"
+        replacements["converter"] = "JS_ValueToBoolean"
     elif tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                  IDLType.Tags.int32, IDLType.Tags.uint32]:
         # XXXbz need to add support for [EnforceRange] and [Clamp]
         # The output of JS_ValueToECMAInt32 is determined as follows:
         #   1) The value is converted to a double
         #   2) Anything that's not a finite double returns 0
         #   3) The double is rounded towards zero to the nearest integer
         #   4) The resulting integer is reduced mod 2^32.  The output of this
@@ -514,50 +527,49 @@ def getArgumentConversionTemplate(type):
         # will become 0.
         #
         # Once we have step 4 done, we're just going to assume 2s-complement
         # representation and cast directly to the type we really want.
         #
         # So we can cast directly for all unsigned types an for int32_t; for
         # the smaller-width signed types we need to cast through the
         # corresponding unsigned type.
+        replacements["jstype"] = "int32_t"
+        replacements["converter"] = "JS_ValueToECMAInt32"
         if tag is IDLType.Tags.int8:
-            intermediate_type = "(uint8_t)"
+            replacements["intermediateCast"] = "(uint8_t)"
         elif tag is IDLType.Tags.int16:
-            intermediate_type = "(uint16_t)"
+            replacements["intermediateCast"] = "(uint16_t)"
         else:
-            intermediate_type = ""
-        return ("  int32_t ${name}_int32;\n"
-                "  if (!JS_ValueToECMAInt32(cx, ${argVal}, &${name}_int32)) {\n"
-                "    return false;\n"
-                "  }\n"
-                "  ${typeName} ${name} = (${typeName})%s${name}_int32;" %
-                intermediate_type)
+            replacements["intermediateCast"] = ""
     elif tag is IDLType.Tags.int64:
         # XXXbz this may not match what WebIDL says to do in terms of reducing
         # mod 2^64.  Should we check?
-        return ("  PRInt64 ${name};\n"
-                "  if (!xpc_qsValueToInt64(cx, ${argVal}, &${name})) {\n"
-                "    return false;\n"
-                "  }")
+        replacements["jstype"] = "PRInt64"
+        replacements["converter"] = "xpc_qsValueToInt64"
     elif tag is IDLType.Tags.uint64:
-        return ("  PRUint64 ${name};\n"
-                "  if (!xpc_qsValueToUint64(cx, ${argVal}, &${name})) {\n"
-                "    return false;\n"
-                "  }")
+        # XXXbz this may not match what WebIDL says to do in terms of reducing
+        # mod 2^64.  Should we check?
+        replacements["jstype"] = "PRUint64"
+        replacements["converter"] = "xpc_qsValueToUint64"
     elif tag in [IDLType.Tags.float, IDLType.Tags.double]:
-        return ("  jsdouble ${name}_jsdouble;\n"
-                "  if (!JS_ValueToNumber(cx, ${argVal}, &${name}_jsdouble) {\n"
-                "    return false;\n"
-                "  }\n"
-                "  ${typeName} ${name} = (${typeName})${name}_jsdouble;")
+        replacements["jstype"] = "jsdouble"
+        replacements["converter"] = "JS_ValueToNumber"
     else:
-            pass
-    return """
-  // XXXbz Need conversion for argument type '%s'""" % type
+        raise TypeError("Unknown primitive type '%s'" % type);
+
+    # We substitute the %(name)s things here.  Our caller will
+    # substitute the ${name} things.
+    return ("  %(jstype)s ${name}_jstype;\n"
+            "%(declareArg)s" # No leading whitespace or newline here, on purpose
+            "  if (%(converter)s(cx, ${argVal}, &${name}_jstype)) {\n"
+            "    %(finalValueSetter)s((${typeName})%(intermediateCast)s${name}_jstype);\n"
+            "  } else {\n"
+            "    return false;\n"
+            "  }\n" % replacements)
 
 class ArgumentConverter():
     """
     A class that takes an IDL argument object, its index in the
     argument list, and the argv and argc strings and generates code to
     unwrap the argument to the right native type.
     """
     def __init__(self, argument, index, argv, argc):
@@ -604,16 +616,19 @@ def getWrapTemplateForType(type):
   // XXXbz need to learn to wrap objects
   return false;"""
 
     if not type.isPrimitive():
         return """
   // XXXbz need to learn to wrap other things
   return false;"""
 
+    if type.nullable():
+        raise TypeError("We don't support nullable primitive return types yet")
+    
     tag = type.tag()
     
     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                IDLType.Tags.uint16, IDLType.Tags.int32]:
         return """
   ${jsvalRef} = INT_TO_JSVAL(int32_t(result));
   return true;"""
 
@@ -732,17 +747,17 @@ class PerSignatureMethodCall(PerSignatur
     def getArgv(self):
         return "argv" if len(self.arguments) > 0 else ""
     def getArgc(self):
         return "argc"
     def unwrap_arguments(self):
         requiredArgs = len(self.arguments)
         while requiredArgs and self.arguments[requiredArgs-1].optional:
             requiredArgs -= 1
-        argv = "jsval *argv = JS_ARGV(cx, vp);" if len(self.arguments) > 0 else ""
+        argv = "jsval *argv = JS_ARGV(cx, vp);\n" if len(self.arguments) > 0 else ""
         return ("""
   // XXXbz is this the right place for this check?  Or should it be more
   // up-front somewhere, not per-signature?
   if (argc < %d) {
     return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
   %s""" % (requiredArgs, argv)) + PerSignatureCall.unwrap_arguments(self)
 
--- a/dom/bindings/Utils.h
+++ b/dom/bindings/Utils.h
@@ -219,13 +219,49 @@ WrapOtherObject(JSContext *cx, JSObject 
     if (!JS_IsExceptionPending(cx))
       XPCThrower::Throw(rv, cx);
     return false;
   }
 
   return true;
 }
 
+// Support for nullable types
+template<typename T>
+struct Nullable
+{
+private:
+  T mValue;
+  bool mIsNull;
+
+public:
+  Nullable() :
+    mIsNull(true)
+  {}
+
+  Nullable(T aValue) :
+    mValue(aValue), mIsNull(false)
+  {}
+
+  void SetValue(T aValue) {
+    mValue = aValue;
+    mIsNull = false;
+  }
+
+  void SetNull() {
+    mIsNull = true;
+  }
+
+  T Value() {
+    MOZ_ASSERT(!mIsNull);
+    return mValue;
+  }
+
+  bool IsNull() {
+    return mIsNull;
+  }
+};
+
 } // namespace bindings
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_bindings_Utils_h__ */