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"""
13 from xpidl
import xpidl
15 # A map of xpidl.py types to xpt enum variants
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",
30 "long long": "TD_INT64",
31 "unsigned short": "TD_UINT16",
32 "unsigned long": "TD_UINT32",
33 "unsigned long long": "TD_UINT64",
35 "double": "TD_DOUBLE",
37 "string": "TD_PSTRING",
39 "wstring": "TD_PWSTRING",
42 "astring": "TD_ASTRING",
43 "utf8string": "TD_UTF8STRING",
44 "cstring": "TD_CSTRING",
46 "promise": "TD_PROMISE",
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
):
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
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.
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.
77 "tag": "TD_LEGACY_ARRAY",
79 "element": get_type(type.type, calltype
, iid_is
),
82 if isinstance(type, xpidl
.Interface
) or isinstance(type, xpidl
.Forward
):
84 "tag": "TD_INTERFACE_TYPE",
88 if isinstance(type, xpidl
.WebIDL
):
90 "tag": "TD_DOMOBJECT",
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:
103 "tag": "TD_INTERFACE_IS_TYPE",
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):
122 ("optional", optional
),
127 def mk_method(method
, params
, getter
=0, setter
=0, optargc
=0, hasretval
=0, symbol
=0):
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.
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)
152 for i
, param
in enumerate(m
.params
):
153 if param
.name
== attr_val
:
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.")
163 iface
.attributes
.scriptable
164 ), "Don't generate XPT info for non-scriptable interfaces"
166 # State used while building an interface
174 "type": get_type(c
.basetype
, ""),
175 "value": c
.getValue(), # All of our consts are numbers
180 for var
in b
.variants
:
184 "type": get_type(b
, "in"),
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"),
206 hasretval
= len(m
.params
) > 0 and m
.params
[-1].retval
207 if not m
.notxpcom
and m
.realtype
.name
!= "void":
209 params
.append(mk_param(get_type(m
.realtype
, "out"), out
=1))
212 mk_method(m
, params
, optargc
=m
.optional_argc
, hasretval
=hasretval
)
216 assert a
.realtype
.name
!= "void"
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
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
):
232 elif isinstance(member
, xpidl
.Attribute
):
234 elif isinstance(member
, xpidl
.Method
):
236 elif isinstance(member
, xpidl
.CEnum
):
238 elif isinstance(member
, xpidl
.CDATA
):
241 raise Exception("Unexpected interface member: %s" % member
)
245 "uuid": iface
.attributes
.uuid
,
248 "parent": iface
.base
,
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"""
266 for p
in idl
.productions
267 if p
.kind
== "interface" and p
.attributes
.scriptable
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(
276 ), "Multiple typelibs containing the same interface were linked together"
280 def write(typelib
, fd
):
281 """Write typelib into fd"""
282 json
.dump(typelib
, fd
, indent
=2, sort_keys
=True)