diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/idl-parser/xpidl/jsonxpt.py | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/xpcom/idl-parser/xpidl/jsonxpt.py b/xpcom/idl-parser/xpidl/jsonxpt.py new file mode 100644 index 0000000000..342188f645 --- /dev/null +++ b/xpcom/idl-parser/xpidl/jsonxpt.py @@ -0,0 +1,282 @@ +#!/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 itertools +import json + +from xpidl import xpidl + +# 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_NSID", + "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 == "nsid" and type.isPtr(calltype): + return {"tag": "TD_NSIDPTR"} + elif 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"} + + 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!") + + +def mk_param(type, in_=0, out=0, optional=0): + return { + "type": type, + "flags": flags( + ("in", in_), + ("out", out), + ("optional", optional), + ), + } + + +def mk_method(method, params, getter=0, setter=0, optargc=0, hasretval=0, symbol=0): + return { + "name": method.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), + ("hidden", method.noscript or method.notxpcom), + ("optargc", optargc), + ("jscontext", method.implicit_jscontext), + ("hasretval", hasretval), + ("symbol", method.symbol), + ), + } + + +def attr_param_idx(p, m, attr): + attr_val = getattr(p, attr, None) + if not attr_val: + return None + for i, param in enumerate(m.params): + if param.name == attr_val: + return i + raise Exception(f"Need parameter named '{attr_val}' for attribute '{attr}'") + + +def build_interface(iface): + if iface.namemap is None: + raise Exception("Interface was not resolved.") + + assert ( + iface.attributes.scriptable + ), "Don't generate XPT info for non-scriptable interfaces" + + # 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_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: + 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, params, optargc=m.optional_argc, hasretval=hasretval) + ) + + def build_attr(a): + assert a.realtype.name != "void" + # Write the getter + getter_params = [] + if not a.notxpcom: + getter_params.append(mk_param(get_type(a.realtype, "out"), out=1)) + + methods.append(mk_method(a, getter_params, getter=1, hasretval=1)) + + # And maybe the setter + if not a.readonly: + param = mk_param(get_type(a.realtype, "in"), in_=1) + methods.append(mk_method(a, [param], setter=1)) + + 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.CEnum): + build_cenum(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( + ("function", iface.attributes.function), + ("builtinclass", iface.attributes.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, sort_keys=True) |