summaryrefslogtreecommitdiffstats
path: root/dom/bindings/parser
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings/parser')
-rw-r--r--dom/bindings/parser/WebIDL.py134
-rw-r--r--dom/bindings/parser/tests/test_builtin_filename.py2
-rw-r--r--dom/bindings/parser/tests/test_distinguishability.py97
-rw-r--r--dom/bindings/parser/tests/test_legacyTreatNonObjectAsNull.py11
-rw-r--r--dom/bindings/parser/tests/test_union_callback_dict.py132
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",
+ )