Bug 1359269 - Part 8: Make it a hard error to apply type attributes on arguments, attrs, and dict members when it's unambiguous, update webidls; r=bzbarsky
☠☠ backed out by 0c70617150d9 ☠ ☠
authorManish Goregaokar <manishearth@gmail.com>
Sat, 02 Mar 2019 01:23:28 +0000
changeset 519948 8f9509e82439ccd4544624d7f52773698db3ea3c
parent 519947 bfb153c7f9c39160da697d3e2af5bcb7f594547b
child 519949 ae30401e7988d753acd0ae8f9086e9129b6bbb1e
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1359269
milestone67.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 1359269 - Part 8: Make it a hard error to apply type attributes on arguments, attrs, and dict members when it's unambiguous, update webidls; r=bzbarsky Depends on D20058 Differential Revision: https://phabricator.services.mozilla.com/D20059
dom/bindings/parser/WebIDL.py
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
dom/bindings/test/TestJSImplGen.webidl
dom/webidl/Blob.webidl
dom/webidl/IDBIndex.webidl
dom/webidl/IDBObjectStore.webidl
dom/webidl/SubtleCrypto.webidl
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -2251,18 +2251,19 @@ class IDLType(IDLObject):
         return True
 
 
 class IDLUnresolvedType(IDLType):
     """
         Unresolved types are interface types
     """
 
-    def __init__(self, location, name):
+    def __init__(self, location, name, attrs=[]):
         IDLType.__init__(self, location, name)
+        self.extraTypeAttributes = attrs
 
     def isComplete(self):
         return False
 
     def complete(self, scope):
         obj = None
         try:
             obj = scope._lookupIdentifier(self.name)
@@ -2274,24 +2275,27 @@ class IDLUnresolvedType(IDLType):
         if obj.isType():
             print obj
         assert not obj.isType()
         if obj.isTypedef():
             assert self.name.name == obj.identifier.name
             typedefType = IDLTypedefType(self.location, obj.innerType,
                                          obj.identifier)
             assert not typedefType.isComplete()
-            return typedefType.complete(scope)
+            return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes)
         elif obj.isCallback() and not obj.isInterface():
             assert self.name.name == obj.identifier.name
             return IDLCallbackType(self.location, obj)
 
         name = self.name.resolve(scope, None)
         return IDLWrapperType(self.location, obj)
 
+    def withExtendedAttributes(self, attrs):
+        return IDLUnresolvedType(self.location, self.name, attrs)
+
     def isDistinguishableFrom(self, other):
         raise TypeError("Can't tell whether an unresolved type is or is not "
                         "distinguishable from other things")
 
 
 class IDLParametrizedType(IDLType):
     def __init__(self, location, name, innerType):
         IDLType.__init__(self, location, name)
@@ -2789,16 +2793,19 @@ class IDLTypedefType(IDLType):
         return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         return self.inner.isDistinguishableFrom(other)
 
     def _getDependentObjects(self):
         return self.inner._getDependentObjects()
 
+    def withExtendedAttributes(self, attrs):
+        return IDLTypedefType(self.location, self.inner.withExtendedAttributes(attrs), self.name)
+
 
 class IDLTypedef(IDLObjectWithIdentifier):
     def __init__(self, location, parentScope, innerType, name):
         # Set self.innerType first, because IDLObjectWithIdentifier.__init__
         # will call our __str__, which wants to use it.
         self.innerType = innerType
         identifier = IDLUnresolvedIdentifier(location, name)
         IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
@@ -4228,16 +4235,19 @@ class IDLAttribute(IDLInterfaceMember):
         if not self.type.isComplete():
             t = self.type.complete(scope)
 
             assert not isinstance(t, IDLUnresolvedType)
             assert not isinstance(t, IDLTypedefType)
             assert not isinstance(t.name, IDLUnresolvedIdentifier)
             self.type = t
 
+        if self.readonly and (self.type.clamp or self.type.enforceRange):
+            raise WebIDLError("A readonly attribute cannot be [Clamp] or [EnforceRange]",
+                              [self.location])
         if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
             raise WebIDLError("An attribute cannot be of a dictionary type",
                               [self.location])
         if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
             raise WebIDLError("A non-cached attribute cannot be of a sequence "
                               "type", [self.location])
         if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
             raise WebIDLError("A non-cached attribute cannot be of a record "
@@ -4451,26 +4461,16 @@ class IDLAttribute(IDLInterfaceMember):
         elif identifier == "LenientFloat":
             if self.readonly:
                 raise WebIDLError("[LenientFloat] used on a readonly attribute",
                                   [attr.location, self.location])
             if not self.type.includesRestrictedFloat():
                 raise WebIDLError("[LenientFloat] used on an attribute with a "
                                   "non-restricted-float type",
                                   [attr.location, self.location])
-        elif identifier == "EnforceRange":
-            if self.readonly:
-                raise WebIDLError("[EnforceRange] used on a readonly attribute",
-                                  [attr.location, self.location])
-            self.enforceRange = True
-        elif identifier == "Clamp":
-            if self.readonly:
-                raise WebIDLError("[Clamp] used on a readonly attribute",
-                                  [attr.location, self.location])
-            self.clamp = True
         elif identifier == "StoreInSlot":
             if self.getExtendedAttribute("Cached"):
                 raise WebIDLError("[StoreInSlot] and [Cached] must not be "
                                   "specified on the same attribute",
                                   [attr.location, self.location])
         elif identifier == "Cached":
             if self.getExtendedAttribute("StoreInSlot"):
                 raise WebIDLError("[Cached] and [StoreInSlot] must not be "
@@ -4585,58 +4585,45 @@ class IDLAttribute(IDLInterfaceMember):
     def isUnforgeable(self):
         return self._unforgeable
 
     def _getDependentObjects(self):
         return set([self.type])
 
 
 class IDLArgument(IDLObjectWithIdentifier):
-    def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
+    def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False, allowTypeAttributes=False):
         IDLObjectWithIdentifier.__init__(self, location, None, identifier)
 
         assert isinstance(type, IDLType)
         self.type = type
 
         self.optional = optional
         self.defaultValue = defaultValue
         self.variadic = variadic
         self.dictionaryMember = dictionaryMember
         self._isComplete = False
         self.enforceRange = False
         self.clamp = False
         self._allowTreatNonCallableAsNull = False
         self._extendedAttrDict = {}
+        self.allowTypeAttributes = allowTypeAttributes
 
         assert not variadic or optional
         assert not variadic or not defaultValue
 
     def addExtendedAttributes(self, attrs):
         attrs = self.checkForStringHandlingExtendedAttributes(
             attrs,
             isDictionaryMember=self.dictionaryMember,
             isOptional=self.optional)
         for attribute in attrs:
             identifier = attribute.identifier()
-            if identifier == "Clamp":
-                if not attribute.noArguments():
-                    raise WebIDLError("[Clamp] must take no arguments",
-                                      [attribute.location])
-                if self.enforceRange:
-                    raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
-                                      [self.location])
-                self.clamp = True
-            elif identifier == "EnforceRange":
-                if not attribute.noArguments():
-                    raise WebIDLError("[EnforceRange] must take no arguments",
-                                      [attribute.location])
-                if self.clamp:
-                    raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
-                                      [self.location])
-                self.enforceRange = True
+            if self.allowTypeAttributes and (identifier == "EnforceRange" or identifier == "Clamp"):
+                self.type = self.type.withExtendedAttributes([attribute])
             elif identifier == "TreatNonCallableAsNull":
                 self._allowTreatNonCallableAsNull = True
             elif (self.dictionaryMember and
                   (identifier == "ChromeOnly" or identifier == "Func")):
                 if not self.optional:
                     raise WebIDLError("[%s] must not be used on a required "
                                       "dictionary member" % identifier,
                                       [attribute.location])
@@ -5934,20 +5921,23 @@ class Parser(Tokenizer):
             DictionaryMember : Type IDENTIFIER Default SEMICOLON
         """
         # These quack a lot like optional arguments, so just treat them that way.
         t = p[1]
         assert isinstance(t, IDLType)
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
         defaultValue = p[3]
 
+        # Any attributes that precede this may apply to the type, so
+        # we configure the argument to forward type attributes down instead of producing
+        # a parse error
         p[0] = IDLArgument(self.getLocation(p, 2), identifier, t,
                            optional=True,
                            defaultValue=defaultValue, variadic=False,
-                           dictionaryMember=True)
+                           dictionaryMember=True, allowTypeAttributes=True)
 
     def p_Default(self, p):
         """
             Default : EQUALS DefaultValue
                     |
         """
         if len(p) > 1:
             p[0] = p[2]
@@ -6479,17 +6469,20 @@ class Parser(Tokenizer):
 
         variadic = p[2]
 
         # We can't test t.isAny() here and give it a default value as needed,
         # since at this point t is not a fully resolved type yet (e.g. it might
         # be a typedef).  We'll handle the 'any' case in IDLArgument.complete.
 
         # variadic implies optional
-        p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, variadic, None, variadic)
+        # Any attributes that precede this may apply to the type, so
+        # we configure the argument to forward type attributes down instead of producing
+        # a parse error
+        p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, variadic, None, variadic, allowTypeAttributes=True)
 
     def p_ArgumentName(self, p):
         """
             ArgumentName : IDENTIFIER
                          | ATTRIBUTE
                          | CALLBACK
                          | CONST
                          | DELETER
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -778,18 +778,18 @@ interface TestInterface {
   void passDictContainingSequence(optional DictContainingSequence arg);
   DictContainingSequence receiveDictContainingSequence();
   void passVariadicDictionary(Dict... arg);
 
   // EnforceRange/Clamp tests
   void dontEnforceRangeOrClamp(byte arg);
   void doEnforceRange([EnforceRange] byte arg);
   void doClamp([Clamp] byte arg);
-  [EnforceRange] attribute byte enforcedByte;
-  [Clamp] attribute byte clampedByte;
+  attribute [EnforceRange] byte enforcedByte;
+  attribute [Clamp] byte clampedByte;
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Deprecated methods and attributes
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -617,18 +617,18 @@ interface TestExampleInterface {
   void passDictContainingSequence(optional DictContainingSequence arg);
   DictContainingSequence receiveDictContainingSequence();
   void passVariadicDictionary(Dict... arg);
 
   // EnforceRange/Clamp tests
   void dontEnforceRangeOrClamp(byte arg);
   void doEnforceRange([EnforceRange] byte arg);
   void doClamp([Clamp] byte arg);
-  [EnforceRange] attribute byte enforcedByte;
-  [Clamp] attribute byte clampedByte;
+  attribute [EnforceRange] byte enforcedByte;
+  attribute [Clamp] byte clampedByte;
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Deprecated methods and attributes
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -632,18 +632,18 @@ interface TestJSImplInterface {
   void passDictContainingSequence(optional DictContainingSequence arg);
   DictContainingSequence receiveDictContainingSequence();
   void passVariadicDictionary(Dict... arg);
 
   // EnforceRange/Clamp tests
   void dontEnforceRangeOrClamp(byte arg);
   void doEnforceRange([EnforceRange] byte arg);
   void doClamp([Clamp] byte arg);
-  [EnforceRange] attribute byte enforcedByte;
-  [Clamp] attribute byte clampedByte;
+  attribute [EnforceRange] byte enforcedByte;
+  attribute [Clamp] byte clampedByte;
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestJSImplInterface arg);
   AnotherNameForTestJSImplInterface exerciseTypedefInterfaces2(NullableTestJSImplInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestJSImplInterface arg);
 
   // Deprecated methods and attributes
--- a/dom/webidl/Blob.webidl
+++ b/dom/webidl/Blob.webidl
@@ -20,18 +20,18 @@ interface Blob {
   [GetterThrows]
   readonly attribute unsigned long long size;
 
   readonly attribute DOMString type;
 
   //slice Blob into byte-ranged chunks
 
   [Throws]
-  Blob slice([Clamp] optional long long start,
-             [Clamp] optional long long end,
+  Blob slice(optional [Clamp] long long start,
+             optional [Clamp] long long end,
              optional DOMString contentType);
 };
 
 enum EndingTypes { "transparent", "native" };
 
 dictionary BlobPropertyBag {
   DOMString type = "";
   EndingTypes endings = "transparent";
--- a/dom/webidl/IDBIndex.webidl
+++ b/dom/webidl/IDBIndex.webidl
@@ -53,19 +53,19 @@ interface IDBIndex {
     IDBRequest getKey (any key);
 
     [Throws]
     IDBRequest count (optional any key);
 };
 
 partial interface IDBIndex {
     [Throws]
-    IDBRequest mozGetAll (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest mozGetAll (optional any key, optional [EnforceRange] unsigned long limit);
 
     [Throws]
-    IDBRequest mozGetAllKeys (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest mozGetAllKeys (optional any key, optional [EnforceRange] unsigned long limit);
 
     [Throws]
-    IDBRequest getAll (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest getAll (optional any key, optional [EnforceRange] unsigned long limit);
 
     [Throws]
-    IDBRequest getAllKeys (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest getAllKeys (optional any key, optional [EnforceRange] unsigned long limit);
 };
--- a/dom/webidl/IDBObjectStore.webidl
+++ b/dom/webidl/IDBObjectStore.webidl
@@ -56,19 +56,19 @@ interface IDBObjectStore {
 
     [Throws]
     IDBRequest count (optional any key);
 };
 
 partial interface IDBObjectStore {
     // Success fires IDBTransactionEvent, result == array of values for given keys
     [Throws]
-    IDBRequest mozGetAll (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest mozGetAll (optional any key, optional [EnforceRange] unsigned long limit);
 
     [Throws]
-    IDBRequest getAll (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest getAll (optional any key, optional [EnforceRange] unsigned long limit);
 
     [Throws]
-    IDBRequest getAllKeys (optional any key, [EnforceRange] optional unsigned long limit);
+    IDBRequest getAllKeys (optional any key, optional [EnforceRange] unsigned long limit);
 
     [Throws]
     IDBRequest openKeyCursor (optional any range, optional IDBCursorDirection direction = "next");
 };
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -19,73 +19,73 @@ dictionary Algorithm {
 };
 
 dictionary AesCbcParams : Algorithm {
   required BufferSource iv;
 };
 
 dictionary AesCtrParams : Algorithm {
   required BufferSource counter;
-  [EnforceRange] required octet length;
+  required [EnforceRange] octet length;
 };
 
 dictionary AesGcmParams : Algorithm {
   required BufferSource iv;
   BufferSource additionalData;
   [EnforceRange] octet tagLength;
 };
 
 dictionary HmacImportParams : Algorithm {
   required AlgorithmIdentifier hash;
 };
 
 dictionary Pbkdf2Params : Algorithm {
   required BufferSource salt;
-  [EnforceRange] required unsigned long iterations;
+  required [EnforceRange] unsigned long iterations;
   required AlgorithmIdentifier hash;
 };
 
 dictionary RsaHashedImportParams {
   required AlgorithmIdentifier hash;
 };
 
 dictionary AesKeyGenParams : Algorithm {
-  [EnforceRange] required unsigned short length;
+  required [EnforceRange] unsigned short length;
 };
 
 dictionary HmacKeyGenParams : Algorithm {
   required AlgorithmIdentifier hash;
   [EnforceRange] unsigned long length;
 };
 
 dictionary RsaHashedKeyGenParams : Algorithm {
-  [EnforceRange] required unsigned long modulusLength;
+  required [EnforceRange] unsigned long modulusLength;
   required BigInteger publicExponent;
   required AlgorithmIdentifier hash;
 };
 
 dictionary RsaOaepParams : Algorithm {
   BufferSource label;
 };
 
 dictionary RsaPssParams : Algorithm {
-  [EnforceRange] required unsigned long saltLength;
+  required [EnforceRange] unsigned long saltLength;
 };
 
 dictionary DhKeyGenParams : Algorithm {
   required BigInteger prime;
   required BigInteger generator;
 };
 
 dictionary EcKeyGenParams : Algorithm {
   required NamedCurve namedCurve;
 };
 
 dictionary AesDerivedKeyParams : Algorithm {
-  [EnforceRange] required unsigned long length;
+  required [EnforceRange] unsigned long length;
 };
 
 dictionary HmacDerivedKeyParams : HmacImportParams {
   [EnforceRange] unsigned long length;
 };
 
 dictionary EcdhKeyDeriveParams : Algorithm {
   required CryptoKey public;