Bug 742153 part 1. Add support for dictionaries in the WebIDL parser. r=khuey

This commit is contained in:
Boris Zbarsky 2012-06-12 10:22:05 -04:00
Родитель c2b074f698
Коммит 40dc245f66
4 изменённых файлов: 203 добавлений и 25 удалений

Просмотреть файл

@ -317,7 +317,7 @@ class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope):
IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
IDLScope.__init__(self, location, parentScope, self.identifier)
class IDLInterfacePlaceholder(IDLObjectWithIdentifier):
class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
def __init__(self, location, identifier):
assert isinstance(identifier, IDLUnresolvedIdentifier)
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
@ -328,8 +328,8 @@ class IDLInterfacePlaceholder(IDLObjectWithIdentifier):
except:
raise WebIDLError("Unresolved type '%s'." % self.identifier, self.location)
iface = self.identifier.resolve(scope, None)
return scope.lookupIdentifier(iface)
obj = self.identifier.resolve(scope, None)
return scope.lookupIdentifier(obj)
class IDLExternalInterface(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, identifier):
@ -358,7 +358,7 @@ class IDLInterface(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members):
assert isinstance(parentScope, IDLScope)
assert isinstance(name, IDLUnresolvedIdentifier)
assert not parent or isinstance(parent, IDLInterfacePlaceholder)
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
self.parent = parent
self._callback = False
@ -403,7 +403,7 @@ class IDLInterface(IDLObjectWithScope):
self._finished = True
assert not self.parent or isinstance(self.parent, IDLInterfacePlaceholder)
assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
parent = self.parent.finish(scope) if self.parent else None
assert not parent or isinstance(parent, IDLInterface)
@ -588,6 +588,70 @@ class IDLInterface(IDLObjectWithScope):
return consequentialInterfaces | temp
class IDLDictionary(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members):
assert isinstance(parentScope, IDLScope)
assert isinstance(name, IDLUnresolvedIdentifier)
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
self.parent = parent
self._finished = False
self.members = list(members)
IDLObjectWithScope.__init__(self, location, parentScope, name)
def __str__(self):
return "Dictionary '%s'" % self.identifier.name
def finish(self, scope):
if self._finished:
return
self._finished = True
if self.parent:
assert isinstance(self.parent, IDLIdentifierPlaceholder)
oldParent = self.parent
self.parent = self.parent.finish(scope)
if not isinstance(self.parent, IDLDictionary):
raise WebIDLError("Dictionary %s has parent that is not a dictionary" %
self.identifier.name,
oldParent.location,
extraLocation=self.parent.location)
# Make sure the parent resolves all its members before we start
# looking at them.
self.parent.finish(scope)
for member in self.members:
member.resolve(self)
# Members of a dictionary are sorted in lexicographic order
self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
inheritedMembers = []
ancestor = self.parent
while ancestor:
if ancestor == self:
raise WebIDLError("Dictionary %s has itself as an ancestor" %
self.identifier.name,
self.identifier.location)
inheritedMembers.extend(ancestor.members)
ancestor = ancestor.parent
# Catch name duplication
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,
extraLocation=inheritedMember.location)
def addExtendedAttributes(self, attrs):
assert len(attrs) == 0
class IDLEnum(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, name, values):
assert isinstance(parentScope, IDLScope)
@ -1093,7 +1157,7 @@ class IDLWrapperType(IDLType):
return False
def isDictionary(self):
return False
return isinstance(self.inner, IDLDictionary)
def isInterface(self):
return isinstance(self.inner, IDLInterface) or \
@ -1114,6 +1178,8 @@ class IDLWrapperType(IDLType):
return IDLType.Tags.interface
elif self.isEnum():
return IDLType.Tags.enum
elif self.isDictionary():
return IDLType.Tags.dictionary
else:
assert False
@ -1481,6 +1547,9 @@ class IDLConst(IDLInterfaceMember):
IDLInterfaceMember.Tags.Const)
assert isinstance(type, IDLType)
if type.isDictionary():
raise WebIDLError("A constant cannot be of a dictionary type",
self.location)
self.type = type
# The value might not match the type
@ -1518,6 +1587,9 @@ class IDLAttribute(IDLInterfaceMember):
assert not isinstance(t, IDLUnresolvedType)
assert not isinstance(t.name, IDLUnresolvedIdentifier)
if t.isDictionary():
raise WebIDLError("An attribute cannot be of a dictionary type",
self.location)
self.type = t
def handleExtendedAttribute(self, name, list):
@ -1804,8 +1876,8 @@ class IDLImplementsStatement(IDLObject):
self.implementee = implementee
def finish(self, scope):
assert(isinstance(self.implementor, IDLInterfacePlaceholder))
assert(isinstance(self.implementee, IDLInterfacePlaceholder))
assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
implementor = self.implementor.finish(scope)
implementee = self.implementee.finish(scope)
implementor.addImplementedInterface(implementee)
@ -2041,7 +2113,7 @@ class Parser(Tokenizer):
"""
Inheritance : COLON ScopedName
"""
p[0] = IDLInterfacePlaceholder(self.getLocation(p, 2), p[2])
p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
def p_InheritanceEmpty(self, p):
"""
@ -2077,20 +2149,37 @@ class Parser(Tokenizer):
"""
Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
"""
pass
location = self.getLocation(p, 1)
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
members = p[5]
p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
def p_DictionaryMembers(self, p):
"""
DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
|
"""
pass
if len(p) == 1:
# We're at the end of the list
p[0] = []
return
# Add our extended attributes
p[2].addExtendedAttributes(p[1])
p[0] = [p[2]]
p[0].extend(p[3])
def p_DictionaryMember(self, p):
"""
DictionaryMember : Type IDENTIFIER DefaultValue SEMICOLON
"""
pass
# 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]
p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, optional=True,
defaultValue=defaultValue, variadic=False)
def p_DefaultValue(self, p):
"""
@ -2167,8 +2256,8 @@ class Parser(Tokenizer):
ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
"""
assert(p[2] == "implements")
implementor = IDLInterfacePlaceholder(self.getLocation(p, 1), p[1])
implementee = IDLInterfacePlaceholder(self.getLocation(p, 3), p[3])
implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
implementee)

Просмотреть файл

@ -62,8 +62,8 @@ def run_tests(tests, verbose):
harness.start()
try:
_test.WebIDLTest.__call__(WebIDL.Parser(), harness)
except:
print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s" % testpath
except Exception, ex:
print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s: %s" % (testpath, ex)
traceback.print_exc()
finally:
harness.finish()

Просмотреть файл

@ -0,0 +1,97 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
dictionary Dict2 : Dict1 {
long child = 5;
Dict1 aaandAnother;
};
dictionary Dict1 {
long parent;
double otherParent;
};
""")
results = parser.finish()
dict1 = results[1];
dict2 = results[0];
harness.check(len(dict1.members), 2, "Dict1 has two members")
harness.check(len(dict2.members), 2, "Dict2 has four members")
harness.check(dict1.members[0].identifier.name, "otherParent",
"'o' comes before 'p'")
harness.check(dict1.members[1].identifier.name, "parent",
"'o' really comes before 'p'")
harness.check(dict2.members[0].identifier.name, "aaandAnother",
"'a' comes before 'c'")
harness.check(dict2.members[1].identifier.name, "child",
"'a' really comes before 'c'")
# Now reset our parser
parser = WebIDL.Parser()
threw = False
try:
parser.parse("""
dictionary Dict {
long prop = 5;
long prop;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should not allow name duplication in a dictionary")
# Now reset our parser again
parser = WebIDL.Parser()
threw = False
try:
parser.parse("""
dictionary Dict1 : Dict2 {
long prop = 5;
};
dictionary Dict2 : Dict3 {
long prop2;
};
dictionary Dict3 {
double prop;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should not allow name duplication in a dictionary and "
"its ancestor")
# More reset
parser = WebIDL.Parser()
threw = False
try:
parser.parse("""
interface Iface {};
dictionary Dict : Iface {
long prop;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")
# Even more reset
parser = WebIDL.Parser()
threw = False
try:
parser.parse("""
dictionary A : B {};
dictionary B : A {};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")

Просмотреть файл

@ -12,15 +12,7 @@ def WebIDLTest(parser, harness):
attribute ArrayBuffer? b;
};
/* Not implemented */
/*dictionary TestNullableEquivalency3Dict {
long foo = 42;
};
interface TestNullableEquivalency3 {
attribute Test3Dict a;
attribute Test3Dict? b;
};*/
/* Can't have dictionary-valued attributes, so can't test that here */
enum TestNullableEquivalency4Enum {
"Foo",