Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / idl-parser / xpidl / jsonxpt.py
blob342188f6451b1022684792c6252b791c8f5c899e
1 #!/usr/bin/env python
2 # jsonxpt.py - Generate json XPT typelib files from IDL.
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 """Generate a json XPT typelib for an IDL file"""
10 import itertools
11 import json
13 from xpidl import xpidl
15 # A map of xpidl.py types to xpt enum variants
16 TypeMap = {
17 # builtins
18 "boolean": "TD_BOOL",
19 "void": "TD_VOID",
20 "int16_t": "TD_INT16",
21 "int32_t": "TD_INT32",
22 "int64_t": "TD_INT64",
23 "uint8_t": "TD_UINT8",
24 "uint16_t": "TD_UINT16",
25 "uint32_t": "TD_UINT32",
26 "uint64_t": "TD_UINT64",
27 "octet": "TD_UINT8",
28 "short": "TD_INT16",
29 "long": "TD_INT32",
30 "long long": "TD_INT64",
31 "unsigned short": "TD_UINT16",
32 "unsigned long": "TD_UINT32",
33 "unsigned long long": "TD_UINT64",
34 "float": "TD_FLOAT",
35 "double": "TD_DOUBLE",
36 "char": "TD_CHAR",
37 "string": "TD_PSTRING",
38 "wchar": "TD_WCHAR",
39 "wstring": "TD_PWSTRING",
40 # special types
41 "nsid": "TD_NSID",
42 "astring": "TD_ASTRING",
43 "utf8string": "TD_UTF8STRING",
44 "cstring": "TD_CSTRING",
45 "jsval": "TD_JSVAL",
46 "promise": "TD_PROMISE",
50 def flags(*flags):
51 return [flag for flag, cond in flags if cond]
54 def get_type(type, calltype, iid_is=None, size_is=None):
55 while isinstance(type, xpidl.Typedef):
56 type = type.realtype
58 if isinstance(type, xpidl.Builtin):
59 ret = {"tag": TypeMap[type.name]}
60 if type.name in ["string", "wstring"] and size_is is not None:
61 ret["tag"] += "_SIZE_IS"
62 ret["size_is"] = size_is
63 return ret
65 if isinstance(type, xpidl.Array):
66 # NB: For a Array<T> we pass down the iid_is to get the type of T.
67 # This allows Arrays of InterfaceIs types to work.
68 return {
69 "tag": "TD_ARRAY",
70 "element": get_type(type.type, calltype, iid_is),
73 if isinstance(type, xpidl.LegacyArray):
74 # NB: For a Legacy [array] T we pass down iid_is to get the type of T.
75 # This allows [array] of InterfaceIs types to work.
76 return {
77 "tag": "TD_LEGACY_ARRAY",
78 "size_is": size_is,
79 "element": get_type(type.type, calltype, iid_is),
82 if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
83 return {
84 "tag": "TD_INTERFACE_TYPE",
85 "name": type.name,
88 if isinstance(type, xpidl.WebIDL):
89 return {
90 "tag": "TD_DOMOBJECT",
91 "name": type.name,
92 "native": type.native,
93 "headerFile": type.headerFile,
96 if isinstance(type, xpidl.Native):
97 if type.specialtype == "nsid" and type.isPtr(calltype):
98 return {"tag": "TD_NSIDPTR"}
99 elif type.specialtype:
100 return {"tag": TypeMap[type.specialtype]}
101 elif iid_is is not None:
102 return {
103 "tag": "TD_INTERFACE_IS_TYPE",
104 "iid_is": iid_is,
106 else:
107 return {"tag": "TD_VOID"}
109 if isinstance(type, xpidl.CEnum):
110 # As far as XPConnect is concerned, cenums are just unsigned integers.
111 return {"tag": "TD_UINT%d" % type.width}
113 raise Exception("Unknown type!")
116 def mk_param(type, in_=0, out=0, optional=0):
117 return {
118 "type": type,
119 "flags": flags(
120 ("in", in_),
121 ("out", out),
122 ("optional", optional),
127 def mk_method(method, params, getter=0, setter=0, optargc=0, hasretval=0, symbol=0):
128 return {
129 "name": method.name,
130 # NOTE: We don't include any return value information here, as we'll
131 # never call the methods if they're marked notxpcom, and all xpcom
132 # methods return the same type (nsresult).
133 # XXX: If we ever use these files for other purposes than xptcodegen we
134 # may want to write that info.
135 "params": params,
136 "flags": flags(
137 ("getter", getter),
138 ("setter", setter),
139 ("hidden", method.noscript or method.notxpcom),
140 ("optargc", optargc),
141 ("jscontext", method.implicit_jscontext),
142 ("hasretval", hasretval),
143 ("symbol", method.symbol),
148 def attr_param_idx(p, m, attr):
149 attr_val = getattr(p, attr, None)
150 if not attr_val:
151 return None
152 for i, param in enumerate(m.params):
153 if param.name == attr_val:
154 return i
155 raise Exception(f"Need parameter named '{attr_val}' for attribute '{attr}'")
158 def build_interface(iface):
159 if iface.namemap is None:
160 raise Exception("Interface was not resolved.")
162 assert (
163 iface.attributes.scriptable
164 ), "Don't generate XPT info for non-scriptable interfaces"
166 # State used while building an interface
167 consts = []
168 methods = []
170 def build_const(c):
171 consts.append(
173 "name": c.name,
174 "type": get_type(c.basetype, ""),
175 "value": c.getValue(), # All of our consts are numbers
179 def build_cenum(b):
180 for var in b.variants:
181 consts.append(
183 "name": var.name,
184 "type": get_type(b, "in"),
185 "value": var.value,
189 def build_method(m):
190 params = []
191 for p in m.params:
192 params.append(
193 mk_param(
194 get_type(
195 p.realtype,
196 p.paramtype,
197 iid_is=attr_param_idx(p, m, "iid_is"),
198 size_is=attr_param_idx(p, m, "size_is"),
200 in_=p.paramtype.count("in"),
201 out=p.paramtype.count("out"),
202 optional=p.optional,
206 hasretval = len(m.params) > 0 and m.params[-1].retval
207 if not m.notxpcom and m.realtype.name != "void":
208 hasretval = True
209 params.append(mk_param(get_type(m.realtype, "out"), out=1))
211 methods.append(
212 mk_method(m, params, optargc=m.optional_argc, hasretval=hasretval)
215 def build_attr(a):
216 assert a.realtype.name != "void"
217 # Write the getter
218 getter_params = []
219 if not a.notxpcom:
220 getter_params.append(mk_param(get_type(a.realtype, "out"), out=1))
222 methods.append(mk_method(a, getter_params, getter=1, hasretval=1))
224 # And maybe the setter
225 if not a.readonly:
226 param = mk_param(get_type(a.realtype, "in"), in_=1)
227 methods.append(mk_method(a, [param], setter=1))
229 for member in iface.members:
230 if isinstance(member, xpidl.ConstMember):
231 build_const(member)
232 elif isinstance(member, xpidl.Attribute):
233 build_attr(member)
234 elif isinstance(member, xpidl.Method):
235 build_method(member)
236 elif isinstance(member, xpidl.CEnum):
237 build_cenum(member)
238 elif isinstance(member, xpidl.CDATA):
239 pass
240 else:
241 raise Exception("Unexpected interface member: %s" % member)
243 return {
244 "name": iface.name,
245 "uuid": iface.attributes.uuid,
246 "methods": methods,
247 "consts": consts,
248 "parent": iface.base,
249 "flags": flags(
250 ("function", iface.attributes.function),
251 ("builtinclass", iface.attributes.builtinclass),
252 ("main_process_only", iface.attributes.main_process_scriptable_only),
257 # These functions are the public interface of this module. They are very simple
258 # functions, but are exported so that if we need to do something more
259 # complex in them in the future we can.
262 def build_typelib(idl):
263 """Given a parsed IDL file, generate and return the typelib"""
264 return [
265 build_interface(p)
266 for p in idl.productions
267 if p.kind == "interface" and p.attributes.scriptable
271 def link(typelibs):
272 """Link a list of typelibs together into a single typelib"""
273 linked = list(itertools.chain.from_iterable(typelibs))
274 assert len(set(iface["name"] for iface in linked)) == len(
275 linked
276 ), "Multiple typelibs containing the same interface were linked together"
277 return linked
280 def write(typelib, fd):
281 """Write typelib into fd"""
282 json.dump(typelib, fd, indent=2, sort_keys=True)