summaryrefslogtreecommitdiffstats
path: root/xpcom/idl-parser/xpidl/jsonxpt.py
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/idl-parser/xpidl/jsonxpt.py')
-rw-r--r--xpcom/idl-parser/xpidl/jsonxpt.py282
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)