Bug 1474369 - Part 3: Add generic type parsing support to xpidl, r=mccr8

Summary:
This patch allows parsing generic types, such as Sequence<T>, in XPIDL. It does
this by introducing a new type, TypeId, which contains both the name string and
an optional list of generic parameters.

Various places which use the xpidl.py library had to be updated to construct one
of these TypeId objects, as TypeId and `str` are not compatible types.

Depends On D2106

Reviewers: mccr8!

Tags: #secure-revision

Bug #: 1474369

Differential Revision: https://phabricator.services.mozilla.com/D2109
This commit is contained in:
Nika Layzell 2018-07-10 21:15:16 -04:00
parent ef3ba34832
commit 5e6f9e81ab
3 changed files with 58 additions and 21 deletions

View File

@ -197,7 +197,7 @@ def interfaces(iface):
interfaces = []
while iface.base:
interfaces.append(iface)
iface = iface.idl.getName(iface.base, iface.location)
iface = iface.idl.getName(xpidl.TypeId(iface.base), iface.location)
interfaces.append(iface)
interfaces.reverse()
return interfaces

View File

@ -60,7 +60,7 @@ void bar();
m = iface.members[0]
self.assertTrue(isinstance(m, xpidl.Method))
self.assertEqual("bar", m.name)
self.assertEqual("void", m.type)
self.assertEqual(xpidl.TypeId("void"), m.type)
def testMethodParams(self):
i = self.p.parse("""[uuid(abc)] interface foo {
@ -73,13 +73,13 @@ long bar(in long a, in float b, [array] in long c);
m = iface.members[0]
self.assertTrue(isinstance(m, xpidl.Method))
self.assertEqual("bar", m.name)
self.assertEqual("long", m.type)
self.assertEqual(xpidl.TypeId("long"), m.type)
self.assertEqual(3, len(m.params))
self.assertEqual("long", m.params[0].type)
self.assertEqual(xpidl.TypeId("long"), m.params[0].type)
self.assertEqual("in", m.params[0].paramtype)
self.assertEqual("float", m.params[1].type)
self.assertEqual(xpidl.TypeId("float"), m.params[1].type)
self.assertEqual("in", m.params[1].paramtype)
self.assertEqual("long", m.params[2].type)
self.assertEqual(xpidl.TypeId("long"), m.params[2].type)
self.assertEqual("in", m.params[2].paramtype)
self.assertTrue(isinstance(m.params[2].realtype, xpidl.Array))
self.assertEqual("long", m.params[2].realtype.type.name)
@ -94,7 +94,7 @@ attribute long bar;
a = iface.members[0]
self.assertTrue(isinstance(a, xpidl.Attribute))
self.assertEqual("bar", a.name)
self.assertEqual("long", a.type)
self.assertEqual(xpidl.TypeId("long"), a.type)
def testOverloadedVirtual(self):
i = self.p.parse("""[uuid(abc)] interface foo {

View File

@ -12,6 +12,7 @@ import os.path
import re
from ply import lex
from ply import yacc
from collections import namedtuple
"""A type conforms to the following pattern:
@ -328,10 +329,13 @@ class IDL(object):
self.namemap.set(object)
def getName(self, id, location):
if id.params is not None:
raise IDLError("Generic type '%s' unrecognized" % id.name, location)
try:
return self.namemap[id]
return self.namemap[id.name]
except KeyError:
raise IDLError("type '%s' not found" % id, location)
raise IDLError("type '%s' not found" % id.name, location)
def hasName(self, id):
return id in self.namemap
@ -663,7 +667,7 @@ class Interface(object):
if hasattr(member, 'doccomments'):
member.doccomments[0:0] = self.doccomments
break
self.doccomments = parent.getName(self.name, None).doccomments
self.doccomments = parent.getName(TypeId(self.name), None).doccomments
if self.attributes.function:
has_method = False
@ -678,7 +682,7 @@ class Interface(object):
parent.setName(self)
if self.base is not None:
realbase = parent.getName(self.base, self.location)
realbase = parent.getName(TypeId(self.base), self.location)
if realbase.kind != 'interface':
raise IDLError("interface '%s' inherits from non-interface type '%s'" %
(self.name, self.base), self.location)
@ -737,7 +741,7 @@ class Interface(object):
# The constant may be in a base class
iface = self
while name not in iface.namemap and iface is not None:
iface = self.idl.getName(self.base, self.location)
iface = self.idl.getName(TypeId(self.base), self.location)
if iface is None:
raise IDLError("cannot find symbol '%s'" % name)
c = iface.namemap.get(name, location)
@ -748,7 +752,7 @@ class Interface(object):
def needsJSTypes(self):
for m in self.members:
if m.kind == "attribute" and m.type == "jsval":
if m.kind == "attribute" and m.type == TypeId("jsval"):
return True
if m.kind == "method" and m.needsJSTypes():
return True
@ -758,7 +762,7 @@ class Interface(object):
''' Returns the number of entries in the vtable for this interface. '''
total = sum(member.count() for member in self.members)
if self.base is not None:
realbase = self.idl.getName(self.base, self.location)
realbase = self.idl.getName(TypeId(self.base), self.location)
total += realbase.countEntries()
return total
@ -1084,7 +1088,7 @@ class Method(object):
def needsJSTypes(self):
if self.implicit_jscontext:
return True
if self.type == "jsval":
if self.type == TypeId("jsval"):
return True
for p in self.params:
t = p.realtype
@ -1234,6 +1238,20 @@ class Array(object):
self.type.rustType('element'))
TypeId = namedtuple('TypeId', 'name params')
# Make str(TypeId) produce a nicer value
TypeId.__str__ = lambda self: \
"%s<%s>" % (self.name, ', '.join(str(p) for p in self.params)) \
if self.params is not None \
else self.name
# Allow skipping 'params' in TypeId(..)
TypeId.__new__.__defaults__ = (None,)
class IDLParser(object):
keywords = {
'const': 'CONST',
@ -1274,7 +1292,7 @@ class IDLParser(object):
t_LSHIFT = r'<<'
t_RSHIFT = r'>>'
literals = '"(){}[],;:=|+-*'
literals = '"(){}[]<>,;:=|+-*'
t_ignore = ' \t'
@ -1367,7 +1385,7 @@ class IDLParser(object):
p[0].insert(0, p[1])
def p_typedef(self, p):
"""typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
"""typedef : TYPEDEF type IDENTIFIER ';'"""
p[0] = Typedef(type=p[2],
name=p[3],
location=self.getLocation(p, 1),
@ -1480,7 +1498,7 @@ class IDLParser(object):
p[0] = CDATA(p[1], self.getLocation(p, 1))
def p_member_const(self, p):
"""member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
"""member : CONST type IDENTIFIER '=' number ';' """
p[0] = ConstMember(type=p[2], name=p[3],
value=p[5], location=self.getLocation(p, 1),
doccomments=p.slice[1].doccomments)
@ -1542,7 +1560,7 @@ class IDLParser(object):
p[0] = lambda i: n1(i) | n2(i)
def p_member_att(self, p):
"""member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
"""member : attributes optreadonly ATTRIBUTE type IDENTIFIER ';'"""
if 'doccomments' in p[1]:
doccomments = p[1]['doccomments']
elif p[2] is not None:
@ -1558,7 +1576,7 @@ class IDLParser(object):
doccomments=doccomments)
def p_member_method(self, p):
"""member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
"""member : attributes type IDENTIFIER '(' paramlist ')' raises ';'"""
if 'doccomments' in p[1]:
doccomments = p[1]['doccomments']
else:
@ -1591,7 +1609,7 @@ class IDLParser(object):
p[0].insert(0, p[2])
def p_param(self, p):
"""param : attributes paramtype IDENTIFIER IDENTIFIER"""
"""param : attributes paramtype type IDENTIFIER"""
p[0] = Param(paramtype=p[2],
type=p[3],
name=p[4],
@ -1629,6 +1647,25 @@ class IDLParser(object):
p[0] = list(p[3])
p[0].insert(0, p[1])
def p_type_id(self, p):
"""type : IDENTIFIER"""
p[0] = TypeId(name=p[1])
p.slice[0].doccomments = p.slice[1].doccomments
def p_type_generic(self, p):
"""type : IDENTIFIER '<' typelist '>'"""
p[0] = TypeId(name=p[1], params=p[3])
p.slice[0].doccomments = p.slice[1].doccomments
def p_typelist(self, p):
"""typelist : type"""
p[0] = [p[1]]
def p_typelist_continue(self, p):
"""typelist : type ',' typelist"""
p[0] = list(p[3])
p[0].insert(0, p[1])
def p_error(self, t):
if not t:
raise IDLError(