Bug 1498059 - Add CEnum types to XPIDL; r=nika,froydnj

Add CEnum types to XPIDL, allowing for typed enums in C++ instead of
using uintXX_t types. Javascript will still reflect CEnums as
interface level consts.

Depends on D8593

Differential Revision: https://phabricator.services.mozilla.com/D8594

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Kyle Machulis 2018-11-06 00:05:37 +00:00
parent 911d1c3784
commit cd6f797161
3 changed files with 130 additions and 0 deletions

View File

@ -390,6 +390,12 @@ def write_interface(iface, fd):
fd.write(",\n".join(enums))
fd.write("\n };\n\n")
def write_cenum_decl(b):
fd.write(" enum %s : uint%d_t {\n" % (b.basename, b.width))
for var in b.variants:
fd.write(" %s = %s,\n" % (var.name, var.value))
fd.write(" };\n\n")
def write_method_decl(m):
printComments(fd, m.doccomments, ' ')
@ -471,6 +477,8 @@ def write_interface(iface, fd):
write_method_decl(member)
elif key == xpidl.CDATA:
fd.write(" %s" % member.data)
elif key == xpidl.CEnum:
write_cenum_decl(member)
else:
raise Exception("Unexpected interface member: %s" % member)

View File

@ -105,6 +105,10 @@ def get_type(type, calltype, iid_is=None, size_is=None):
else:
return {'tag': 'TD_VOID'}
if isinstance(type, xpidl.CEnum):
# As far as XPConnect is concerned, cenums are just unsigned integers.
return {'tag': 'TD_UINT%d' % type.width}
raise Exception("Unknown type!")
@ -166,6 +170,14 @@ def build_interface(iface):
'value': c.getValue(), # All of our consts are numbers
})
def build_cenum(b):
for var in b.variants:
consts.append({
'name': var.name,
'type': get_type(b, 'in'),
'value': var.value,
})
def build_method(m):
params = []
for p in m.params:
@ -210,6 +222,8 @@ def build_interface(iface):
build_attr(member)
elif isinstance(member, xpidl.Method):
build_method(member)
elif isinstance(member, xpidl.CEnum):
build_cenum(member)
elif isinstance(member, xpidl.CDATA):
pass
else:

View File

@ -910,6 +910,85 @@ class ConstMember(object):
return 0
# Represents a single name/value pair in a CEnum
class CEnumVariant(object):
# Treat CEnumVariants as consts in terms of value resolution, so we can
# do things like binary operation values for enum members.
kind = 'const'
def __init__(self, name, value, location):
self.name = name
self.value = value
self.location = location
def getValue(self):
return self.value
class CEnum(object):
kind = 'cenum'
def __init__(self, width, name, variants, location, doccomments):
# We have to set a name here, otherwise we won't pass namemap checks on
# the interface. This name will change it in resolve(), in order to
# namespace the enum within the interface.
self.name = name
self.basename = name
self.width = width
self.location = location
self.namemap = NameMap()
self.doccomments = doccomments
self.variants = variants
if self.width not in (8, 16, 32):
raise IDLError("Width must be one of {8, 16, 32}", self.location)
def getValue(self):
return self.value(self.iface)
def resolve(self, iface):
self.iface = iface
# Renaming enum to faux-namespace the enum type to the interface in JS
# so we don't collide in the global namespace. Hacky/ugly but it does
# the job well enough, and the name will still be interface::variant in
# C++.
self.name = '%s_%s' % (self.iface.name, self.basename)
self.iface.idl.setName(self)
# Compute the value for each enum variant that doesn't set its own
# value
next_value = 0
for variant in self.variants:
# CEnum variants resolve to interface level consts in javascript,
# meaning their names could collide with other interface members.
# Iterate through all CEnum variants to make sure there are no
# collisions.
self.iface.namemap.set(variant)
# Value may be a lambda. If it is, resolve it.
if variant.value:
next_value = variant.value = variant.value(self.iface)
else:
variant.value = next_value
next_value += 1
def count(self):
return 0
def isScriptable(self):
return True
def nativeType(self, calltype):
if 'out' in calltype:
return "%s::%s *" % (self.iface.name, self.basename)
return "%s::%s " % (self.iface.name, self.basename)
def rustType(self, calltype):
raise RustNoncompat('cenums unimplemented')
def __str__(self):
body = ', '.join('%s = %s' % v for v in self.variants)
return "\tcenum %s : %d { %s };\n" % (self.name, self.width, body)
class Attribute(object):
kind = 'attribute'
noscript = False
@ -1267,6 +1346,7 @@ TypeId.__new__.__defaults__ = (None,)
class IDLParser(object):
keywords = {
'cenum': 'CENUM',
'const': 'CONST',
'interface': 'INTERFACE',
'in': 'IN',
@ -1572,6 +1652,34 @@ class IDLParser(object):
n2 = p[3]
p[0] = lambda i: n1(i) | n2(i)
def p_member_cenum(self, p):
"""member : CENUM IDENTIFIER ':' NUMBER '{' variants '}' ';'"""
p[0] = CEnum(name=p[2],
width=int(p[4]),
variants=p[6],
location=self.getLocation(p, 1),
doccomments=p.slice[1].doccomments)
def p_variants_start(self, p):
"""variants : """
p[0] = []
def p_variants_single(self, p):
"""variants : variant"""
p[0] = [p[1]]
def p_variants_continue(self, p):
"""variants : variant ',' variants"""
p[0] = [p[1]] + p[3]
def p_variant_implicit(self, p):
"""variant : IDENTIFIER"""
p[0] = CEnumVariant(p[1], None, self.getLocation(p, 1))
def p_variant_explicit(self, p):
"""variant : IDENTIFIER '=' number"""
p[0] = CEnumVariant(p[1], p[3], self.getLocation(p, 1))
def p_member_att(self, p):
"""member : attributes optreadonly ATTRIBUTE type IDENTIFIER ';'"""
if 'doccomments' in p[1]: