Bug 863094 - Infinity/-Infinity/NaN defaults for unrestricted types, r=bz
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 18 Apr 2013 19:58:01 +0300
changeset 140176 d214b2f5a61f06e0efbea845e1698f84e857ddb4
parent 140175 2dd4a898a3534efc084885825dc218727c498bad
child 140177 1bc754a570f0f859c7798684c17eb9bcba815011
push idunknown
push userunknown
push dateunknown
reviewersbz
bugs863094
milestone23.0a1
Bug 863094 - Infinity/-Infinity/NaN defaults for unrestricted types, r=bz
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_dictionary.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3,16 +3,17 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Common codegen classes.
 
 import operator
 import os
 import re
 import string
+import math
 
 from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType
 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
 
 AUTOGENERATED_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
 ADDPROPERTY_HOOK_NAME = '_addProperty'
 FINALIZE_HOOK_NAME = '_finalize'
@@ -2040,20 +2041,33 @@ numericSuffixes = {
     IDLType.Tags.int8: '',
     IDLType.Tags.uint8: '',
     IDLType.Tags.int16: '',
     IDLType.Tags.uint16: '',
     IDLType.Tags.int32: '',
     IDLType.Tags.uint32: 'U',
     IDLType.Tags.int64: 'LL',
     IDLType.Tags.uint64: 'ULL',
+    IDLType.Tags.unrestricted_float: 'F',
     IDLType.Tags.float: 'F',
+    IDLType.Tags.unrestricted_double: 'D',
     IDLType.Tags.double: 'D'
 }
 
+def numericValue(t, v):
+    if (t == IDLType.Tags.unrestricted_double or
+        t == IDLType.Tags.unrestricted_float):
+        if v == float("inf"):
+            return "MOZ_DOUBLE_POSITIVE_INFINITY()"
+        if v == float("-inf"):
+            return "MOZ_DOUBLE_NEGATIVE_INFINITY()"
+        if math.isnan(v):
+            return "MOZ_DOUBLE_NaN()"
+    return "%s%s" % (v, numericSuffixes[t])
+
 class CastableObjectUnwrapper():
     """
     A class for unwrapping an object named by the "source" argument
     based on the passed-in descriptor and storing it in a variable
     called by the name in the "target" argument.
 
     codeOnFailure is the code to run if unwrapping fails.
     """
@@ -3174,17 +3188,17 @@ for (uint32_t i = 0; i < length; ++i) {
                      "}" % (readLoc, nonFiniteCode))
 
     if (defaultValue is not None and
         # We already handled IDLNullValue, so just deal with the other ones
         not isinstance(defaultValue, IDLNullValue)):
         tag = defaultValue.type.tag()
         if tag in numericSuffixes:
             # Some numeric literals require a suffix to compile without warnings
-            defaultStr = "%s%s" % (defaultValue.value, numericSuffixes[tag])
+            defaultStr = numericValue(tag, defaultValue.value)
         else:
             assert(tag == IDLType.Tags.bool)
             defaultStr = toStringBool(defaultValue.value)
         template = CGWrapper(CGIndenter(CGGeneric(template)),
                              pre="if (${haveValue}) {\n",
                              post=("\n"
                                    "} else {\n"
                                    "  %s = %s;\n"
@@ -3281,17 +3295,17 @@ def convertConstIDLValueToJSVal(value):
         return "JSVAL_NULL"
     tag = value.type.tag()
     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                IDLType.Tags.uint16, IDLType.Tags.int32]:
         return "INT_TO_JSVAL(%s)" % (value.value)
     if tag == IDLType.Tags.uint32:
         return "UINT_TO_JSVAL(%sU)" % (value.value)
     if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
-        return "DOUBLE_TO_JSVAL(%s%s)" % (value.value, numericSuffixes[tag])
+        return "DOUBLE_TO_JSVAL(%s)" % numericValue(tag, value.value)
     if tag == IDLType.Tags.bool:
         return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
     if tag in [IDLType.Tags.float, IDLType.Tags.double]:
         return "DOUBLE_TO_JSVAL(%s)" % (value.value)
     raise TypeError("Const value of unhandled type: " + value.type)
 
 class CGArgumentConverter(CGThing):
     """
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -3,16 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 """ A WebIDL parser. """
 
 from ply import lex, yacc
 import re
 import os
 import traceback
+import math
 
 # Machinery
 
 def parseInt(literal):
     string = literal
     sign = 0
     base = 0
 
@@ -2252,16 +2253,23 @@ class IDLValue(IDLObject):
                                   (self.value, type), [location])
         elif self.type.isString() and type.isEnum():
             # Just keep our string, but make sure it's a valid value for this enum
             if self.value not in type.inner.values():
                 raise WebIDLError("'%s' is not a valid default value for enum %s"
                                   % (self.value, type.inner.identifier.name),
                                   [location, type.inner.location])
             return self
+        elif self.type.isFloat() and type.isFloat():
+            if (not type.isUnrestricted() and
+                (self.value == float("inf") or self.value == float("-inf") or
+                 math.isnan(self.value))):
+                raise WebIDLError("Trying to convert unrestricted value %s to non-unrestricted"
+                                  % self.value, [location]);
+            return self
         else:
             raise WebIDLError("Cannot coerce type %s to type %s." %
                               (self.type, type), [location])
 
     def _getDependentObjects(self):
         return set()
 
 class IDLNullValue(IDLObject):
@@ -3143,34 +3151,34 @@ class Tokenizer(object):
         "INTEGER",
         "FLOATLITERAL",
         "IDENTIFIER",
         "STRING",
         "WHITESPACE",
         "OTHER"
         ]
 
+    def t_FLOATLITERAL(self, t):
+        r'(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN'
+        t.value = float(t.value)
+        return t
+
     def t_INTEGER(self, t):
         r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
         try:
             # Can't use int(), because that doesn't handle octal properly.
             t.value = parseInt(t.value)
         except:
             raise WebIDLError("Invalid integer literal",
                               [Location(lexer=self.lexer,
                                         lineno=self.lexer.lineno,
                                         lexpos=self.lexer.lexpos,
                                         filename=self._filename)])
         return t
 
-    def t_FLOATLITERAL(self, t):
-        r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)'
-        assert False
-        return t
-
     def t_IDENTIFIER(self, t):
         r'[A-Z_a-z][0-9A-Z_a-z]*'
         t.type = self.keywords.get(t.value, 'IDENTIFIER')
         return t
 
     def t_STRING(self, t):
         r'"[^"]*"'
         t.value = t.value[1:-1]
@@ -3589,18 +3597,18 @@ class Parser(Tokenizer):
             raise WebIDLError("Integer literal out of range", [location])
 
         p[0] = IDLValue(location, integerType, p[1])
 
     def p_ConstValueFloat(self, p):
         """
             ConstValue : FLOATLITERAL
         """
-        assert False
-        pass
+        location = self.getLocation(p, 1)
+        p[0] = IDLValue(location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1])
 
     def p_ConstValueString(self, p):
         """
             ConstValue : STRING
         """
         location = self.getLocation(p, 1)
         stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
         p[0] = IDLValue(location, stringType, p[1])
--- a/dom/bindings/parser/tests/test_dictionary.py
+++ b/dom/bindings/parser/tests/test_dictionary.py
@@ -437,8 +437,116 @@ def WebIDLTest(parser, harness):
               Foo? d;
             };
         """)
         results = parser.finish()
     except:
         threw = True
 
     harness.ok(threw, "Member type must not be a nullable dictionary")
+
+    parser = parser.reset();
+    parser.parse("""
+        dictionary Foo {
+          unrestricted float  urFloat = 0;
+          unrestricted float  urFloat2 = 1.1;
+          unrestricted float  urFloat3 = -1.1;
+          unrestricted float? urFloat4 = null;
+          unrestricted float  infUrFloat = Infinity;
+          unrestricted float  negativeInfUrFloat = -Infinity;
+          unrestricted float  nanUrFloat = NaN;
+
+          unrestricted double  urDouble = 0;
+          unrestricted double  urDouble2 = 1.1;
+          unrestricted double  urDouble3 = -1.1;
+          unrestricted double? urDouble4 = null;
+          unrestricted double  infUrDouble = Infinity;
+          unrestricted double  negativeInfUrDouble = -Infinity;
+          unrestricted double  nanUrDouble = NaN;
+        };
+    """)
+    results = parser.finish()
+    harness.ok(True, "Parsing default values for unrestricted types succeeded.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              double f = Infinity;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              double f = -Infinity;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              double f = NaN;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Only unrestricted values can be initialized to NaN")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              float f = Infinity;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              float f = -Infinity;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              float f = NaN;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Only unrestricted values can be initialized to NaN")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -224,16 +224,25 @@ public:
                         const Sequence<double>&, const Sequence<double>&,
                         const Sequence<Nullable<double> >&,
                         const Sequence<Nullable<double> >&);
   float LenientFloatAttr() const;
   void SetLenientFloatAttr(float);
   double LenientDoubleAttr() const;
   void SetLenientDoubleAttr(double);
 
+  void PassUnrestricted(float arg1,
+                        float arg2,
+                        float arg3,
+                        float arg4,
+                        double arg5,
+                        double arg6,
+                        double arg7,
+                        double arg8);
+
   // Interface types
   already_AddRefed<TestInterface> ReceiveSelf();
   already_AddRefed<TestInterface> ReceiveNullableSelf();
   TestInterface* ReceiveWeakSelf();
   TestInterface* ReceiveWeakNullableSelf();
   void PassSelf(TestInterface&);
   void PassSelf2(NonNull<TestInterface>&);
   void PassNullableSelf(TestInterface*);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -187,16 +187,25 @@ interface TestInterface {
                         sequence<unrestricted double> arg14,
                         sequence<double?> arg15,
                         sequence<unrestricted double?> arg16);
   [LenientFloat]
   attribute float lenientFloatAttr;
   [LenientFloat]
   attribute double lenientDoubleAttr;
 
+  void passUnrestricted(optional unrestricted float arg1 = 0,
+                        optional unrestricted float arg2 = Infinity,
+                        optional unrestricted float arg3 = -Infinity,
+                        optional unrestricted float arg4 = NaN,
+                        optional unrestricted double arg5 = 0,
+                        optional unrestricted double arg6 = Infinity,
+                        optional unrestricted double arg7 = -Infinity,
+                        optional unrestricted double arg8 = NaN);
+
   // Castable interface types
   // XXXbz add tests for throwing versions of all the castable interface stuff
   TestInterface receiveSelf();
   TestInterface? receiveNullableSelf();
   TestInterface receiveWeakSelf();
   TestInterface? receiveWeakNullableSelf();
   // A verstion to test for casting to TestInterface&
   void passSelf(TestInterface arg);
@@ -584,16 +593,33 @@ dictionary Dict : ParentDict {
   DOMString otherStr = "def";
   DOMString? yetAnotherStr = null;
   DOMString template;
   object someObj;
   boolean prototype;
   object? anotherObj = null;
   TestCallback? someCallback = null;
   any someAny;
+
+  unrestricted float  urFloat = 0;
+  unrestricted float  urFloat2 = 1.1;
+  unrestricted float  urFloat3 = -1.1;
+  unrestricted float? urFloat4 = null;
+  unrestricted float  infUrFloat = Infinity;
+  unrestricted float  negativeInfUrFloat = -Infinity;
+  unrestricted float  nanUrFloat = NaN;
+
+  unrestricted double  urDouble = 0;
+  unrestricted double  urDouble2 = 1.1;
+  unrestricted double  urDouble3 = -1.1;
+  unrestricted double? urDouble4 = null;
+  unrestricted double  infUrDouble = Infinity;
+  unrestricted double  negativeInfUrDouble = -Infinity;
+  unrestricted double  nanUrDouble = NaN;
+
 };
 
 dictionary ParentDict : GrandparentDict {
   long c = 5;
   TestInterface someInterface;
   TestExternalInterface someExternalInterface;
 };