gecko-dev/xpcom/docs/xpidl.rst
Andrew McCreight ba8af7f4fa Bug 1792920, part 4 - Add special handling for MozExternalRefCountType to XPIDL. r=nika
In XPIDL, the return type of AddRef and Release are nsrefcnt, which
is defined as unsigned long. The first problem is that nsrefcnt is
defined in XPIDL as unsigned long, but in C++ as uintptr_t. The second
problem is that the actual return type of these methods in C++ is
MozExternalRefCountType.

This patch fixes the return type of these methods in XPIDL. However,
due to MSCOM compatibility requirements this type is not expressible
in XPIDL, so I added it as a new builtin type to XPIDL. In C++, it
defers to the typedef and for Rust it uses u32 directly. I tried
using the new Rust typedef I defined in refptr.rs, but it seemed like
it was being used in places that didn't have refptr.rs available.

I also deleted the incorrect definition of nsrefcnt in XPIDL. It is
not used anywhere else in XPIDL files, and my previous patches removed
all uses of this type definition from Rust.

Note that this patch does not really change nsISupports.h,
because this part of the file is ignored via an #if 0. However, this
patch does clear the way for further work to start using the auto
generated nsISupports.h instead of nsISupportsBase.h.

Differential Revision: https://phabricator.services.mozilla.com/D159323
2022-10-17 16:09:21 +00:00

391 lines
22 KiB
ReStructuredText

XPIDL
=====
**XPIDL** is an Interface Description Language used to specify XPCOM interface
classes.
Interface Description Languages (IDL) are used to describe interfaces in a
language- and machine-independent way. IDLs make it possible to define
interfaces which can then be processed by tools to autogenerate
language-dependent interface specifications.
An xpidl file is essentially just a series of declarations. At the top level,
we can define typedefs, native types, or interfaces. Interfaces may
furthermore contain typedefs, natives, methods, constants, or attributes.
Most declarations can have properties applied to them.
Types
-----
There are three ways to make types: a typedef, a native, or an interface. In
addition, there are a few built-in native types. The built-in native types
are those listed under the type_spec production above. The following is the
correspondence table:
=========================== =============== =========================== ============================ ======================= =======================
IDL Type Javascript Type C++ in parameter C++ out parameter Rust in parameter Rust out parameter
=========================== =============== =========================== ============================ ======================= =======================
``boolean`` boolean ``bool`` ``bool*`` ``bool`` ``*mut bool``
``char`` string ``char`` ``char*`` ``c_char`` ``*mut c_char``
``double`` number ``double`` ``double*`` ``f64`` ``*mut f64``
``float`` number ``float`` ``float*`` ``f32`` ``*mut f32``
``long`` number ``int32_t`` ``int32_t*`` ``i32`` ``*mut i32``
``long long`` number ``int64_t`` ``int64_t*`` ``i64`` ``*mut i64``
``octet`` number ``uint8_t`` ``uint8_t*`` ``u8`` ``*mut u8``
``short`` number ``uint16_t`` ``uint16_t*`` ``u16`` ``*mut u16``
``string`` [#strptr]_ string ``const char*`` ``char**`` ``*const c_char`` ``*mut *mut c_char``
``unsigned long`` number ``uint32_t`` ``uint32_t*`` ``u32`` ``*mut u32``
``unsigned long long`` number ``uint64_t`` ``uint64_t*`` ``u64`` ``*mut u64``
``unsigned short`` number ``uint16_t`` ``uint16_t*`` ``u16`` ``*mut u16``
``wchar`` string ``char16_t`` ``char16_t*`` ``i16`` ``*mut i16``
``wstring`` [#strptr]_ string ``const char16_t*`` ``char16_t**`` ``*const i16`` ``*mut *mut i16``
``MozExternalRefCountType`` number ``MozExternalRefCountType`` ``MozExternalRefCountType*`` ``u32`` ``*mut u32``
``Array<T>`` [#array]_ array ``const nsTArray<T>&`` ``nsTArray<T>&`` ``*const ThinVec<T>`` ``*mut ThinVec<T>``
=========================== =============== =========================== ============================ ======================= =======================
.. [#strptr]
Prefer using the string class types such as ``AString``, ``AUTF8String``
or ``ACString`` to this type. The behaviour of these types is documented
more in the :ref:`String Guide <stringguide.xpidl>`
.. [#array]
The C++ or Rust exposed type ``T`` will be an owned variant. (e.g.
``ns[C]String``, ``RefPtr<T>``, or ``uint32_t``)
``string``, ``wstring``, ``[ptr] native`` and ``[ref] native`` are
unsupported as element types.
In addition to this list, nearly every IDL file includes ``nsrootidl.idl`` in
some fashion, which also defines the following types:
======================= ======================= ======================= ======================= ======================= =======================
IDL Type Javascript Type C++ in parameter C++ out parameter Rust in parameter Rust out parameter
======================= ======================= ======================= ======================= ======================= =======================
``PRTime`` number ``uint64_t`` ``uint64_t*`` ``u64`` ``*mut u64``
``nsresult`` number ``nsresult`` ``nsresult*`` ``u32`` [#rsresult]_ ``*mut u32``
``size_t`` number ``uint32_t`` ``uint32_t*`` ``u32`` ``*mut u32``
``voidPtr`` N/A ``void*`` ``void**`` ``*mut c_void`` ``*mut *mut c_void``
``charPtr`` N/A ``char*`` ``char**`` ``*mut c_char`` ``*mut *mut c_char``
``unicharPtr`` N/A ``char16_t*`` ``char16_t**`` ``*mut i16`` ``*mut *mut i16``
``nsIDRef`` ID object ``const nsID&`` ``nsID*`` ``*const nsID`` ``*mut nsID``
``nsIIDRef`` ID object ``const nsIID&`` ``nsIID*`` ``*const nsIID`` ``*mut nsIID``
``nsCIDRef`` ID object ``const nsCID&`` ``nsCID*`` ``*const nsCID`` ``*mut nsCID``
``nsIDPtr`` ID object ``const nsID*`` ``nsID**`` ``*const nsID`` ``*mut *mut nsID``
``nsIIDPtr`` ID object ``const nsIID*`` ``nsIID**`` ``*const nsIID`` ``*mut *mut nsIID``
``nsCIDPtr`` ID object ``const nsCID*`` ``nsCID**`` ``*const nsCID`` ``*mut *mut nsCID``
``nsID`` N/A ``nsID`` ``nsID*`` N/A N/A
``nsIID`` N/A ``nsIID`` ``nsIID*`` N/A N/A
``nsCID`` N/A ``nsCID`` ``nsCID*`` N/A N/A
``nsQIResult`` object ``void*`` ``void**`` ``*mut c_void`` ``*mut *mut c_void``
``AUTF8String`` [#str]_ string ``const nsACString&`` ``nsACString&`` ``*const nsACString`` ``*mut nsACString``
``ACString`` [#str]_ string ``const nsACString&`` ``nsACString&`` ``*const nsACString`` ``*mut nsACString``
``AString`` [#str]_ string ``const nsAString&`` ``nsAString&`` ``*const nsAString`` ``*mut nsAString``
``jsval`` any ``HandleValue`` ``MutableHandleValue`` N/A N/A
``jsid`` N/A ``jsid`` ``jsid*`` N/A N/A
``Promise`` Promise object ``dom::Promise*`` ``dom::Promise**`` N/A N/A
======================= ======================= ======================= ======================= ======================= =======================
.. [#rsresult]
A bare ``u32`` is only for bare ``nsresult`` in/outparams in XPIDL. The
result should be wrapped as the ``nserror::nsresult`` type.
.. [#str]
The behaviour of these types is documented more in the :ref:`String Guide
<stringguide.xpidl>`
Typedefs in IDL are basically as they are in C or C++: you define first the
type that you want to refer to and then the name of the type. Types can of
course be one of the fundamental types, or any other type declared via a
typedef, interface, or a native type.
Native types are types which correspond to a given C++ type. Most native
types are not scriptable: if it is not present in the list above, then it is
certainly not scriptable (some of the above, particularly jsid, are not
scriptable).
The contents of the parentheses of a native type declaration (although native
declarations without parentheses are parsable, I do not trust that they are
properly handled by the xpidl handlers) is a string equivalent to the C++
type. XPIDL itself does not interpret this string, it just literally pastes
it anywhere the native type is used. The interpretation of the type can be
modified by using the ``[ptr]`` or ``[ref]`` attributes on the native
declaration. Other attributes are only intended for use in ``nsrootidl.idl``.
WebIDL Interfaces
~~~~~~~~~~~~~~~~~
WebIDL interfaces are also valid XPIDL types. To declare a WebIDL interface in
XPIDL, write:
.. code-block::
webidl InterfaceName;
WebIDL types will be passed as ``mozilla::dom::InterfaceName*`` when used as
in-parameters, as ``mozilla::dom::InterfaceName**`` when used as out or
inout-parameters, and as ``RefPtr<mozilla::dom::InterfaceName>`` when used as
an array element.
.. note::
Other WebIDL types (e.g. dictionaries, enums, and unions) are not currently
supported.
Constants and CEnums
~~~~~~~~~~~~~~~~~~~~
Constants must be attched to an interface. The only constants supported are
those which become integer types when compiled to source code; string constants
and floating point constants are currently not supported.
Often constants are used to describe a set of enum values. In cases like this
the ``cenum`` construct can be used to group constants together. Constants
grouped in a ``cenum`` will be reflected as-if they were declared directly on
the interface, in Rust and Javascript code.
.. code-block::
cenum MyCEnum : 8 {
eSomeValue, // starts at 0
eSomeOtherValue,
};
The number after the enum name, like ``: 8`` in the example above, defines the
width of enum values with the given type. The cenum's type may be referenced in
xpidl as ``nsIInterfaceName_MyCEnum``.
Interfaces
----------
Interfaces are basically a collection of constants, methods, and attributes.
Interfaces can inherit from one-another, and every interface must eventually
inherit from ``nsISupports``.
Interface Attributes
~~~~~~~~~~~~~~~~~~~~
Interfaces may have the following attributes:
``uuid``
````````
The internal unique identifier for the interface. it must be unique, and the
uuid must be generated when creating the interface. After that, it doesn't need
to be changed any more.
Online tools such as http://mozilla.pettay.fi/cgi-bin/mozuuid.pl can help
generate UUIDs for new interfaces.
``builtinclass``
````````````````
JavaScript classes are forbidden from implementing this interface. All child
interfaces must also be marked with this property.
``function``
````````````
The JavaScript implementation of this interface may be a function that is
invoked on property calls instead of an object with the given property
``scriptable``
``````````````
This interface is usable by JavaScript classes. Must inherit from a
``scriptable`` interface.
Methods and Attributes
~~~~~~~~~~~~~~~~~~~~~~
Interfaces declare a series of attributes and methods. Attributes in IDL are
akin to JavaScript properties, in that they are a getter and (optionally) a
setter pair. In JavaScript contexts, attributes are exposed as a regular
property access, while native code sees attributes as a Get and possibly a Set
method.
Attributes can be declared readonly, in which case setting causes an error to
be thrown in script contexts and native contexts lack the Set method, by using
the ``readonly`` keyword.
To native code, on attribute declared ``attribute type foo;`` is syntactic
sugar for the declaration of two methods ``type getFoo();`` and ``void
setFoo(in type foo);``. If ``foo`` were declared readonly, the latter method
would not be present. Attributes support all of the properties of methods with
the exception of ``optional_argc``, as this does not make sense for attributes.
There are some special rules for attribute naming. As a result of vtable
munging by the MSVC++ compiler, an attribute with the name ``IID`` is
forbidden. Also like methods, if the first character of an attribute is
lowercase in IDL, it is made uppercase in native code only.
Methods define a return type and a series of in and out parameters. When called
from a JavaScript context, they invocation looks as it is declared for the most
part; some parameter properties can adjust what the code looks like. The calls
are more mangled in native contexts.
An important attribute for methods and attributes is scriptability. A method or
attribute is scriptable if it is declared in a ``scriptable`` interface and it
lacks a ``noscript`` or ``notxpcom`` property. Any method that is not
scriptable can only be accessed by native code. However, ``scriptable`` methods
must contain parameters and a return type that can be translated to script: any
native type, save a few declared in ``nsrootidl.idl`` (see above), may not be
used in a scriptable method or attribute. An exception to the above rule is if
a ``nsQIResult`` parameter has the ``iid_is`` property (a special case for some
QueryInterface-like operations).
Methods and attributes are mangled on conversion to native code. If a method is
declared ``notxpcom``, the mangling of the return type is prevented, so it is
called mostly as it looks. Otherwise, the return type of the native method is
``nsresult``, and the return type acts as a final outparameter if it is not
``void``. The name is translated so that the first character is
unconditionally uppercase; subsequent characters are unaffected. However, the
presence of the ``binaryname`` property allows the user to select another name
to use in native code (to avoid conflicts with other functions). For example,
the method ``[binaryname(foo)] void bar();`` becomes ``nsresult Foo()`` in
native code (note that capitalization is still applied). However, the
capitalization is not applied when using ``binaryname`` with attributes; i.e.,
``[binaryname(foo)] readonly attribute Quux bar;`` becomes ``Getfoo(Quux**)``
in native code.
The ``implicit_jscontext`` and ``optional_argc`` parameters are properties
which help native code implementations determine how the call was made from
script. If ``implicit_jscontext`` is present on a method, then an additional
``JSContext* cx`` parameter is added just after the regular list which receives
the context of the caller. If ``optional_argc`` is present, then an additional
``uint8_t _argc`` parameter is added at the end which receives the number of
optional arguments that were actually used (obviously, you need to have an
optional argument in the first place). Note that if both properties are set,
the ``JSContext* cx`` is added first, followed by the ``uint8_t _argc``, and
then ending with return value parameter. Finally, as an exception to everything
already mentioned, for attribute getters and setters the ``JSContext *cx``
comes before any other arguments.
Another native-only property is ``nostdcall``. Normally, declarations are made
in the stdcall ABI on Windows to be ABI-compatible with COM interfaces. Any
non-scriptable method or attribute with ``nostdcall`` instead uses the
``thiscall`` ABI convention. Methods without this property generally use
``NS_IMETHOD`` in their declarations and ``NS_IMETHODIMP`` in their definitions
to automatically add in the stdcall declaration specifier on requisite
compilers; those that use this method may use a plain ``nsresult`` instead.
Another property, ``infallible``, is attribute-only. When present, it causes an
infallible C++ getter function definition to be generated for the attribute
alongside the normal fallible C++ getter declaration. It should only be used if
the fallible getter will be infallible in practice (i.e. always return
``NS_OK``) for all possible implementations. This infallible getter contains
code that calls the fallible getter, asserts success, and returns the gotten
value directly. The point of using this property is to make C++ code nicer -- a
call to the infallible getter is more concise and readable than a call to the
fallible getter. This property can only be used for attributes having built-in
or interface types, and within classes that are marked with ``builtinclass``.
The latter restriction is because C++ implementations of fallible getters can
be audited for infallibility, but JS implementations can always throw (e.g. due
to OOM).
The ``must_use`` property is useful if the result of a method call or an
attribute get/set should always (or usually) be checked, which is frequently
the case. (e.g. a method that opens a file should almost certainly have its
result checked.) This property will cause ``[[nodiscard]]`` to be added to the
generated function declarations, which means certain compilers (e.g. clang and
GCC) will reports errors if these results are not used.
Method Parameters
~~~~~~~~~~~~~~~~~
Each method parameter can be specified in one of three modes: ``in``, ``out``,
or ``inout``. An ``out`` parameter is essentially an auxiliary return value,
although these are moderately cumbersome to use from script contexts and should
therefore be avoided if reasonable. An ``inout`` parameter is an in parameter
whose value may be changed as a result of the method; these parameters are
rather annoying to use and should generally be avoided if at all possible.
``out`` and ``inout`` parameters are reflected as objects having the ``.value``
property which contains the real value of the parameter; the ``value``
attribute is missing in the case of ``out`` parameters and is initialized to
the passed-in-value for ``inout`` parameters. The script code needs to set this
property to assign a value to the parameter. Regular ``in`` parameters are
reflected more or less normally, with numeric types all representing numbers,
booleans as ``true`` or ``false``, the various strings (including ``AString``
etc.) as a JavaScript string, and ``nsID`` types as a ``Components.ID``
instance. In addition, the ``jsval`` type is translated as the appropriate
JavaScript value (since a ``jsval`` is the internal representation of all
JavaScript values), and parameters with the ``nsIVeriant`` interface have their
types automatically boxed and unboxed as appropriate.
The equivalent representations of all IDL types in native code is given in the
earlier tables; parameters of type ``inout`` follow their ``out`` form. Native
code should pay particular attention to not passing in null values for out
parameters (although some parts of the codebase are known to violate this, it
is strictly enforced at the JS<->native barrier).
Representations of types additionally depend on some of the many types of
properties they may have. The ``array`` property turns the parameter into an array;
the parameter must also have a corresponding ``size_is`` property whose argument is
the parameter that has the size of the array. In native code, the type gains
another pointer indirection, and JavaScript arrays are used in script code.
Script code callers can ignore the value of array parameter, but implementors
must still set the values appropriately.
.. note::
Prefer using the ``Array<T>`` builtin over the ``[array]`` attribute for
new code. It is more ergonomic to use from both JS and C++. In the future,
``[array]`` may be deprecated and removed.
The ``const`` and ``shared`` properties are special to native code. As its name
implies, the ``const`` property makes its corresponding argument ``const``. The
``shared`` property is only meaningful for ``out`` or ``inout`` parameters and
it means that the pointer value should not be freed by the caller. Only simple
native pointer types like ``string``, ``wstring``, and ``octetPtr`` may be
declared shared. The shared property also makes its corresponding argument
const.
The ``retval`` property indicates that the parameter is actually acting as the
return value, and it is only the need to assign properties to the parameter
that is causing it to be specified as a parameter. It has no effect on native
code, but script code uses it like a regular return value. Naturally, a method
which contains a ``retval`` parameter must be declared ``void``, and the
parameter itself must be an ``out`` parameter and the last parameter.
Other properties are the ``optional`` and ``iid_is`` property. The ``optional``
property indicates that script code may omit the property without problems; all
subsequent parameters must either by optional themselves or the retval
parameter. Note that optional out parameters still pass in a variable for the
parameter, but its value will be ignored. The ``iid_is`` parameter indicates
that the real IID of an ``nsQIResult`` parameter may be found in the
corresponding parameter, to allow script code to automatically unbox the type.
Not all type combinations are possible. Native types with the various string
properties are all forbidden from being used as an ``inout`` parameter or as an
``array`` parameter. In addition, native types with the ``nsid`` property but
lacking either a ``ptr`` or ``ref`` property are forbidden unless the method is
``notxpcom`` and it is used as an ``in`` parameter.
Ownership Rules
```````````````
For types that reference heap-allocated data (strings, arrays, interface
pointers, etc), you must follow the XPIDL data ownership conventions in order
to avoid memory corruption and security vulnerabilities:
* For ``in`` parameters, the caller allocates and deallocates all data. If the
callee needs to use the data after the call completes, it must make a private
copy of the data, or, in the case of interface pointers, ``AddRef`` it.
* For ``out`` parameters, the callee creates the data, and transfers ownership
to the caller. For buffers, the callee allocates the buffer with ``malloc``,
and the caller frees the buffer with ``free``. For interface pointers, the
callee does the ``AddRef`` on behalf of the caller, and the caller must call
``Release``. This manual reference/memory management should be performed
using the ``getter_AddRefs`` and ``getter_Transfers`` helpers in new code.
* For ``inout`` parameters, the callee must clean up the old data if it chooses
to replace it. Buffers must be deallocated with ``free``, and interface
pointers must be ``Release``'d. Afterwards, the above rules for ``out``
apply.
* ``shared`` out-parameters should not be freed, as they are intended to refer
to constant string literals.