diff options
Diffstat (limited to 'dom/bindings/parser')
-rw-r--r-- | dom/bindings/parser/WebIDL.py | 134 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_builtin_filename.py | 2 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_distinguishability.py | 97 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_legacyTreatNonObjectAsNull.py | 11 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_union_callback_dict.py | 132 |
5 files changed, 288 insertions, 88 deletions
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 9101e14cf2..43f9ec12f1 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -43,25 +43,22 @@ def parseInt(literal): return value * sign -def enum(*names, **kw): - class Foo(object): - attrs = OrderedDict() +# This is surprisingly faster than using the enum.IntEnum type (which doesn't +# support 'base' anyway) +def enum(*names, base=None): + if base is not None: + names = base.attrs + names - def __init__(self, names): - for v, k in enumerate(names): - self.attrs[k] = v - - def __getattr__(self, attr): - if attr in self.attrs: - return self.attrs[attr] - raise AttributeError + class CustomEnumType(object): + attrs = names def __setattr__(self, name, value): # this makes it read-only raise NotImplementedError - if "base" not in kw: - return Foo(names) - return Foo(chain(kw["base"].attrs.keys(), names)) + for v, k in enumerate(names): + setattr(CustomEnumType, k, v) + + return CustomEnumType() class WebIDLError(Exception): @@ -85,13 +82,10 @@ class Location(object): self._lineno = lineno self._lexpos = lexpos self._lexdata = lexer.lexdata - self._file = filename if filename else "<unknown>" + self.filename = filename if filename else "<unknown>" def __eq__(self, other): - return self._lexpos == other._lexpos and self._file == other._file - - def filename(self): - return self._file + return self._lexpos == other._lexpos and self.filename == other.filename def resolve(self): if self._line: @@ -110,7 +104,7 @@ class Location(object): def get(self): self.resolve() - return "%s line %s:%s" % (self._file, self._lineno, self._colno) + return "%s line %s:%s" % (self.filename, self._lineno, self._colno) def _pointerline(self): return " " * self._colno + "^" @@ -118,7 +112,7 @@ class Location(object): def __str__(self): self.resolve() return "%s line %s:%s\n%s\n%s" % ( - self._file, + self.filename, self._lineno, self._colno, self._line, @@ -129,13 +123,11 @@ class Location(object): class BuiltinLocation(object): def __init__(self, text): self.msg = text + "\n" + self.filename = "<builtin>" def __eq__(self, other): return isinstance(other, BuiltinLocation) and self.msg == other.msg - def filename(self): - return "<builtin>" - def resolve(self): pass @@ -153,9 +145,7 @@ class IDLObject(object): def __init__(self, location): self.location = location self.userData = dict() - - def filename(self): - return self.location.filename() + self.filename = location and location.filename def isInterface(self): return False @@ -220,8 +210,8 @@ class IDLObject(object): visited.add(self) deps = set() - if self.filename() != "<builtin>": - deps.add(self.filename()) + if self.filename != "<builtin>": + deps.add(self.filename) for d in self._getDependentObjects(): deps.update(d.getDeps(visited)) @@ -3032,6 +3022,9 @@ class IDLRecordType(IDLParametrizedType): if other.isUnion(): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) + if other.isCallback(): + # Let other determine if it's a LegacyTreatNonObjectAsNull callback + return other.isDistinguishableFrom(self) return ( other.isPrimitive() or other.isString() @@ -3490,7 +3483,7 @@ class IDLWrapperType(IDLType): or other.isSequence() or other.isRecord() ) - if self.isDictionary() and (other.nullable() or other.isUndefined()): + if self.isDictionary() and other.nullable(): return False if ( other.isPrimitive() @@ -3499,28 +3492,51 @@ class IDLWrapperType(IDLType): or other.isSequence() ): return True - if self.isDictionary(): + + # If this needs to handle other dictionary-like types we probably need + # some additional checks first. + assert self.isDictionaryLike() == ( + self.isDictionary() or self.isCallbackInterface() + ) + if self.isDictionaryLike(): + if other.isCallback(): + # Let other determine if it's a LegacyTreatNonObjectAsNull callback + return other.isDistinguishableFrom(self) + + assert ( + other.isNonCallbackInterface() + or other.isAny() + or other.isUndefined() + or other.isObject() + or other.isDictionaryLike() + ) + # At this point, dictionary-like (for 'self') and interface-like + # (for 'other') are the only two that are distinguishable. + # any is the union of all non-union types, so it's not distinguishable + # from other unions (because it is a union itself), or from all + # non-union types (because it has all of them as its members). return other.isNonCallbackInterface() - assert self.isInterface() - if other.isInterface(): + assert self.isNonCallbackInterface() + + if other.isUndefined() or other.isDictionaryLike() or other.isCallback(): + return True + + if other.isNonCallbackInterface(): if other.isSpiderMonkeyInterface(): # Just let |other| handle things return other.isDistinguishableFrom(self) + assert self.isGeckoInterface() and other.isGeckoInterface() if self.inner.isExternal() or other.unroll().inner.isExternal(): return self != other - return len( - self.inner.interfacesBasedOnSelf - & other.unroll().inner.interfacesBasedOnSelf - ) == 0 and (self.isNonCallbackInterface() or other.isNonCallbackInterface()) - if ( - other.isUndefined() - or other.isDictionary() - or other.isCallback() - or other.isRecord() - ): - return self.isNonCallbackInterface() + return ( + len( + self.inner.interfacesBasedOnSelf + & other.unroll().inner.interfacesBasedOnSelf + ) + == 0 + ) # Not much else |other| can be. # any is the union of all non-union types, so it's not distinguishable @@ -6067,6 +6083,9 @@ class IDLCallbackType(IDLType): if other.isUnion(): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) + # Callbacks without `LegacyTreatNonObjectAsNull` are distinguishable from Dictionary likes + if other.isDictionaryLike(): + return not self.callback._treatNonObjectAsNull return ( other.isUndefined() or other.isPrimitive() @@ -6943,7 +6962,7 @@ class IDLExtendedAttribute(IDLObject): class Tokenizer(object): - tokens = ["INTEGER", "FLOATLITERAL", "IDENTIFIER", "STRING", "WHITESPACE", "OTHER"] + tokens = ["INTEGER", "FLOATLITERAL", "IDENTIFIER", "STRING", "COMMENTS", "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" @@ -6979,17 +6998,19 @@ class Tokenizer(object): t.value = t.value[1:-1] return t - def t_WHITESPACE(self, t): - r"[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+" + t_ignore = "\t\n\r " + + def t_COMMENTS(self, t): + r"//[^\n]*|/\*(?s:.)*?\*/" pass def t_ELLIPSIS(self, t): r"\.\.\." - t.type = self.keywords.get(t.value) + t.type = "ELLIPSIS" return t def t_OTHER(self, t): - r"[^\t\n\r 0-9A-Z_a-z]" + r"[^0-9A-Z_a-z]" t.type = self.keywords.get(t.value, "OTHER") return t @@ -7086,14 +7107,14 @@ class Tokenizer(object): if lexer: self.lexer = lexer else: - self.lexer = lex.lex(object=self, reflags=re.DOTALL) + self.lexer = lex.lex(object=self) class SqueakyCleanLogger(object): errorWhitelist = [ - # Web IDL defines the WHITESPACE token, but doesn't actually + # Web IDL defines the COMMENTS token, but doesn't actually # use it ... so far. - "Token 'WHITESPACE' defined, but not used", + "Token 'COMMENTS' defined, but not used", # And that means we have an unused token "There is 1 unused token", # Web IDL defines a OtherOrComma rule that's only used in @@ -9097,13 +9118,8 @@ class Parser(Tokenizer): production.validate() # De-duplicate self._productions, without modifying its order. - seen = set() - result = [] - for p in self._productions: - if p not in seen: - seen.add(p) - result.append(p) - return result + result = dict.fromkeys(self._productions) + return list(result.keys()) def reset(self): return Parser(lexer=self.lexer) diff --git a/dom/bindings/parser/tests/test_builtin_filename.py b/dom/bindings/parser/tests/test_builtin_filename.py index 25ed32befc..97e8cb061f 100644 --- a/dom/bindings/parser/tests/test_builtin_filename.py +++ b/dom/bindings/parser/tests/test_builtin_filename.py @@ -8,4 +8,4 @@ def WebIDLTest(parser, harness): ) attr = parser.finish()[0].members[0] - harness.check(attr.type.filename(), "<builtin>", "Filename on builtin type") + harness.check(attr.type.filename, "<builtin>", "Filename on builtin type") diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py index 8497490b1f..caf726c16b 100644 --- a/dom/bindings/parser/tests/test_distinguishability.py +++ b/dom/bindings/parser/tests/test_distinguishability.py @@ -228,7 +228,9 @@ def WebIDLTest(parser, harness): and (a != "any" and a != "Promise<any>" and a != "Promise<any>?") ] - unions = ["(long or Callback)", "(long or Dict)"] + unionsWithCallback = ["(long or Callback)"] + unionsNoCallback = ["(long or Dict)"] + unions = unionsWithCallback + unionsNoCallback numerics = ["long", "short", "long?", "short?"] booleans = ["boolean", "boolean?"] undefineds = ["undefined", "undefined?"] @@ -265,7 +267,7 @@ def WebIDLTest(parser, harness): "Date?", "any", "Promise<any>?", - ] + allBut(unions, ["(long or Callback)"]) + ] + unionsNoCallback sequences = ["sequence<long>", "sequence<short>"] nonUserObjects = nonObjects + interfaces + sequences otherObjects = allBut(argTypes, nonUserObjects + ["object"]) @@ -282,17 +284,14 @@ def WebIDLTest(parser, harness): "record<ByteString, long>", "record<UTF8String, long>", ] # JSString not supported in records - dictionaryLike = ( - [ - "Dict", - "Dict2", - "CallbackInterface", - "CallbackInterface?", - "CallbackInterface2", - ] - + records - + allBut(unions, ["(long or Callback)"]) - ) + dicts = ["Dict", "Dict2"] + callbacks = ["Callback", "Callback2"] + callbackInterfaces = [ + "CallbackInterface", + "CallbackInterface?", + "CallbackInterface2", + ] + dictionaryLike = dicts + callbackInterfaces + records + unionsNoCallback # Build a representation of the distinguishability table as a dict # of dicts, holding True values where needed, holes elsewhere. @@ -327,23 +326,60 @@ def WebIDLTest(parser, harness): setDistinguishable( "UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"]) ) - setDistinguishable("CallbackInterface", allBut(nonUserObjects, undefineds)) setDistinguishable( - "CallbackInterface?", allBut(nonUserObjects, nullables + undefineds) + "CallbackInterface", + allBut(nonUserObjects + callbacks + unionsWithCallback, undefineds), + ) + setDistinguishable( + "CallbackInterface?", + allBut(nonUserObjects + callbacks + unionsWithCallback, nullables + undefineds), + ) + setDistinguishable( + "CallbackInterface2", + allBut(nonUserObjects + callbacks + unionsWithCallback, undefineds), ) - setDistinguishable("CallbackInterface2", allBut(nonUserObjects, undefineds)) setDistinguishable("object", nonObjects) - setDistinguishable("Callback", nonUserObjects) - setDistinguishable("Callback2", nonUserObjects) - setDistinguishable("Dict", allBut(nonUserObjects, nullables + undefineds)) - setDistinguishable("Dict2", allBut(nonUserObjects, nullables + undefineds)) - setDistinguishable("sequence<long>", allBut(argTypes, sequences + ["object"])) - setDistinguishable("sequence<short>", allBut(argTypes, sequences + ["object"])) - setDistinguishable("record<DOMString, object>", allBut(nonUserObjects, undefineds)) - setDistinguishable("record<USVString, Dict>", allBut(nonUserObjects, undefineds)) + setDistinguishable( + "Callback", + nonUserObjects + unionsNoCallback + dicts + records + callbackInterfaces, + ) + setDistinguishable( + "Callback2", + nonUserObjects + unionsNoCallback + dicts + records + callbackInterfaces, + ) + setDistinguishable( + "Dict", + allBut(nonUserObjects + unionsWithCallback + callbacks, nullables + undefineds), + ) + setDistinguishable( + "Dict2", + allBut(nonUserObjects + unionsWithCallback + callbacks, nullables + undefineds), + ) + setDistinguishable( + "sequence<long>", + allBut(argTypes, sequences + ["object"]), + ) + setDistinguishable( + "sequence<short>", + allBut(argTypes, sequences + ["object"]), + ) + setDistinguishable( + "record<DOMString, object>", + allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), + ) + setDistinguishable( + "record<USVString, Dict>", + allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), + ) # JSString not supported in records - setDistinguishable("record<ByteString, long>", allBut(nonUserObjects, undefineds)) - setDistinguishable("record<UTF8String, long>", allBut(nonUserObjects, undefineds)) + setDistinguishable( + "record<ByteString, long>", + allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), + ) + setDistinguishable( + "record<UTF8String, long>", + allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), + ) setDistinguishable("any", []) setDistinguishable("Promise<any>", []) setDistinguishable("Promise<any>?", []) @@ -358,9 +394,13 @@ def WebIDLTest(parser, harness): setDistinguishable( "Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]) ) - setDistinguishable("(long or Callback)", allBut(nonUserObjects, numerics)) setDistinguishable( - "(long or Dict)", allBut(nonUserObjects, numerics + nullables + undefineds) + "(long or Callback)", + allBut(nonUserObjects + dicts + records + callbackInterfaces, numerics), + ) + setDistinguishable( + "(long or Dict)", + allBut(nonUserObjects + callbacks, numerics + nullables + undefineds), ) def areDistinguishable(type1, type2): @@ -377,6 +417,7 @@ def WebIDLTest(parser, harness): callback interface CallbackInterface2 {}; callback Callback = any(); callback Callback2 = long(short arg); + [LegacyTreatNonObjectAsNull] callback LegacyCallback1 = any(); // Give our dictionaries required members so we don't need to // mess with optional and default values. dictionary Dict { required long member; }; diff --git a/dom/bindings/parser/tests/test_legacyTreatNonObjectAsNull.py b/dom/bindings/parser/tests/test_legacyTreatNonObjectAsNull.py new file mode 100644 index 0000000000..380ccdc4e7 --- /dev/null +++ b/dom/bindings/parser/tests/test_legacyTreatNonObjectAsNull.py @@ -0,0 +1,11 @@ +def WebIDLTest(parser, harness): + parser.parse( + """ + [LegacyTreatNonObjectAsNull] callback Function = any(any... arguments); + """ + ) + + results = parser.finish() + + callback = results[0] + harness.check(callback._treatNonObjectAsNull, True, "Got the expected value") diff --git a/dom/bindings/parser/tests/test_union_callback_dict.py b/dom/bindings/parser/tests/test_union_callback_dict.py new file mode 100644 index 0000000000..3e87e16ad4 --- /dev/null +++ b/dom/bindings/parser/tests/test_union_callback_dict.py @@ -0,0 +1,132 @@ +import WebIDL + + +def WebIDLTest(parser, harness): + parser = parser.reset() + threw = False + try: + parser.parse( + """ + dictionary TestDict { + DOMString member; + }; + [LegacyTreatNonObjectAsNull] callback TestCallback = undefined (); + typedef (TestCallback or TestDict) TestUnionCallbackDict; + """ + ) + results = parser.finish() + except WebIDL.WebIDLError: + threw = True + harness.ok( + threw, + "Should not allow Dict/Callback union where callback is [LegacyTreatNonObjectAsNull]", + ) + + parser = parser.reset() + + threw = False + try: + parser.parse( + """ + dictionary TestDict { + DOMString member; + }; + [LegacyTreatNonObjectAsNull] callback TestCallback = undefined (); + typedef (TestDict or TestCallback) TestUnionCallbackDict; + """ + ) + results = parser.finish() + except WebIDL.WebIDLError: + threw = True + harness.ok( + threw, + "Should not allow Dict/Callback union where callback is [LegacyTreatNonObjectAsNull]", + ) + + parser = parser.reset() + + parser.parse( + """ + dictionary TestDict { + DOMString member; + }; + callback TestCallback = undefined (); + typedef (TestCallback or TestDict) TestUnionCallbackDict; + """ + ) + results = parser.finish() + + harness.ok(True, "TestUnionCallbackDict interface parsed without error") + harness.check(len(results), 3, "Document should have 3 types") + + myDict = results[0] + harness.ok(isinstance(myDict, WebIDL.IDLDictionary), "Expect an IDLDictionary") + + myCallback = results[1] + harness.ok(isinstance(myCallback, WebIDL.IDLCallback), "Expect an IDLCallback") + + myUnion = results[2] + harness.ok(isinstance(myUnion, WebIDL.IDLTypedef), "Expect a IDLTypedef") + harness.ok( + isinstance(myUnion.innerType, WebIDL.IDLUnionType), "Expect a IDLUnionType" + ) + harness.ok( + isinstance(myUnion.innerType.memberTypes[0], WebIDL.IDLCallbackType), + "Expect a IDLCallbackType", + ) + harness.ok( + isinstance(myUnion.innerType.memberTypes[1], WebIDL.IDLWrapperType), + "Expect a IDLDictionary", + ) + harness.ok( + (myUnion.innerType.memberTypes[0].callback == myCallback), + "Expect left Union member to be MyCallback", + ) + harness.ok( + (myUnion.innerType.memberTypes[1].inner == myDict), + "Expect right Union member to be MyDict", + ) + + parser = parser.reset() + + parser.parse( + """ + dictionary TestDict { + DOMString member; + }; + callback TestCallback = undefined (); + typedef (TestDict or TestCallback) TestUnionCallbackDict; + """ + ) + results = parser.finish() + + harness.ok(True, "TestUnionCallbackDict interface parsed without error") + harness.check(len(results), 3, "Document should have 3 types") + + myDict = results[0] + harness.ok(isinstance(myDict, WebIDL.IDLDictionary), "Expect an IDLDictionary") + + myCallback = results[1] + harness.ok(isinstance(myCallback, WebIDL.IDLCallback), "Expect an IDLCallback") + + myUnion = results[2] + harness.ok(isinstance(myUnion, WebIDL.IDLTypedef), "Expect a IDLTypedef") + harness.ok( + isinstance(myUnion.innerType, WebIDL.IDLUnionType), "Expect a IDLUnionType" + ) + harness.ok( + isinstance(myUnion.innerType.memberTypes[0], WebIDL.IDLWrapperType), + "Expect a IDLDictionary", + ) + harness.ok( + isinstance(myUnion.innerType.memberTypes[1], WebIDL.IDLCallbackType), + "Expect a IDLCallbackType", + ) + harness.ok( + (myUnion.innerType.memberTypes[0].inner == myDict), + "Expect right Union member to be MyDict", + ) + harness.ok( + (myUnion.innerType.memberTypes[1].callback == myCallback), + "Expect left Union member to be MyCallback", + ) |