summaryrefslogtreecommitdiffstats
path: root/xpcom/reflect/xptinfo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /xpcom/reflect/xptinfo
parentInitial commit. (diff)
downloadfirefox-esr-upstream/115.8.0esr.tar.xz
firefox-esr-upstream/115.8.0esr.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/reflect/xptinfo')
-rw-r--r--xpcom/reflect/xptinfo/moz.build21
-rw-r--r--xpcom/reflect/xptinfo/xptcodegen.py646
-rw-r--r--xpcom/reflect/xptinfo/xptinfo.cpp105
-rw-r--r--xpcom/reflect/xptinfo/xptinfo.h711
4 files changed, 1483 insertions, 0 deletions
diff --git a/xpcom/reflect/xptinfo/moz.build b/xpcom/reflect/xptinfo/moz.build
new file mode 100644
index 0000000000..9e4ce23258
--- /dev/null
+++ b/xpcom/reflect/xptinfo/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ "xptinfo.cpp",
+]
+
+SOURCES += [
+ "!xptdata.cpp",
+]
+
+EXPORTS += [
+ "xptinfo.h",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/xpcom/reflect/xptinfo/xptcodegen.py b/xpcom/reflect/xptinfo/xptcodegen.py
new file mode 100644
index 0000000000..34131f6678
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptcodegen.py
@@ -0,0 +1,646 @@
+#!/usr/bin/env python
+# jsonlink.py - Merge JSON typelib files into a .cpp file
+#
+# 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/.
+
+import json
+from collections import OrderedDict
+
+import buildconfig
+from perfecthash import PerfectHash
+
+# Pick a nice power-of-two size for our intermediate PHF tables.
+PHFSIZE = 512
+
+
+def indented(s):
+ return s.replace("\n", "\n ")
+
+
+def cpp(v):
+ if type(v) == bool:
+ return "true" if v else "false"
+ return str(v)
+
+
+def mkstruct(*fields):
+ def mk(comment, **vals):
+ assert len(fields) == len(vals)
+ r = "{ // " + comment
+ r += indented(",".join("\n/* %s */ %s" % (k, cpp(vals[k])) for k in fields))
+ r += "\n}"
+ return r
+
+ return mk
+
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
+nsXPTInterfaceInfo = mkstruct(
+ "mIID",
+ "mName",
+ "mParent",
+ "mBuiltinClass",
+ "mMainProcessScriptableOnly",
+ "mMethods",
+ "mConsts",
+ "mFunction",
+ "mNumMethods",
+ "mNumConsts",
+)
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
+nsXPTType = mkstruct(
+ "mTag",
+ "mInParam",
+ "mOutParam",
+ "mOptionalParam",
+ "mData1",
+ "mData2",
+)
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
+nsXPTParamInfo = mkstruct(
+ "mType",
+)
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
+nsXPTMethodInfo = mkstruct(
+ "mName",
+ "mParams",
+ "mNumParams",
+ "mGetter",
+ "mSetter",
+ "mReflectable",
+ "mOptArgc",
+ "mContext",
+ "mHasRetval",
+ "mIsSymbol",
+)
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
+nsXPTDOMObjectInfo = mkstruct(
+ "mUnwrap",
+ "mWrap",
+ "mCleanup",
+)
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
+nsXPTConstantInfo = mkstruct(
+ "mName",
+ "mSigned",
+ "mValue",
+)
+
+
+# Helper functions for dealing with IIDs.
+#
+# Unfortunately, the way we represent IIDs in memory depends on the endianness
+# of the target architecture. We store an nsIID as a 16-byte, 4-tuple of:
+#
+# (uint32_t, uint16_t, uint16_t, [uint8_t; 8])
+#
+# Unfortunately, this means that when we hash the bytes of the nsIID on a
+# little-endian target system, we need to hash them in little-endian order.
+# These functions let us split the input hexadecimal string into components,
+# encoding each as a little-endian value, and producing an accurate bytearray.
+#
+# It would be nice to have a consistent representation of IIDs in memory such
+# that we don't have to do these gymnastics to get an accurate hash.
+
+
+def split_at_idxs(s, lengths):
+ idx = 0
+ for length in lengths:
+ yield s[idx : idx + length]
+ idx += length
+ assert idx == len(s)
+
+
+def split_iid(iid): # Get the individual components out of an IID string.
+ iid = iid.replace("-", "") # Strip any '-' delimiters
+ return tuple(split_at_idxs(iid, (8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2)))
+
+
+def iid_bytes(iid): # Get the byte representation of the IID for hashing.
+ bs = bytearray()
+ for num in split_iid(iid):
+ b = bytearray.fromhex(num)
+ # Match endianness of the target platform for each component
+ if buildconfig.substs["TARGET_ENDIANNESS"] == "little":
+ b.reverse()
+ bs += b
+ return bs
+
+
+# Split a 16-bit integer into its high and low 8 bits
+def splitint(i):
+ assert i < 2 ** 16
+ return (i >> 8, i & 0xFF)
+
+
+# Occasionally in xpconnect, we need to fabricate types to pass into the
+# conversion methods. In some cases, these types need to be arrays, which hold
+# indicies into the extra types array.
+#
+# These are some types which should have known indexes into the extra types
+# array.
+utility_types = [
+ {"tag": "TD_INT8"},
+ {"tag": "TD_UINT8"},
+ {"tag": "TD_INT16"},
+ {"tag": "TD_UINT16"},
+ {"tag": "TD_INT32"},
+ {"tag": "TD_UINT32"},
+ {"tag": "TD_INT64"},
+ {"tag": "TD_UINT64"},
+ {"tag": "TD_FLOAT"},
+ {"tag": "TD_DOUBLE"},
+ {"tag": "TD_BOOL"},
+ {"tag": "TD_CHAR"},
+ {"tag": "TD_WCHAR"},
+ {"tag": "TD_NSIDPTR"},
+ {"tag": "TD_PSTRING"},
+ {"tag": "TD_PWSTRING"},
+ {"tag": "TD_INTERFACE_IS_TYPE", "iid_is": 0},
+]
+
+
+# Core of the code generator. Takes a list of raw JSON XPT interfaces, and
+# writes out a file containing the necessary static declarations into fd.
+def link_to_cpp(interfaces, fd, header_fd):
+ # Perfect Hash from IID to interface.
+ iid_phf = PerfectHash(interfaces, PHFSIZE, key=lambda i: iid_bytes(i["uuid"]))
+ for idx, iface in enumerate(iid_phf.entries):
+ iface["idx"] = idx # Store the index in iid_phf of the entry.
+
+ # Perfect Hash from name to iid_phf index.
+ name_phf = PerfectHash(interfaces, PHFSIZE, key=lambda i: i["name"].encode("ascii"))
+
+ def interface_idx(name):
+ entry = name and name_phf.get_entry(name.encode("ascii"))
+ if entry:
+ return entry["idx"] + 1 # 1-based, use 0 as a sentinel.
+ return 0
+
+ # NOTE: State used while linking. This is done with closures rather than a
+ # class due to how this file's code evolved.
+ includes = set()
+ types = []
+ type_cache = {}
+ params = []
+ param_cache = {}
+ methods = []
+ max_params = 0
+ method_with_max_params = None
+ consts = []
+ domobjects = []
+ domobject_cache = {}
+ strings = OrderedDict()
+
+ def lower_uuid(uuid):
+ return (
+ "{0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}}"
+ % split_iid(uuid)
+ )
+
+ def lower_domobject(do):
+ assert do["tag"] == "TD_DOMOBJECT"
+
+ idx = domobject_cache.get(do["name"])
+ if idx is None:
+ idx = domobject_cache[do["name"]] = len(domobjects)
+
+ includes.add(do["headerFile"])
+ domobjects.append(
+ nsXPTDOMObjectInfo(
+ "%d = %s" % (idx, do["name"]),
+ # These methods are defined at the top of the generated file.
+ mUnwrap="UnwrapDOMObject<mozilla::dom::prototypes::id::%s, %s>"
+ % (do["name"], do["native"]),
+ mWrap="WrapDOMObject<%s>" % do["native"],
+ mCleanup="CleanupDOMObject<%s>" % do["native"],
+ )
+ )
+
+ return idx
+
+ def lower_string(s):
+ if s in strings:
+ # We've already seen this string.
+ return strings[s]
+ elif len(strings):
+ # Get the last string we inserted (should be O(1) on OrderedDict).
+ last_s = next(reversed(strings))
+ strings[s] = strings[last_s] + len(last_s) + 1
+ else:
+ strings[s] = 0
+ return strings[s]
+
+ def lower_symbol(s):
+ return "uint32_t(JS::SymbolCode::%s)" % s
+
+ def lower_extra_type(type):
+ key = describe_type(type)
+ idx = type_cache.get(key)
+ if idx is None:
+ idx = type_cache[key] = len(types)
+ # Make sure `types` is the proper length for any recursive calls
+ # to `lower_extra_type` that might happen from within `lower_type`.
+ types.append(None)
+ realtype = lower_type(type)
+ types[idx] = realtype
+ return idx
+
+ def describe_type(type): # Create the type's documentation comment.
+ tag = type["tag"][3:].lower()
+ if tag == "legacy_array":
+ return "%s[size_is=%d]" % (describe_type(type["element"]), type["size_is"])
+ elif tag == "array":
+ return "Array<%s>" % describe_type(type["element"])
+ elif tag == "interface_type" or tag == "domobject":
+ return type["name"]
+ elif tag == "interface_is_type":
+ return "iid_is(%d)" % type["iid_is"]
+ elif tag.endswith("_size_is"):
+ return "%s(size_is=%d)" % (tag, type["size_is"])
+ return tag
+
+ def lower_type(type, in_=False, out=False, optional=False):
+ tag = type["tag"]
+ d1 = d2 = 0
+
+ # TD_VOID is used for types that can't be represented in JS, so they
+ # should not be represented in the XPT info.
+ assert tag != "TD_VOID"
+
+ if tag == "TD_LEGACY_ARRAY":
+ d1 = type["size_is"]
+ d2 = lower_extra_type(type["element"])
+
+ elif tag == "TD_ARRAY":
+ # NOTE: TD_ARRAY can hold 16 bits of type index, while
+ # TD_LEGACY_ARRAY can only hold 8.
+ d1, d2 = splitint(lower_extra_type(type["element"]))
+
+ elif tag == "TD_INTERFACE_TYPE":
+ d1, d2 = splitint(interface_idx(type["name"]))
+
+ elif tag == "TD_INTERFACE_IS_TYPE":
+ d1 = type["iid_is"]
+
+ elif tag == "TD_DOMOBJECT":
+ d1, d2 = splitint(lower_domobject(type))
+
+ elif tag.endswith("_SIZE_IS"):
+ d1 = type["size_is"]
+
+ assert d1 < 256 and d2 < 256, "Data values too large"
+ return nsXPTType(
+ describe_type(type),
+ mTag=tag,
+ mData1=d1,
+ mData2=d2,
+ mInParam=in_,
+ mOutParam=out,
+ mOptionalParam=optional,
+ )
+
+ def lower_param(param, paramname):
+ params.append(
+ nsXPTParamInfo(
+ "%d = %s" % (len(params), paramname),
+ mType=lower_type(
+ param["type"],
+ in_="in" in param["flags"],
+ out="out" in param["flags"],
+ optional="optional" in param["flags"],
+ ),
+ )
+ )
+
+ def is_type_reflectable(type):
+ # All native types end up getting tagged as void*, or as wrapper types around void*
+ if type["tag"] == "TD_VOID":
+ return False
+ if type["tag"] in ("TD_ARRAY", "TD_LEGACY_ARRAY"):
+ return is_type_reflectable(type["element"])
+ return True
+
+ def is_method_reflectable(method):
+ if "hidden" in method["flags"]:
+ return False
+
+ for param in method["params"]:
+ # Reflected methods can't use non-reflectable types.
+ if not is_type_reflectable(param["type"]):
+ return False
+
+ return True
+
+ def lower_method(method, ifacename):
+ methodname = "%s::%s" % (ifacename, method["name"])
+
+ isSymbol = "symbol" in method["flags"]
+ reflectable = is_method_reflectable(method)
+
+ if not reflectable:
+ # Hide the parameters of methods that can't be called from JS to
+ # reduce the size of the file.
+ paramidx = name = numparams = 0
+ else:
+ if isSymbol:
+ name = lower_symbol(method["name"])
+ else:
+ name = lower_string(method["name"])
+
+ numparams = len(method["params"])
+
+ # Check cache for parameters
+ cachekey = json.dumps(method["params"], sort_keys=True)
+ paramidx = param_cache.get(cachekey)
+ if paramidx is None:
+ paramidx = param_cache[cachekey] = len(params)
+ for idx, param in enumerate(method["params"]):
+ lower_param(param, "%s[%d]" % (methodname, idx))
+
+ nonlocal max_params, method_with_max_params
+ if numparams > max_params:
+ max_params = numparams
+ method_with_max_params = methodname
+ methods.append(
+ nsXPTMethodInfo(
+ "%d = %s" % (len(methods), methodname),
+ mName=name,
+ mParams=paramidx,
+ mNumParams=numparams,
+ # Flags
+ mGetter="getter" in method["flags"],
+ mSetter="setter" in method["flags"],
+ mReflectable=reflectable,
+ mOptArgc="optargc" in method["flags"],
+ mContext="jscontext" in method["flags"],
+ mHasRetval="hasretval" in method["flags"],
+ mIsSymbol=isSymbol,
+ )
+ )
+
+ def lower_const(const, ifacename):
+ assert const["type"]["tag"] in [
+ "TD_INT16",
+ "TD_INT32",
+ "TD_UINT8",
+ "TD_UINT16",
+ "TD_UINT32",
+ ]
+ is_signed = const["type"]["tag"] in ["TD_INT16", "TD_INT32"]
+
+ # Constants are always either signed or unsigned 16 or 32 bit integers,
+ # which we will only need to convert to JS values. To save on space,
+ # don't bother storing the type, and instead just store a 32-bit
+ # unsigned integer, and stash whether to interpret it as signed.
+ consts.append(
+ nsXPTConstantInfo(
+ "%d = %s::%s" % (len(consts), ifacename, const["name"]),
+ mName=lower_string(const["name"]),
+ mSigned=is_signed,
+ mValue="(uint32_t)%d" % const["value"],
+ )
+ )
+
+ def ancestors(iface):
+ yield iface
+ while iface["parent"]:
+ iface = name_phf.get_entry(iface["parent"].encode("ascii"))
+ yield iface
+
+ def lower_iface(iface):
+ method_cnt = sum(len(i["methods"]) for i in ancestors(iface))
+ const_cnt = sum(len(i["consts"]) for i in ancestors(iface))
+
+ # The number of maximum methods is not arbitrary. It is the same value
+ # as in xpcom/reflect/xptcall/genstubs.pl; do not change this value
+ # without changing that one or you WILL see problems.
+ #
+ # In addition, mNumMethods and mNumConsts are stored as a 8-bit ints,
+ # meaning we cannot exceed 255 methods/consts on any interface.
+ assert method_cnt < 250, "%s has too many methods" % iface["name"]
+ assert const_cnt < 256, "%s has too many constants" % iface["name"]
+
+ # Store the lowered interface as 'cxx' on the iface object.
+ iface["cxx"] = nsXPTInterfaceInfo(
+ "%d = %s" % (iface["idx"], iface["name"]),
+ mIID=lower_uuid(iface["uuid"]),
+ mName=lower_string(iface["name"]),
+ mParent=interface_idx(iface["parent"]),
+ mMethods=len(methods),
+ mNumMethods=method_cnt,
+ mConsts=len(consts),
+ mNumConsts=const_cnt,
+ # Flags
+ mBuiltinClass="builtinclass" in iface["flags"],
+ mMainProcessScriptableOnly="main_process_only" in iface["flags"],
+ mFunction="function" in iface["flags"],
+ )
+
+ # Lower methods and constants used by this interface
+ for method in iface["methods"]:
+ lower_method(method, iface["name"])
+ for const in iface["consts"]:
+ lower_const(const, iface["name"])
+
+ # Lower the types which have fixed indexes first, and check that the indexes
+ # seem correct.
+ for expected, ty in enumerate(utility_types):
+ got = lower_extra_type(ty)
+ assert got == expected, "Wrong index when lowering"
+
+ # Lower interfaces in the order of the IID phf's entries lookup.
+ for iface in iid_phf.entries:
+ lower_iface(iface)
+
+ # Write out the final output files
+ fd.write("/* THIS FILE WAS GENERATED BY xptcodegen.py - DO NOT EDIT */\n\n")
+ header_fd.write("/* THIS FILE WAS GENERATED BY xptcodegen.py - DO NOT EDIT */\n\n")
+
+ header_fd.write(
+ """
+#ifndef xptdata_h
+#define xptdata_h
+
+enum class nsXPTInterface : uint16_t {
+"""
+ )
+
+ for entry in iid_phf.entries:
+ header_fd.write(" %s,\n" % entry["name"])
+
+ header_fd.write(
+ """
+};
+
+#endif
+"""
+ )
+
+ # Include any bindings files which we need to include for webidl types
+ for include in sorted(includes):
+ fd.write('#include "%s"\n' % include)
+
+ # Write out our header
+ fd.write(
+ """
+#include "xptinfo.h"
+#include "mozilla/PerfectHash.h"
+#include "mozilla/dom/BindingUtils.h"
+
+// These template methods are specialized to be used in the sDOMObjects table.
+template<mozilla::dom::prototypes::ID PrototypeID, typename T>
+static nsresult UnwrapDOMObject(JS::Handle<JS::Value> aHandle, void** aObj, JSContext* aCx)
+{
+ RefPtr<T> p;
+ nsresult rv = mozilla::dom::UnwrapObject<PrototypeID, T>(aHandle, p, aCx);
+ p.forget(aObj);
+ return rv;
+}
+
+template<typename T>
+static bool WrapDOMObject(JSContext* aCx, void* aObj, JS::MutableHandle<JS::Value> aHandle)
+{
+ return mozilla::dom::GetOrCreateDOMReflector(aCx, reinterpret_cast<T*>(aObj), aHandle);
+}
+
+template<typename T>
+static void CleanupDOMObject(void* aObj)
+{
+ RefPtr<T> p = already_AddRefed<T>(reinterpret_cast<T*>(aObj));
+}
+
+namespace xpt {
+namespace detail {
+
+"""
+ )
+
+ # Static data arrays
+ def array(ty, name, els):
+ fd.write(
+ "const %s %s[] = {%s\n};\n\n"
+ % (ty, name, ",".join(indented("\n" + str(e)) for e in els))
+ )
+
+ array("nsXPTType", "sTypes", types)
+ array("nsXPTParamInfo", "sParams", params)
+ array("nsXPTMethodInfo", "sMethods", methods)
+ # Verify that stack-allocated buffers will do for xptcall implementations.
+ msg = (
+ "Too many method arguments in %s. "
+ "Either reduce the number of arguments "
+ "or increase PARAM_BUFFER_COUNT." % method_with_max_params
+ )
+ fd.write('static_assert(%s <= PARAM_BUFFER_COUNT, "%s");\n\n' % (max_params, msg))
+ array("nsXPTDOMObjectInfo", "sDOMObjects", domobjects)
+ array("nsXPTConstantInfo", "sConsts", consts)
+
+ # The strings array. We write out individual characters to avoid MSVC restrictions.
+ fd.write("const char sStrings[] = {\n")
+ for s, off in strings.items():
+ fd.write(" // %d = %s\n '%s','\\0',\n" % (off, s, "','".join(s)))
+ fd.write("};\n\n")
+
+ # Build the perfect hash table for InterfaceByIID
+ fd.write(
+ iid_phf.cxx_codegen(
+ name="InterfaceByIID",
+ entry_type="nsXPTInterfaceInfo",
+ entries_name="sInterfaces",
+ lower_entry=lambda iface: iface["cxx"],
+ # Check that the IIDs match to support IID keys not in the map.
+ return_type="const nsXPTInterfaceInfo*",
+ return_entry="return entry.IID().Equals(aKey) ? &entry : nullptr;",
+ key_type="const nsIID&",
+ key_bytes="reinterpret_cast<const char*>(&aKey)",
+ key_length="sizeof(nsIID)",
+ )
+ )
+ fd.write("\n")
+
+ # Build the perfect hash table for InterfaceByName
+ fd.write(
+ name_phf.cxx_codegen(
+ name="InterfaceByName",
+ entry_type="uint16_t",
+ lower_entry=lambda iface: "%-4d /* %s */" % (iface["idx"], iface["name"]),
+ # Get the actual nsXPTInterfaceInfo from sInterfaces, and
+ # double-check that names match.
+ return_type="const nsXPTInterfaceInfo*",
+ return_entry="return strcmp(sInterfaces[entry].Name(), aKey) == 0"
+ " ? &sInterfaces[entry] : nullptr;",
+ )
+ )
+ fd.write("\n")
+
+ # Generate some checks that the indexes for the utility types match the
+ # declared ones in xptinfo.h
+ for idx, ty in enumerate(utility_types):
+ fd.write(
+ 'static_assert(%d == (uint8_t)nsXPTType::Idx::%s, "Bad idx");\n'
+ % (idx, ty["tag"][3:])
+ )
+
+ fd.write(
+ """
+const uint16_t sInterfacesSize = mozilla::ArrayLength(sInterfaces);
+
+} // namespace detail
+} // namespace xpt
+"""
+ )
+
+
+def link_and_write(files, outfile, outheader):
+ interfaces = []
+ for file in files:
+ with open(file, "r") as fd:
+ interfaces += json.load(fd)
+
+ iids = set()
+ names = set()
+ for interface in interfaces:
+ assert interface["uuid"] not in iids, "duplicated UUID %s" % interface["uuid"]
+ assert interface["name"] not in names, "duplicated name %s" % interface["name"]
+ iids.add(interface["uuid"])
+ names.add(interface["name"])
+
+ link_to_cpp(interfaces, outfile, outheader)
+
+
+def main():
+ import sys
+ from argparse import ArgumentParser
+
+ parser = ArgumentParser()
+ parser.add_argument("outfile", help="Output C++ file to generate")
+ parser.add_argument("outheader", help="Output C++ header file to generate")
+ parser.add_argument("xpts", nargs="*", help="source xpt files")
+
+ args = parser.parse_args(sys.argv[1:])
+ with open(args.outfile, "w") as fd, open(args.outheader, "w") as header_fd:
+ link_and_write(args.xpts, fd, header_fd)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/xpcom/reflect/xptinfo/xptinfo.cpp b/xpcom/reflect/xptinfo/xptinfo.cpp
new file mode 100644
index 0000000000..c44e97cad4
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptinfo.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "xptinfo.h"
+#include "nsISupports.h"
+#include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/ArrayUtils.h"
+
+#include "jsfriendapi.h"
+#include "js/Symbol.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace xpt::detail;
+
+////////////////////////////////////
+// Constant Lookup Helper Methods //
+////////////////////////////////////
+
+bool nsXPTInterfaceInfo::HasAncestor(const nsIID& aIID) const {
+ for (const auto* info = this; info; info = info->GetParent()) {
+ if (info->IID() == aIID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const nsXPTConstantInfo& nsXPTInterfaceInfo::Constant(uint16_t aIndex) const {
+ MOZ_ASSERT(aIndex < ConstantCount());
+
+ if (const nsXPTInterfaceInfo* pi = GetParent()) {
+ if (aIndex < pi->ConstantCount()) {
+ return pi->Constant(aIndex);
+ }
+ aIndex -= pi->ConstantCount();
+ }
+
+ return xpt::detail::GetConstant(mConsts + aIndex);
+}
+
+const nsXPTMethodInfo& nsXPTInterfaceInfo::Method(uint16_t aIndex) const {
+ MOZ_ASSERT(aIndex < MethodCount());
+
+ if (const nsXPTInterfaceInfo* pi = GetParent()) {
+ if (aIndex < pi->MethodCount()) {
+ return pi->Method(aIndex);
+ }
+ aIndex -= pi->MethodCount();
+ }
+
+ return xpt::detail::GetMethod(mMethods + aIndex);
+}
+
+nsresult nsXPTInterfaceInfo::GetMethodInfo(
+ uint16_t aIndex, const nsXPTMethodInfo** aInfo) const {
+ *aInfo = aIndex < MethodCount() ? &Method(aIndex) : nullptr;
+ return *aInfo ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsXPTInterfaceInfo::GetConstant(uint16_t aIndex,
+ JS::MutableHandle<JS::Value> aConstant,
+ char** aName) const {
+ if (aIndex < ConstantCount()) {
+ aConstant.set(Constant(aIndex).JSValue());
+ *aName = moz_xstrdup(Constant(aIndex).Name());
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+////////////////////////////////////
+// nsXPTMethodInfo symbol helpers //
+////////////////////////////////////
+
+const char* nsXPTMethodInfo::SymbolDescription() const {
+ switch (GetSymbolCode()) {
+#define XPC_WELL_KNOWN_SYMBOL_DESCR_CASE(name) \
+ case JS::SymbolCode::name: \
+ return #name;
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(XPC_WELL_KNOWN_SYMBOL_DESCR_CASE)
+#undef XPC_WELL_KNOWN_SYMBOL_DESCR_CASE
+
+ default:
+ return "";
+ }
+}
+
+bool nsXPTMethodInfo::GetId(JSContext* aCx, jsid& aId) const {
+ if (IsSymbol()) {
+ aId = JS::PropertyKey::Symbol(GetSymbol(aCx));
+ return true;
+ }
+
+ JSString* str = JS_AtomizeString(aCx, Name());
+ if (!str) {
+ return false;
+ }
+ aId = JS::PropertyKey::NonIntAtom(str);
+ return true;
+}
diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h
new file mode 100644
index 0000000000..2456c2c2b5
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -0,0 +1,711 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+/**
+ * Structures and methods with information about XPCOM interfaces for use by
+ * XPConnect. The static backing data structures used by this file are generated
+ * from xpidl interfaces by the jsonxpt.py and xptcodegen.py scripts.
+ */
+
+#ifndef xptinfo_h
+#define xptinfo_h
+
+#include <stdint.h>
+#include "nsID.h"
+#include "mozilla/Assertions.h"
+#include "jsapi.h"
+#include "js/Symbol.h"
+#include "js/Value.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+// Forward Declarations
+namespace mozilla {
+namespace dom {
+struct NativePropertyHooks;
+} // namespace dom
+} // namespace mozilla
+
+struct nsXPTInterfaceInfo;
+struct nsXPTType;
+struct nsXPTParamInfo;
+struct nsXPTMethodInfo;
+struct nsXPTConstantInfo;
+struct nsXPTDOMObjectInfo;
+
+enum class nsXPTInterface : uint16_t;
+
+// Internal helper methods.
+namespace xpt {
+namespace detail {
+
+inline const nsXPTInterfaceInfo* GetInterface(uint16_t aIndex);
+inline const nsXPTType& GetType(uint16_t aIndex);
+inline const nsXPTParamInfo& GetParam(uint16_t aIndex);
+inline const nsXPTMethodInfo& GetMethod(uint16_t aIndex);
+inline const nsXPTConstantInfo& GetConstant(uint16_t aIndex);
+inline const nsXPTDOMObjectInfo& GetDOMObjectInfo(uint16_t aIndex);
+inline const char* GetString(uint32_t aIndex);
+
+const nsXPTInterfaceInfo* InterfaceByIID(const nsIID& aIID);
+const nsXPTInterfaceInfo* InterfaceByName(const char* aName);
+
+extern const uint16_t sInterfacesSize;
+
+} // namespace detail
+} // namespace xpt
+
+/*
+ * An Interface describes a single XPCOM interface, including all of its
+ * methods. We don't record non-scriptable interfaces.
+ */
+struct nsXPTInterfaceInfo {
+ // High efficiency getters for Interfaces based on perfect hashes.
+ static const nsXPTInterfaceInfo* ByIID(const nsIID& aIID) {
+ return xpt::detail::InterfaceByIID(aIID);
+ }
+ static const nsXPTInterfaceInfo* ByName(const char* aName) {
+ return xpt::detail::InterfaceByName(aName);
+ }
+
+ static const nsXPTInterfaceInfo* Get(nsXPTInterface aID) {
+ return ByIndex(uint16_t(aID));
+ }
+
+ // These are only needed for Components_interfaces's enumerator.
+ static const nsXPTInterfaceInfo* ByIndex(uint16_t aIndex) {
+ // NOTE: We add 1 here, as the internal index 0 is reserved for null.
+ return xpt::detail::GetInterface(aIndex + 1);
+ }
+ static uint16_t InterfaceCount() { return xpt::detail::sInterfacesSize; }
+
+ // Interface flag getters
+ bool IsFunction() const { return mFunction; }
+ bool IsBuiltinClass() const { return mBuiltinClass; }
+ bool IsMainProcessScriptableOnly() const {
+ return mMainProcessScriptableOnly;
+ }
+
+ const char* Name() const { return xpt::detail::GetString(mName); }
+ const nsIID& IID() const { return mIID; }
+
+ // Get the parent interface, or null if this interface doesn't have a parent.
+ const nsXPTInterfaceInfo* GetParent() const {
+ return xpt::detail::GetInterface(mParent);
+ }
+
+ // Do we have an ancestor interface with the given IID?
+ bool HasAncestor(const nsIID& aIID) const;
+
+ // Get methods & constants
+ uint16_t ConstantCount() const { return mNumConsts; }
+ const nsXPTConstantInfo& Constant(uint16_t aIndex) const;
+ uint16_t MethodCount() const { return mNumMethods; }
+ const nsXPTMethodInfo& Method(uint16_t aIndex) const;
+
+ nsresult GetMethodInfo(uint16_t aIndex, const nsXPTMethodInfo** aInfo) const;
+ nsresult GetConstant(uint16_t aIndex, JS::MutableHandle<JS::Value> constant,
+ char** aName) const;
+
+ ////////////////////////////////////////////////////////////////
+ // Ensure these fields are in the same order as xptcodegen.py //
+ ////////////////////////////////////////////////////////////////
+
+ nsID mIID;
+ uint32_t mName; // Index into xpt::detail::sStrings
+
+ uint16_t mParent : 14;
+ uint16_t mBuiltinClass : 1;
+ // XXX(nika): Do we need this if we don't have addons anymore?
+ uint16_t mMainProcessScriptableOnly : 1;
+
+ uint16_t mMethods; // Index into xpt::detail::sMethods
+
+ uint16_t mConsts : 14; // Index into xpt::detail::sConsts
+ uint16_t mFunction : 1;
+ // uint16_t unused : 1;
+
+ uint8_t mNumMethods; // NOTE(24/04/18): largest=nsIDocShell (193)
+ uint8_t mNumConsts; // NOTE(24/04/18): largest=nsIAccessibleRole (175)
+};
+
+// The fields in nsXPTInterfaceInfo were carefully ordered to minimize size.
+static_assert(sizeof(nsXPTInterfaceInfo) == 28, "wrong size?");
+
+/*
+ * The following enum represents contains the different tag types which
+ * can be found in nsXPTTypeInfo::mTag.
+ *
+ * WARNING: mTag is 5 bits wide, supporting at most 32 tags.
+ */
+enum nsXPTTypeTag : uint8_t {
+ // Arithmetic (POD) Types
+ // - Do not require cleanup,
+ // - All bit patterns are valid,
+ // - Outparams may be uninitialized by caller,
+ // - Directly supported in xptcall.
+ //
+ // NOTE: The name 'Arithmetic' comes from Harbison/Steele. Despite being a tad
+ // unclear, it is used frequently in xptcall, so is unlikely to be changed.
+ TD_INT8 = 0,
+ TD_INT16 = 1,
+ TD_INT32 = 2,
+ TD_INT64 = 3,
+ TD_UINT8 = 4,
+ TD_UINT16 = 5,
+ TD_UINT32 = 6,
+ TD_UINT64 = 7,
+ TD_FLOAT = 8,
+ TD_DOUBLE = 9,
+ TD_BOOL = 10,
+ TD_CHAR = 11,
+ TD_WCHAR = 12,
+ _TD_LAST_ARITHMETIC = TD_WCHAR,
+
+ // Pointer Types
+ // - Require cleanup unless NULL,
+ // - All-zeros (NULL) bit pattern is valid,
+ // - Outparams may be uninitialized by caller,
+ // - Supported in xptcall as raw pointer.
+ TD_VOID = 13,
+ TD_NSIDPTR = 14,
+ TD_PSTRING = 15,
+ TD_PWSTRING = 16,
+ TD_INTERFACE_TYPE = 17,
+ TD_INTERFACE_IS_TYPE = 18,
+ TD_LEGACY_ARRAY = 19,
+ TD_PSTRING_SIZE_IS = 20,
+ TD_PWSTRING_SIZE_IS = 21,
+ TD_DOMOBJECT = 22,
+ TD_PROMISE = 23,
+ _TD_LAST_POINTER = TD_PROMISE,
+
+ // Complex Types
+ // - Require cleanup,
+ // - Always passed indirectly,
+ // - Outparams must be initialized by caller,
+ // - Supported in xptcall due to indirection.
+ TD_UTF8STRING = 24,
+ TD_CSTRING = 25,
+ TD_ASTRING = 26,
+ TD_NSID = 27,
+ TD_JSVAL = 28,
+ TD_ARRAY = 29,
+ _TD_LAST_COMPLEX = TD_ARRAY
+};
+
+static_assert(_TD_LAST_COMPLEX < 32, "nsXPTTypeTag must fit in 5 bits");
+
+/*
+ * A nsXPTType is a union used to identify the type of a method argument or
+ * return value. The internal data is stored as an 5-bit tag, and two 8-bit
+ * integers, to keep alignment requirements low.
+ *
+ * nsXPTType contains 3 extra bits, reserved for use by nsXPTParamInfo.
+ */
+struct nsXPTType {
+ nsXPTTypeTag Tag() const { return static_cast<nsXPTTypeTag>(mTag); }
+
+ // The index in the function argument list which should be used when
+ // determining the iid_is or size_is properties of this dependent type.
+ uint8_t ArgNum() const {
+ MOZ_ASSERT(Tag() == TD_INTERFACE_IS_TYPE || Tag() == TD_PSTRING_SIZE_IS ||
+ Tag() == TD_PWSTRING_SIZE_IS || Tag() == TD_LEGACY_ARRAY);
+ return mData1;
+ }
+
+ private:
+ // Helper for reading 16-bit data values split between mData1 and mData2.
+ uint16_t Data16() const {
+ return static_cast<uint16_t>(mData1 << 8) | mData2;
+ }
+
+ public:
+ // Get the type of the element in the current array or sequence. Arrays only
+ // fit 8 bits of type data, while sequences support up to 16 bits of type data
+ // due to not needing to store an ArgNum.
+ const nsXPTType& ArrayElementType() const {
+ if (Tag() == TD_LEGACY_ARRAY) {
+ return xpt::detail::GetType(mData2);
+ }
+ MOZ_ASSERT(Tag() == TD_ARRAY);
+ return xpt::detail::GetType(Data16());
+ }
+
+ // We store the 16-bit iface value as two 8-bit values in order to
+ // avoid 16-bit alignment requirements for XPTTypeDescriptor, which
+ // reduces its size and also the size of XPTParamDescriptor.
+ const nsXPTInterfaceInfo* GetInterface() const {
+ MOZ_ASSERT(Tag() == TD_INTERFACE_TYPE);
+ return xpt::detail::GetInterface(Data16());
+ }
+
+ const nsXPTDOMObjectInfo& GetDOMObjectInfo() const {
+ MOZ_ASSERT(Tag() == TD_DOMOBJECT);
+ return xpt::detail::GetDOMObjectInfo(Data16());
+ }
+
+ // See the comments in nsXPTTypeTag for an explanation as to what each of
+ // these categories mean.
+ bool IsArithmetic() const { return Tag() <= _TD_LAST_ARITHMETIC; }
+ bool IsPointer() const {
+ return !IsArithmetic() && Tag() <= _TD_LAST_POINTER;
+ }
+ bool IsComplex() const { return Tag() > _TD_LAST_POINTER; }
+
+ bool IsInterfacePointer() const {
+ return Tag() == TD_INTERFACE_TYPE || Tag() == TD_INTERFACE_IS_TYPE;
+ }
+
+ bool IsDependent() const {
+ return (Tag() == TD_ARRAY && InnermostType().IsDependent()) ||
+ Tag() == TD_INTERFACE_IS_TYPE || Tag() == TD_LEGACY_ARRAY ||
+ Tag() == TD_PSTRING_SIZE_IS || Tag() == TD_PWSTRING_SIZE_IS;
+ }
+
+ // Unwrap a nested type to its innermost value (e.g. through arrays).
+ const nsXPTType& InnermostType() const {
+ if (Tag() == TD_LEGACY_ARRAY || Tag() == TD_ARRAY) {
+ return ArrayElementType().InnermostType();
+ }
+ return *this;
+ }
+
+ // In-memory size of native type in bytes.
+ inline size_t Stride() const;
+
+ // Offset the given base pointer to reference the element at the given index.
+ void* ElementPtr(const void* aBase, uint32_t aIndex) const {
+ return (char*)aBase + (aIndex * Stride());
+ }
+
+ // Zero out a native value of the given type. The type must not be 'complex'.
+ void ZeroValue(void* aValue) const {
+ MOZ_RELEASE_ASSERT(!IsComplex(), "Cannot zero a complex value");
+ memset(aValue, 0, Stride());
+ }
+
+ // Indexes into the extra types array of a small set of known types.
+ enum class Idx : uint8_t {
+ INT8 = 0,
+ UINT8,
+ INT16,
+ UINT16,
+ INT32,
+ UINT32,
+ INT64,
+ UINT64,
+ FLOAT,
+ DOUBLE,
+ BOOL,
+ CHAR,
+ WCHAR,
+ NSIDPTR,
+ PSTRING,
+ PWSTRING,
+ INTERFACE_IS_TYPE
+ };
+
+ // Helper methods for fabricating nsXPTType values used by xpconnect.
+ static nsXPTType MkArrayType(Idx aInner) {
+ MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE);
+ return {TD_LEGACY_ARRAY, false, false, false, 0, (uint8_t)aInner};
+ }
+ static const nsXPTType& Get(Idx aInner) {
+ MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE);
+ return xpt::detail::GetType((uint8_t)aInner);
+ }
+
+ ///////////////////////////////////////
+ // nsXPTType backwards compatibility //
+ ///////////////////////////////////////
+
+ nsXPTType& operator=(nsXPTTypeTag aPrefix) {
+ mTag = aPrefix;
+ return *this;
+ }
+ operator nsXPTTypeTag() const { return Tag(); }
+
+#define TD_ALIAS_(name_, value_) static constexpr nsXPTTypeTag name_ = value_
+ TD_ALIAS_(T_I8, TD_INT8);
+ TD_ALIAS_(T_I16, TD_INT16);
+ TD_ALIAS_(T_I32, TD_INT32);
+ TD_ALIAS_(T_I64, TD_INT64);
+ TD_ALIAS_(T_U8, TD_UINT8);
+ TD_ALIAS_(T_U16, TD_UINT16);
+ TD_ALIAS_(T_U32, TD_UINT32);
+ TD_ALIAS_(T_U64, TD_UINT64);
+ TD_ALIAS_(T_FLOAT, TD_FLOAT);
+ TD_ALIAS_(T_DOUBLE, TD_DOUBLE);
+ TD_ALIAS_(T_BOOL, TD_BOOL);
+ TD_ALIAS_(T_CHAR, TD_CHAR);
+ TD_ALIAS_(T_WCHAR, TD_WCHAR);
+ TD_ALIAS_(T_VOID, TD_VOID);
+ TD_ALIAS_(T_NSIDPTR, TD_NSIDPTR);
+ TD_ALIAS_(T_CHAR_STR, TD_PSTRING);
+ TD_ALIAS_(T_WCHAR_STR, TD_PWSTRING);
+ TD_ALIAS_(T_INTERFACE, TD_INTERFACE_TYPE);
+ TD_ALIAS_(T_INTERFACE_IS, TD_INTERFACE_IS_TYPE);
+ TD_ALIAS_(T_LEGACY_ARRAY, TD_LEGACY_ARRAY);
+ TD_ALIAS_(T_PSTRING_SIZE_IS, TD_PSTRING_SIZE_IS);
+ TD_ALIAS_(T_PWSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS);
+ TD_ALIAS_(T_UTF8STRING, TD_UTF8STRING);
+ TD_ALIAS_(T_CSTRING, TD_CSTRING);
+ TD_ALIAS_(T_ASTRING, TD_ASTRING);
+ TD_ALIAS_(T_NSID, TD_NSID);
+ TD_ALIAS_(T_JSVAL, TD_JSVAL);
+ TD_ALIAS_(T_DOMOBJECT, TD_DOMOBJECT);
+ TD_ALIAS_(T_PROMISE, TD_PROMISE);
+ TD_ALIAS_(T_ARRAY, TD_ARRAY);
+#undef TD_ALIAS_
+
+ ////////////////////////////////////////////////////////////////
+ // Ensure these fields are in the same order as xptcodegen.py //
+ ////////////////////////////////////////////////////////////////
+
+ uint8_t mTag : 5;
+
+ // Parameter bitflags are packed into the XPTTypeDescriptor to save space.
+ // When the TypeDescriptor is not in a parameter, these flags are ignored.
+ uint8_t mInParam : 1;
+ uint8_t mOutParam : 1;
+ uint8_t mOptionalParam : 1;
+
+ // The data for the different variants is stored in these two data fields.
+ // These should only be accessed via the getter methods above, which will
+ // assert if the tag is invalid.
+ uint8_t mData1;
+ uint8_t mData2;
+};
+
+// The fields in nsXPTType were carefully ordered to minimize size.
+static_assert(sizeof(nsXPTType) == 3, "wrong size");
+
+/*
+ * A nsXPTParamInfo is used to describe either a single argument to a method or
+ * a method's result. It stores its flags in the type descriptor to save space.
+ */
+struct nsXPTParamInfo {
+ bool IsIn() const { return mType.mInParam; }
+ bool IsOut() const { return mType.mOutParam; }
+ bool IsOptional() const { return mType.mOptionalParam; }
+ bool IsShared() const { return false; } // XXX remove (backcompat)
+
+ // Get the type of this parameter.
+ const nsXPTType& Type() const { return mType; }
+ const nsXPTType& GetType() const {
+ return Type();
+ } // XXX remove (backcompat)
+
+ // Whether this parameter is passed indirectly on the stack. All out/inout
+ // params are passed indirectly, and complex types are always passed
+ // indirectly.
+ bool IsIndirect() const { return IsOut() || Type().IsComplex(); }
+
+ ////////////////////////////////////////////////////////////////
+ // Ensure these fields are in the same order as xptcodegen.py //
+ ////////////////////////////////////////////////////////////////
+
+ nsXPTType mType;
+};
+
+// The fields in nsXPTParamInfo were carefully ordered to minimize size.
+static_assert(sizeof(nsXPTParamInfo) == 3, "wrong size");
+
+/*
+ * A nsXPTMethodInfo is used to describe a single interface method.
+ */
+struct nsXPTMethodInfo {
+ bool IsGetter() const { return mGetter; }
+ bool IsSetter() const { return mSetter; }
+ bool IsReflectable() const { return mReflectable; }
+ bool IsSymbol() const { return mIsSymbol; }
+ bool WantsOptArgc() const { return mOptArgc; }
+ bool WantsContext() const { return mContext; }
+ uint8_t ParamCount() const { return mNumParams; }
+
+ const char* Name() const {
+ MOZ_ASSERT(!IsSymbol());
+ return xpt::detail::GetString(mName);
+ }
+ const nsXPTParamInfo& Param(uint8_t aIndex) const {
+ MOZ_ASSERT(aIndex < mNumParams);
+ return xpt::detail::GetParam(mParams + aIndex);
+ }
+
+ bool HasRetval() const { return mHasRetval; }
+ const nsXPTParamInfo* GetRetval() const {
+ return mHasRetval ? &Param(mNumParams - 1) : nullptr;
+ }
+
+ // If this is an [implicit_jscontext] method, returns the index of the
+ // implicit JSContext* argument in the C++ method's argument list.
+ // Otherwise returns UINT8_MAX.
+ uint8_t IndexOfJSContext() const {
+ if (!WantsContext()) {
+ return UINT8_MAX;
+ }
+ if (IsGetter() || IsSetter()) {
+ // Getters/setters always have the context as first argument.
+ return 0;
+ }
+ // The context comes before the return value, if there is one.
+ MOZ_ASSERT_IF(HasRetval(), ParamCount() > 0);
+ return ParamCount() - uint8_t(HasRetval());
+ }
+
+ JS::SymbolCode GetSymbolCode() const {
+ MOZ_ASSERT(IsSymbol());
+ return JS::SymbolCode(mName);
+ }
+
+ JS::Symbol* GetSymbol(JSContext* aCx) const {
+ return JS::GetWellKnownSymbol(aCx, GetSymbolCode());
+ }
+
+ const char* SymbolDescription() const;
+
+ const char* NameOrDescription() const {
+ if (IsSymbol()) {
+ return SymbolDescription();
+ }
+ return Name();
+ }
+
+ bool GetId(JSContext* aCx, jsid& aId) const;
+
+ /////////////////////////////////////////////
+ // nsXPTMethodInfo backwards compatibility //
+ /////////////////////////////////////////////
+
+ const char* GetName() const { return Name(); }
+
+ uint8_t GetParamCount() const { return ParamCount(); }
+ const nsXPTParamInfo& GetParam(uint8_t aIndex) const { return Param(aIndex); }
+
+ ////////////////////////////////////////////////////////////////
+ // Ensure these fields are in the same order as xptcodegen.py //
+ ////////////////////////////////////////////////////////////////
+
+ uint32_t mName; // Index into xpt::detail::sStrings.
+ uint16_t mParams; // Index into xpt::detail::sParams.
+ uint8_t mNumParams;
+
+ uint8_t mGetter : 1;
+ uint8_t mSetter : 1;
+ uint8_t mReflectable : 1;
+ uint8_t mOptArgc : 1;
+ uint8_t mContext : 1;
+ uint8_t mHasRetval : 1;
+ uint8_t mIsSymbol : 1;
+};
+
+// The fields in nsXPTMethodInfo were carefully ordered to minimize size.
+static_assert(sizeof(nsXPTMethodInfo) == 8, "wrong size");
+
+// This number is chosen to be no larger than the maximum number of parameters
+// any XPIDL-defined function needs; there is a static assert in the generated
+// code from xptcodegen.py to verify that decision. It is therefore also the
+// maximum number of stack allocated nsXPTCMiniVariant structures for argument
+// passing purposes in PrepareAndDispatch implementations.
+#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
+# define PARAM_BUFFER_COUNT 18
+#else
+# define PARAM_BUFFER_COUNT 14
+#endif
+
+/**
+ * A nsXPTConstantInfo is used to describe a single interface constant.
+ */
+struct nsXPTConstantInfo {
+ const char* Name() const { return xpt::detail::GetString(mName); }
+
+ JS::Value JSValue() const {
+ if (mSigned || mValue <= uint32_t(INT32_MAX)) {
+ return JS::Int32Value(int32_t(mValue));
+ }
+ return JS::DoubleValue(mValue);
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Ensure these fields are in the same order as xptcodegen.py //
+ ////////////////////////////////////////////////////////////////
+
+ uint32_t mName : 31; // Index into xpt::detail::mStrings.
+
+ // Whether the value should be interpreted as a int32_t or uint32_t.
+ uint32_t mSigned : 1;
+ uint32_t mValue; // The value stored as a u32
+};
+
+// The fields in nsXPTConstantInfo were carefully ordered to minimize size.
+static_assert(sizeof(nsXPTConstantInfo) == 8, "wrong size");
+
+/**
+ * Object representing the information required to wrap and unwrap DOMObjects.
+ *
+ * This object will not live in rodata as it contains relocations.
+ */
+struct nsXPTDOMObjectInfo {
+ nsresult Unwrap(JS::Handle<JS::Value> aHandle, void** aObj,
+ JSContext* aCx) const {
+ return mUnwrap(aHandle, aObj, aCx);
+ }
+
+ bool Wrap(JSContext* aCx, void* aObj,
+ JS::MutableHandle<JS::Value> aHandle) const {
+ return mWrap(aCx, aObj, aHandle);
+ }
+
+ void Cleanup(void* aObj) const { return mCleanup(aObj); }
+
+ ////////////////////////////////////////////////////////////////
+ // Ensure these fields are in the same order as xptcodegen.py //
+ ////////////////////////////////////////////////////////////////
+
+ nsresult (*mUnwrap)(JS::Handle<JS::Value> aHandle, void** aObj,
+ JSContext* aCx);
+ bool (*mWrap)(JSContext* aCx, void* aObj,
+ JS::MutableHandle<JS::Value> aHandle);
+ void (*mCleanup)(void* aObj);
+};
+
+namespace xpt {
+namespace detail {
+
+// The UntypedTArray type allows low-level access from XPConnect to nsTArray
+// internals without static knowledge of the array element type in question.
+class UntypedTArray : public nsTArray_base<nsTArrayFallibleAllocator,
+ nsTArray_RelocateUsingMemutils> {
+ public:
+ void* Elements() const { return static_cast<void*>(Hdr() + 1); }
+
+ // Changes the length and capacity to be at least large enough for aTo
+ // elements.
+ bool SetLength(const nsXPTType& aEltTy, uint32_t aTo) {
+ if (!EnsureCapacity<nsTArrayFallibleAllocator>(aTo, aEltTy.Stride())) {
+ return false;
+ }
+
+ if (mHdr != EmptyHdr()) {
+ mHdr->mLength = aTo;
+ }
+
+ return true;
+ }
+
+ // Free backing memory for the nsTArray object.
+ void Clear() {
+ if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
+ nsTArrayFallibleAllocator::Free(mHdr);
+ }
+ mHdr = EmptyHdr();
+ }
+};
+
+//////////////////////////////////////////////
+// Raw typelib data stored in const statics //
+//////////////////////////////////////////////
+
+// XPIDL information
+extern const nsXPTInterfaceInfo sInterfaces[];
+extern const nsXPTType sTypes[];
+extern const nsXPTParamInfo sParams[];
+extern const nsXPTMethodInfo sMethods[];
+extern const nsXPTConstantInfo sConsts[];
+extern const nsXPTDOMObjectInfo sDOMObjects[];
+
+extern const char sStrings[];
+
+//////////////////////////////////////
+// Helper Methods for fetching data //
+//////////////////////////////////////
+
+inline const nsXPTInterfaceInfo* GetInterface(uint16_t aIndex) {
+ if (aIndex > 0 && aIndex <= sInterfacesSize) {
+ return &sInterfaces[aIndex - 1]; // 1-based as 0 is a marker.
+ }
+ return nullptr;
+}
+
+inline const nsXPTType& GetType(uint16_t aIndex) { return sTypes[aIndex]; }
+
+inline const nsXPTParamInfo& GetParam(uint16_t aIndex) {
+ return sParams[aIndex];
+}
+
+inline const nsXPTMethodInfo& GetMethod(uint16_t aIndex) {
+ return sMethods[aIndex];
+}
+
+inline const nsXPTConstantInfo& GetConstant(uint16_t aIndex) {
+ return sConsts[aIndex];
+}
+
+inline const nsXPTDOMObjectInfo& GetDOMObjectInfo(uint16_t aIndex) {
+ return sDOMObjects[aIndex];
+}
+
+inline const char* GetString(uint32_t aIndex) { return &sStrings[aIndex]; }
+
+} // namespace detail
+} // namespace xpt
+
+#define XPT_FOR_EACH_ARITHMETIC_TYPE(MACRO) \
+ MACRO(TD_INT8, int8_t) \
+ MACRO(TD_INT16, int16_t) \
+ MACRO(TD_INT32, int32_t) \
+ MACRO(TD_INT64, int64_t) \
+ MACRO(TD_UINT8, uint8_t) \
+ MACRO(TD_UINT16, uint16_t) \
+ MACRO(TD_UINT32, uint32_t) \
+ MACRO(TD_UINT64, uint64_t) \
+ MACRO(TD_FLOAT, float) \
+ MACRO(TD_DOUBLE, double) \
+ MACRO(TD_BOOL, bool) \
+ MACRO(TD_CHAR, char) \
+ MACRO(TD_WCHAR, char16_t)
+
+#define XPT_FOR_EACH_POINTER_TYPE(MACRO) \
+ MACRO(TD_VOID, void*) \
+ MACRO(TD_NSIDPTR, nsID*) \
+ MACRO(TD_PSTRING, char*) \
+ MACRO(TD_PWSTRING, wchar_t*) \
+ MACRO(TD_INTERFACE_TYPE, nsISupports*) \
+ MACRO(TD_INTERFACE_IS_TYPE, nsISupports*) \
+ MACRO(TD_LEGACY_ARRAY, void*) \
+ MACRO(TD_PSTRING_SIZE_IS, char*) \
+ MACRO(TD_PWSTRING_SIZE_IS, wchar_t*) \
+ MACRO(TD_DOMOBJECT, void*) \
+ MACRO(TD_PROMISE, mozilla::dom::Promise*)
+
+#define XPT_FOR_EACH_COMPLEX_TYPE(MACRO) \
+ MACRO(TD_UTF8STRING, nsCString) \
+ MACRO(TD_CSTRING, nsCString) \
+ MACRO(TD_ASTRING, nsString) \
+ MACRO(TD_NSID, nsID) \
+ MACRO(TD_JSVAL, JS::Value) \
+ MACRO(TD_ARRAY, xpt::detail::UntypedTArray)
+
+#define XPT_FOR_EACH_TYPE(MACRO) \
+ XPT_FOR_EACH_ARITHMETIC_TYPE(MACRO) \
+ XPT_FOR_EACH_POINTER_TYPE(MACRO) \
+ XPT_FOR_EACH_COMPLEX_TYPE(MACRO)
+
+inline size_t nsXPTType::Stride() const {
+ // Compute the stride to use when walking an array of the given type.
+ switch (Tag()) {
+#define XPT_TYPE_STRIDE(tag, type) \
+ case tag: \
+ return sizeof(type);
+ XPT_FOR_EACH_TYPE(XPT_TYPE_STRIDE)
+#undef XPT_TYPE_STRIDE
+ }
+
+ MOZ_CRASH("Unknown type");
+}
+
+#endif /* xptinfo_h */