Bug 836816 - WebIDL parser should enforce spec restrictions on dictionary member types. r=bzbarsky
authorPranav Ravichandran <prp.1111@gmail.com>
Wed, 27 Feb 2013 09:55:56 -0500
changeset 123162 95d500777957227364701ae252e0ce9202046483
parent 123161 c87c6b23794ead3fa0da02225d3c835b2736cc6a
child 123163 f118eb2326f7a80806d137bf760de63cb7da800e
push id24373
push userryanvm@gmail.com
push dateThu, 28 Feb 2013 01:36:21 +0000
treeherdermozilla-central@8cb9d6981978 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs836816
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 836816 - WebIDL parser should enforce spec restrictions on dictionary member types. r=bzbarsky
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_dictionary.py
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1018,17 +1018,75 @@ class IDLDictionary(IDLObjectWithScope):
         for inheritedMember in inheritedMembers:
             for member in self.members:
                 if member.identifier.name == inheritedMember.identifier.name:
                     raise WebIDLError("Dictionary %s has two members with name %s" %
                                       (self.identifier.name, member.identifier.name),
                                       [member.location, inheritedMember.location])
 
     def validate(self):
-        pass
+        def typeContainsDictionary(memberType, dictionary):
+            """
+            Returns a tuple whose:
+
+                - First element is a Boolean value indicating whether
+                  memberType contains dictionary.
+
+                - Second element is:
+                    A list of locations that leads from the type that was passed in
+                    the memberType argument, to the dictionary being validated,
+                    if the boolean value in the first element is True.
+
+                    None, if the boolean value in the first element is False.
+            """
+
+            if memberType.nullable() or \
+               memberType.isArray() or \
+               memberType.isSequence():
+                return typeContainsDictionary(memberType.inner, dictionary)
+
+            if memberType.isDictionary():
+                if memberType.inner == dictionary:
+                    return (True, [memberType.location])
+
+                (contains, locations) = dictionaryContainsDictionary(memberType.inner, \
+                                                                     dictionary)
+                if contains:
+                    return (True, [memberType.location] + locations)
+
+            if memberType.isUnion():
+                for member in memberType.flatMemberTypes:
+                    (contains, locations) = typeContainsDictionary(member, dictionary)
+                    if contains:
+                        return (True, locations)
+
+            return (False, None)
+
+        def dictionaryContainsDictionary(dictMember, dictionary):
+            for member in dictMember.members:
+                (contains, locations) = typeContainsDictionary(member.type, dictionary)
+                if contains:
+                    return (True, [member.location] + locations)
+
+            if dictMember.parent:
+                if dictMember.parent == dictionary:
+                    return (True, [dictMember.location])
+                else:
+                    (contains, locations) = dictionaryContainsDictionary(dictMember.parent, dictionary)
+                    if contains:
+                        return (True, [dictMember.location] + locations)
+
+            return (False, None)
+
+        for member in self.members:
+            (contains, locations) = typeContainsDictionary(member.type, self)
+            if contains:
+                raise WebIDLError("Dictionary %s has member with itself as type." %
+                                  self.identifier.name,
+                                  [member.location] + locations)
 
     def addExtendedAttributes(self, attrs):
         assert len(attrs) == 0
 
     def _getDependentObjects(self):
         deps = set(self.members)
         if (self.parent):
             deps.add(self.parent)
--- a/dom/bindings/parser/tests/test_dictionary.py
+++ b/dom/bindings/parser/tests/test_dictionary.py
@@ -291,8 +291,137 @@ def WebIDLTest(parser, harness):
         dictionary A {
         };
         interface X {
           void doFoo(optional (A or DOMString) arg);
         };
     """)
     results = parser.finish()
     harness.ok(True, "Union arg containing a dictionary should actually parse")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              Foo foo;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo3 : Foo {
+              short d;
+            };
+
+            dictionary Foo2 : Foo3 {
+              boolean c;
+            };
+
+            dictionary Foo1 : Foo2 {
+              long a;
+            };
+
+            dictionary Foo {
+              Foo1 b;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Dictionary that "
+                      "inherits from its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              (Foo or DOMString)[]? b;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Nullable type "
+                      "whose inner type includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              (DOMString or Foo) b;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Union type, one of "
+                      "whose member types includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              sequence<sequence<sequence<Foo>>> c;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Sequence type "
+                      "whose element type includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              (DOMString or Foo)[] d;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be an Array type "
+                      "whose element type includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              Foo1 b;
+            };
+
+            dictionary Foo3 {
+              Foo d;
+            };
+
+            dictionary Foo2 : Foo3 {
+              short c;
+            };
+
+            dictionary Foo1 : Foo2 {
+              long a;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Dictionary, one of whose "
+                      "members or inherited members has a type that includes "
+                      "its Dictionary.")