gecko-dev/xpcom/idl-parser/xpidl/jsonxpt.py

254 строки
8.2 KiB
Python

#!/usr/bin/env python
# jsonxpt.py - Generate json XPT typelib files from IDL.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""Generate a json XPT typelib for an IDL file"""
import xpidl
import json
import itertools
# A map of xpidl.py types to xpt enum variants
TypeMap = {
# builtins
'boolean': 'TD_BOOL',
'void': 'TD_VOID',
'int16_t': 'TD_INT16',
'int32_t': 'TD_INT32',
'int64_t': 'TD_INT64',
'uint8_t': 'TD_UINT8',
'uint16_t': 'TD_UINT16',
'uint32_t': 'TD_UINT32',
'uint64_t': 'TD_UINT64',
'octet': 'TD_UINT8',
'short': 'TD_INT16',
'long': 'TD_INT32',
'long long': 'TD_INT64',
'unsigned short': 'TD_UINT16',
'unsigned long': 'TD_UINT32',
'unsigned long long': 'TD_UINT64',
'float': 'TD_FLOAT',
'double': 'TD_DOUBLE',
'char': 'TD_CHAR',
'string': 'TD_PSTRING',
'wchar': 'TD_WCHAR',
'wstring': 'TD_PWSTRING',
# special types
'nsid': 'TD_PNSIID',
'astring': 'TD_ASTRING',
'utf8string': 'TD_UTF8STRING',
'cstring': 'TD_CSTRING',
'jsval': 'TD_JSVAL',
'promise': 'TD_PROMISE',
}
def flags(*flags):
return [flag for flag, cond in flags if cond]
def get_type(type, calltype, iid_is=None, size_is=None):
while isinstance(type, xpidl.Typedef):
type = type.realtype
if isinstance(type, xpidl.Builtin):
ret = {'tag': TypeMap[type.name]}
if type.name in ['string', 'wstring'] and size_is is not None:
ret['tag'] += '_SIZE_IS'
ret['size_is'] = size_is
return ret
if isinstance(type, xpidl.Array):
# NB: For a Array<T> we pass down the iid_is to get the type of T.
# This allows Arrays of InterfaceIs types to work.
return {
'tag': 'TD_ARRAY',
'element': get_type(type.type, calltype, iid_is),
}
if isinstance(type, xpidl.LegacyArray):
# NB: For a Legacy [array] T we pass down iid_is to get the type of T.
# This allows [array] of InterfaceIs types to work.
return {
'tag': 'TD_LEGACY_ARRAY',
'size_is': size_is,
'element': get_type(type.type, calltype, iid_is),
}
if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
return {
'tag': 'TD_INTERFACE_TYPE',
'name': type.name,
}
if isinstance(type, xpidl.WebIDL):
return {
'tag': 'TD_DOMOBJECT',
'name': type.name,
'native': type.native,
'headerFile': type.headerFile,
}
if isinstance(type, xpidl.Native):
if type.specialtype:
return {
'tag': TypeMap[type.specialtype]
}
elif iid_is is not None:
return {
'tag': 'TD_INTERFACE_IS_TYPE',
'iid_is': iid_is,
}
else:
return {'tag': 'TD_VOID'}
raise Exception("Unknown type!")
def mk_param(type, in_=0, out=0, optional=0):
return {
'type': type,
'flags': flags(
('in', in_),
('out', out),
('optional', optional),
),
}
def mk_method(name, params, getter=0, setter=0, notxpcom=0,
hidden=0, optargc=0, context=0, hasretval=0,
symbol=0):
return {
'name': name,
# NOTE: We don't include any return value information here, as we'll
# never call the methods if they're marked notxpcom, and all xpcom
# methods return the same type (nsresult).
# XXX: If we ever use these files for other purposes than xptcodegen we
# may want to write that info.
'params': params,
'flags': flags(
('getter', getter),
('setter', setter),
('notxpcom', notxpcom),
('hidden', hidden),
('optargc', optargc),
('jscontext', context),
('hasretval', hasretval),
('symbol', symbol),
),
}
def attr_param_idx(p, m, attr):
if hasattr(p, attr) and getattr(p, attr):
for i, param in enumerate(m.params):
if param.name == getattr(p, attr):
return i
return None
def build_interface(iface):
if iface.namemap is None:
raise Exception("Interface was not resolved.")
# State used while building an interface
consts = []
methods = []
def build_const(c):
consts.append({
'name': c.name,
'type': get_type(c.basetype, ''),
'value': c.getValue(), # All of our consts are numbers
})
def build_method(m):
params = []
for p in m.params:
params.append(mk_param(
get_type(
p.realtype, p.paramtype,
iid_is=attr_param_idx(p, m, 'iid_is'),
size_is=attr_param_idx(p, m, 'size_is')),
in_=p.paramtype.count("in"),
out=p.paramtype.count("out"),
optional=p.optional,
))
hasretval = len(m.params) > 0 and m.params[-1].retval
if not m.notxpcom and m.realtype.name != 'void':
hasretval = True
params.append(mk_param(get_type(m.realtype, 'out'), out=1))
methods.append(mk_method(
m.name, params, notxpcom=m.notxpcom, hidden=m.noscript,
optargc=m.optional_argc, context=m.implicit_jscontext,
hasretval=hasretval, symbol=m.symbol))
def build_attr(a):
# Write the getter
param = mk_param(get_type(a.realtype, 'out'), out=1)
methods.append(mk_method(a.name, [param], getter=1, hidden=a.noscript,
context=a.implicit_jscontext, hasretval=1,
symbol=a.symbol))
# And maybe the setter
if not a.readonly:
param = mk_param(get_type(a.realtype, 'in'), in_=1)
methods.append(mk_method(a.name, [param], setter=1, hidden=a.noscript,
context=a.implicit_jscontext,
symbol=a.symbol))
for member in iface.members:
if isinstance(member, xpidl.ConstMember):
build_const(member)
elif isinstance(member, xpidl.Attribute):
build_attr(member)
elif isinstance(member, xpidl.Method):
build_method(member)
elif isinstance(member, xpidl.CDATA):
pass
else:
raise Exception("Unexpected interface member: %s" % member)
return {
'name': iface.name,
'uuid': iface.attributes.uuid,
'methods': methods,
'consts': consts,
'parent': iface.base,
'flags': flags(
('scriptable', iface.attributes.scriptable),
('function', iface.attributes.function),
('builtinclass', iface.attributes.builtinclass or iface.implicit_builtinclass),
('main_process_only', iface.attributes.main_process_scriptable_only),
)
}
# These functions are the public interface of this module. They are very simple
# functions, but are exported so that if we need to do something more
# complex in them in the future we can.
def build_typelib(idl):
"""Given a parsed IDL file, generate and return the typelib"""
return [build_interface(p) for p in idl.productions
if p.kind == 'interface' and p.attributes.scriptable]
def link(typelibs):
"""Link a list of typelibs together into a single typelib"""
linked = list(itertools.chain.from_iterable(typelibs))
assert len(set(iface['name'] for iface in linked)) == len(linked), \
"Multiple typelibs containing the same interface were linked together"
return linked
def write(typelib, fd):
"""Write typelib into fd"""
json.dump(typelib, fd, indent=2)