diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /xpcom/reflect/xptinfo | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/reflect/xptinfo')
-rw-r--r-- | xpcom/reflect/xptinfo/moz.build | 21 | ||||
-rw-r--r-- | xpcom/reflect/xptinfo/xptcodegen.py | 646 | ||||
-rw-r--r-- | xpcom/reflect/xptinfo/xptinfo.cpp | 105 | ||||
-rw-r--r-- | xpcom/reflect/xptinfo/xptinfo.h | 711 |
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 */ |