def main(output, *filenames):
    """
    Generate a Perfect Hash Table for the Histogram name -> Histogram ID lookup.
    The table is immutable once generated and we can avoid any dynamic memory allocation.
    """

    output.write(banner)
    output.write(header)

    try:
        histograms = list(parse_histograms.from_files(filenames))
        histograms = [
            h for h in histograms
            if h.record_on_os(buildconfig.substs["OS_TARGET"])
        ]
    except ParserError as ex:
        print("\nError processing histograms:\n" + str(ex) + "\n")
        sys.exit(1)

    histograms = [(bytearray(hist.name(), 'ascii'), idx)
                  for (idx, hist) in enumerate(histograms)]
    name_phf = PerfectHash(histograms, PHFSIZE)

    output.write(
        name_phf.cxx_codegen(name='HistogramIDByNameLookup',
                             entry_type="uint32_t",
                             lower_entry=lambda x: str(x[1]),
                             key_type="const nsACString&",
                             key_bytes="aKey.BeginReading()",
                             key_length="aKey.Length()"))

    output.write(footer)
def main(output, *filenames):
    """
    Generate a Perfect Hash Table for the UserInteraction name -> UserInteraction ID lookup.
    The table is immutable once generated and we can avoid any dynamic memory allocation.
    """

    output.write(banner)
    output.write(header)

    try:
        user_interactions = list(parse_user_interactions.from_files(filenames))
    except ParserError as ex:
        print("\nError processing UserInteractions:\n" + str(ex) + "\n")
        sys.exit(1)

    user_interactions = [(bytearray(ui.label, "ascii"), idx)
                         for (idx, ui) in enumerate(user_interactions)]
    name_phf = PerfectHash(user_interactions, PHFSIZE)

    output.write(
        name_phf.cxx_codegen(
            name="UserInteractionIDByNameLookup",
            entry_type="uint32_t",
            lower_entry=lambda x: str(x[1]),
            key_type="const nsACString&",
            key_bytes="aKey.BeginReading()",
            key_length="aKey.Length()",
        ))

    output.write(footer)
Beispiel #3
0
def write_pings(objs, output_fd, template_filename):
    """
    Given a tree of objects `objs`, output pings-only code for the JS API to the
    file-like object `output_fd` using template `template_filename`
    """

    template = util.get_jinja2_template(
        template_filename,
        filters=(),
    )

    ping_string_table = StringTable()
    get_ping_id = generate_ping_ids(objs)
    # The map of a ping's name to its entry (a combination of a monotonic
    # integer and its index in the string table)
    pings = {}
    for ping_name in objs["pings"].keys():
        ping_id = get_ping_id(ping_name)
        ping_name = util.camelize(ping_name)
        pings[ping_name] = ping_entry(ping_id,
                                      ping_string_table.stringIndex(ping_name))

    ping_map = [(bytearray(ping_name, "ascii"), ping_entry)
                for (ping_name, ping_entry) in pings.items()]
    ping_string_table = ping_string_table.writeToString("gPingStringTable")
    ping_phf = PerfectHash(ping_map, 64)
    ping_by_name_lookup = ping_phf.cxx_codegen(
        name="PingByNameLookup",
        entry_type="ping_entry_t",
        lower_entry=lambda x: str(x[1]),
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
        return_type="static Maybe<uint32_t>",
        return_entry="return ping_result_check(aKey, entry);",
    )

    output_fd.write(
        template.render(
            ping_index_bits=PING_INDEX_BITS,
            ping_by_name_lookup=ping_by_name_lookup,
            ping_string_table=ping_string_table,
        ))
    output_fd.write("\n")
Beispiel #4
0
def output_js(objs, output_fd, options={}):
    """
    Given a tree of objects, output code for the JS API to the file-like object `output_fd`.

    :param objs: A tree of objects (metrics and pings) as returned from
    `parser.parse_objects`.
    :param output_fd: Writeable file to write the output to.
    :param options: options dictionary.
    """

    # Monkeypatch a util.snake_case function for the templates to use
    util.snake_case = lambda value: value.replace(".", "_").replace("-", "_")

    # Monkeypatch util.get_jinja2_template to find templates nearby

    def get_local_template(template_name, filters=()):
        env = jinja2.Environment(
            loader=jinja2.PackageLoader("js", "templates"),
            trim_blocks=True,
            lstrip_blocks=True,
        )
        env.filters["Camelize"] = util.Camelize
        for filter_name, filter_func in filters:
            env.filters[filter_name] = filter_func
        return env.get_template(template_name)

    util.get_jinja2_template = get_local_template
    template_filename = "js.jinja2"

    template = util.get_jinja2_template(
        template_filename,
        filters=(
            ("type_name", type_name),
            ("is_implemented_type", is_implemented_metric_type),
        ),
    )

    assert (INDEX_BITS + ID_BITS <
            ENTRY_WIDTH), "INDEX_BITS or ID_BITS are larger than allowed"

    get_metric_id = generate_metric_ids(objs)
    # Mapping from a metric's identifier to the entry (metric ID | type id | index)
    metric_id_mapping = {}
    categories = []

    category_string_table = StringTable()
    metric_string_table = StringTable()
    # Mapping from a type name to its ID
    metric_type_ids = {}

    for category_name, objs in objs.items():
        category_name = util.snake_case(category_name)
        id = category_string_table.stringIndex(category_name)
        categories.append((category_name, id))

        for metric in objs.values():
            identifier = metric_identifier(category_name, metric.name)
            if metric.type in metric_type_ids:
                type_id = metric_type_ids[metric.type]
            else:
                type_id = len(metric_type_ids) + 1
                metric_type_ids[metric.type] = type_id

            idx = metric_string_table.stringIndex(identifier)
            metric_id = get_metric_id(metric)
            entry = create_entry(metric_id, type_id, idx)
            metric_id_mapping[identifier] = entry

    # Create a lookup table for the metric categories only
    category_string_table = category_string_table.writeToString(
        "gCategoryStringTable")
    category_map = [(bytearray(category, "ascii"), id)
                    for (category, id) in categories]
    name_phf = PerfectHash(category_map, 64)
    category_by_name_lookup = name_phf.cxx_codegen(
        name="CategoryByNameLookup",
        entry_type="category_entry_t",
        lower_entry=lambda x: str(x[1]),
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
        return_type="static Maybe<uint32_t>",
        return_entry="return category_result_check(aKey, entry);",
    )

    # Create a lookup table for metric's identifiers.
    metric_string_table = metric_string_table.writeToString(
        "gMetricStringTable")
    metric_map = [(bytearray(metric_name, "ascii"), metric_id)
                  for (metric_name, metric_id) in metric_id_mapping.items()]
    metric_phf = PerfectHash(metric_map, 64)
    metric_by_name_lookup = metric_phf.cxx_codegen(
        name="MetricByNameLookup",
        entry_type="metric_entry_t",
        lower_entry=lambda x: str(x[1]),
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
        return_type="static Maybe<uint32_t>",
        return_entry="return metric_result_check(aKey, entry);",
    )

    output_fd.write(
        template.render(
            categories=categories,
            metric_id_mapping=metric_id_mapping,
            metric_type_ids=metric_type_ids,
            entry_width=ENTRY_WIDTH,
            index_bits=INDEX_BITS,
            id_bits=ID_BITS,
            category_string_table=category_string_table,
            category_by_name_lookup=category_by_name_lookup,
            metric_string_table=metric_string_table,
            metric_by_name_lookup=metric_by_name_lookup,
        ))
    output_fd.write("\n")
Beispiel #5
0
def write_metrics(objs, output_fd, template_filename):
    """
    Given a tree of objects `objs`, output metrics-only code for the JS API to the
    file-like object `output_fd` using template `template_filename`
    """

    template = util.get_jinja2_template(
        template_filename,
        filters=(
            ("type_name", type_name),
            ("is_implemented_type", is_implemented_metric_type),
        ),
    )

    assert (INDEX_BITS + ID_BITS <
            ENTRY_WIDTH), "INDEX_BITS or ID_BITS are larger than allowed"

    get_metric_id = generate_metric_ids(objs)
    # Mapping from a metric's identifier to the entry (metric ID | type id | index)
    metric_id_mapping = {}
    categories = []

    category_string_table = StringTable()
    metric_string_table = StringTable()
    # Mapping from a type name to its ID
    metric_type_ids = {}

    for category_name, objs in objs.items():
        category_name = util.camelize(category_name)
        id = category_string_table.stringIndex(category_name)
        categories.append((category_name, id))

        for metric in objs.values():
            identifier = metric_identifier(category_name, metric.name)
            if metric.type in metric_type_ids:
                type_id = metric_type_ids[metric.type]
            else:
                type_id = len(metric_type_ids) + 1
                metric_type_ids[metric.type] = type_id

            idx = metric_string_table.stringIndex(identifier)
            metric_id = get_metric_id(metric)
            entry = create_entry(metric_id, type_id, idx)
            metric_id_mapping[identifier] = entry

    # Create a lookup table for the metric categories only
    category_string_table = category_string_table.writeToString(
        "gCategoryStringTable")
    category_map = [(bytearray(category, "ascii"), id)
                    for (category, id) in categories]
    name_phf = PerfectHash(category_map, 64)
    category_by_name_lookup = name_phf.cxx_codegen(
        name="CategoryByNameLookup",
        entry_type="category_entry_t",
        lower_entry=lambda x: str(x[1]),
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
        return_type="static Maybe<uint32_t>",
        return_entry="return category_result_check(aKey, entry);",
    )

    # Create a lookup table for metric's identifiers.
    metric_string_table = metric_string_table.writeToString(
        "gMetricStringTable")
    metric_map = [(bytearray(metric_name, "ascii"), metric_id)
                  for (metric_name, metric_id) in metric_id_mapping.items()]
    metric_phf = PerfectHash(metric_map, 64)
    metric_by_name_lookup = metric_phf.cxx_codegen(
        name="MetricByNameLookup",
        entry_type="metric_entry_t",
        lower_entry=lambda x: str(x[1]),
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
        return_type="static Maybe<uint32_t>",
        return_entry="return metric_result_check(aKey, entry);",
    )

    output_fd.write(
        template.render(
            categories=categories,
            metric_id_mapping=metric_id_mapping,
            metric_type_ids=metric_type_ids,
            entry_width=ENTRY_WIDTH,
            index_bits=INDEX_BITS,
            id_bits=ID_BITS,
            category_string_table=category_string_table,
            category_by_name_lookup=category_by_name_lookup,
            metric_string_table=metric_string_table,
            metric_by_name_lookup=metric_by_name_lookup,
        ))
    output_fd.write("\n")
Beispiel #6
0
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_method_reflectable(method):
        if "hidden" in method["flags"]:
            return False

        for param in method["params"]:
            # Reflected methods can't use native types. All native types end up
            # getting tagged as void*, so this check is easy.
            if param["type"]["tag"] == "TD_VOID":
                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("""
enum class nsXPTInterface : uint16_t {
""")

    for entry in iid_phf.entries:
        header_fd.write("  %s,\n" % entry["name"])

    header_fd.write("""
};
""")

    # 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::HandleValue 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::MutableHandleValue 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
""")
Beispiel #7
0
def link_to_cpp(interfaces, fd):
    # Perfect Hash from IID into the ifaces array.
    iid_phf = PerfectHash(PHFSIZE, [(iid_bytes(iface['uuid']), iface)
                                    for iface in interfaces])
    # Perfect Hash from name to index in the ifaces array.
    name_phf = PerfectHash(PHFSIZE,
                           [(bytearray(iface['name'], 'ascii'), idx)
                            for idx, iface in enumerate(iid_phf.values)])

    def interface_idx(name):
        if name is not None:
            idx = name_phf.lookup(bytearray(name, 'ascii'))
            if iid_phf.values[idx]['name'] == name:
                return idx + 1  # One-based, so we can 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 = {}
    ifaces = []
    params = []
    param_cache = {}
    methods = []
    consts = []
    prophooks = []
    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_extra_type(type):
        key = describe_type(type)
        idx = type_cache.get(key)
        if idx is None:
            idx = type_cache[key] = len(types)
            types.append(lower_type(type))
        return idx

    def describe_type(type):  # Create the type's documentation comment.
        tag = type['tag'][3:].lower()
        if tag == 'array':
            return '%s[size_is=%d]' % (describe_type(
                type['element']), type['size_is'])
        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

        if tag == 'TD_ARRAY':
            d1 = type['size_is']
            d2 = 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 lower_method(method, ifacename):
        methodname = "%s::%s" % (ifacename, method['name'])

        if 'notxpcom' in method['flags'] or 'hidden' in method['flags']:
            paramidx = name = numparams = 0  # hide parameters
        else:
            name = lower_string(method['name'])
            numparams = len(method['params'])

            # Check cache for parameters
            cachekey = json.dumps(method['params'])
            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))

        methods.append(
            nsXPTMethodInfo(
                "%d = %s" % (len(methods), methodname),

                # If our method is hidden, we can save some memory by not
                # generating parameter info about it.
                mName=name,
                mParams=paramidx,
                mNumParams=numparams,

                # Flags
                mGetter='getter' in method['flags'],
                mSetter='setter' in method['flags'],
                mNotXPCOM='notxpcom' in method['flags'],
                mHidden='hidden' in method['flags'],
                mOptArgc='optargc' in method['flags'],
                mContext='jscontext' in method['flags'],
                mHasRetval='hasretval' in method['flags'],
            ))

    def lower_const(const, ifacename):
        assert const['type']['tag'] in \
            ['TD_INT16', 'TD_INT32', '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(
            ConstInfo(
                "%d = %s::%s" % (len(consts), ifacename, const['name']),
                mName=lower_string(const['name']),
                mSigned=is_signed,
                mValue="(uint32_t)%d" % const['value'],
            ))

    def lower_prop_hooks(iface):  # XXX: Used by xpt shims
        assert iface['shim'] is not None

        # Add an include for the Binding file for the shim.
        includes.add("mozilla/dom/%sBinding.h" %
                     (iface['shimfile'] or iface['shim']))

        # Add the property hook reference to the sPropHooks table.
        prophooks.append(
            "mozilla::dom::%sBinding::sNativePropertyHooks, // %d = %s(%s)" %
            (iface['shim'], len(prophooks), iface['name'], iface['shim']))

    def collect_base_info(iface):
        methods = 0
        consts = 0
        while iface is not None:
            methods += len(iface['methods'])
            consts += len(iface['consts'])
            idx = interface_idx(iface['parent'])
            if idx == 0:
                break
            iface = iid_phf.values[idx - 1]

        return methods, consts

    def lower_iface(iface):
        isshim = iface['shim'] is not None
        assert isshim or 'scriptable' in iface['flags']

        method_off = len(methods)
        consts_off = len(consts)
        method_cnt = const_cnt = 0
        if isshim:
            # If we are looking at a shim, don't lower any methods or constants,
            # as they will be pulled from the WebIDL binding instead. Instead,
            # we use the constants offset field to store the index into the prop
            # hooks table.
            consts_off = len(prophooks)
        else:
            method_cnt, const_cnt = collect_base_info(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']

        ifaces.append(
            nsXPTInterfaceInfo(
                "%d = %s" % (len(ifaces), iface['name']),
                mIID=lower_uuid(iface['uuid']),
                mName=lower_string(iface['name']),
                mParent=interface_idx(iface['parent']),
                mMethods=method_off,
                mNumMethods=method_cnt,
                mConsts=consts_off,
                mNumConsts=const_cnt,

                # Flags
                mIsShim=isshim,
                mBuiltinClass='builtinclass' in iface['flags'],
                mMainProcessScriptableOnly='main_process_only'
                in iface['flags'],
                mFunction='function' in iface['flags'],
            ))

        if isshim:
            lower_prop_hooks(iface)
            return

        # Lower the 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 values lookup.
    for iface in iid_phf.values:
        lower_iface(iface)

    # Write out the final output file
    fd.write(
        "/* THIS FILE WAS GENERATED BY xptcodegen.py - DO NOT EDIT */\n\n")

    # Include any bindings files which we need to include due to XPT shims.
    for include in includes:
        fd.write('#include "%s"\n' % include)

    # Write out our header
    fd.write("""
#include "xptinfo.h"
#include "mozilla/TypeTraits.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::HandleValue aHandle, void** aObj)
{
  RefPtr<T> p;
  nsresult rv = mozilla::dom::UnwrapObject<PrototypeID, T>(aHandle, p);
  p.forget(aObj);
  return rv;
}

template<typename T>
static bool WrapDOMObject(JSContext* aCx, void* aObj, JS::MutableHandleValue 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("nsXPTInterfaceInfo", "sInterfaces", ifaces)
    array("nsXPTType", "sTypes", types)
    array("nsXPTParamInfo", "sParams", params)
    array("nsXPTMethodInfo", "sMethods", methods)
    array("nsXPTDOMObjectInfo", "sDOMObjects", domobjects)
    array("ConstInfo", "sConsts", consts)
    array("mozilla::dom::NativePropertyHooks*", "sPropHooks", prophooks)

    # The strings array. We write out individual characters to avoid MSVC restrictions.
    fd.write("const char sStrings[] = {\n")
    for s, off in strings.iteritems():
        fd.write("  // %d = %s\n  '%s','\\0',\n" % (off, s, "','".join(s)))
    fd.write("};\n\n")

    # Record the information required for perfect hashing.
    # NOTE: Intermediates stored as 32-bit for safety. Shouldn't need >16-bit.
    def phfarr(name, ty, it):
        fd.write("const %s %s[] = {" % (ty, name))
        for idx, v in enumerate(it):
            if idx % 8 == 0:
                fd.write('\n ')
            fd.write(" 0x%04x," % v)
        fd.write("\n};\n\n")

    phfarr("sPHF_IIDs", "uint32_t", iid_phf.intermediate)
    phfarr("sPHF_Names", "uint32_t", name_phf.intermediate)
    phfarr("sPHF_NamesIdxs", "uint16_t", name_phf.values)

    # 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:]))

    # The footer contains some checks re: the size of the generated arrays.
    fd.write("""
const uint16_t sInterfacesSize = mozilla::ArrayLength(sInterfaces);
static_assert(sInterfacesSize == mozilla::ArrayLength(sPHF_NamesIdxs),
              "sPHF_NamesIdxs must have same size as sInterfaces");

static_assert(kPHFSize == mozilla::ArrayLength(sPHF_Names),
              "sPHF_IIDs must have size kPHFSize");
static_assert(kPHFSize == mozilla::ArrayLength(sPHF_IIDs),
              "sPHF_Names must have size kPHFSize");

} // namespace detail
} // namespace xpt
""")
def gen_substs(manifests):
    module_funcs = []

    headers = set()

    modules = []

    for manifest in manifests:
        headers |= set(manifest.get('Headers', []))

        init_idx = None
        init = manifest.get('InitFunc')
        unload = manifest.get('UnloadFunc')
        if init or unload:
            init_idx = len(module_funcs)
            module_funcs.append((init, unload))

        for clas in manifest['Classes']:
            modules.append(ModuleEntry(clas, init_idx))

    contracts = []
    contract_map = {}
    categories = defaultdict(list)

    jsms = set()

    types = set()

    for mod in modules:
        headers |= set(mod.headers)

        for contract_id in mod.contract_ids:
            if contract_id in contract_map:
                raise Exception('Duplicate contract ID: %s' % contract_id)

            entry = ContractEntry(contract_id, mod)
            contracts.append(entry)
            contract_map[contract_id] = entry

        for category, entries in mod.categories.items():
            for entry in to_list(entries):
                categories[category].append(
                    (entry, mod.contract_id, mod.processes))

        if mod.type and not mod.headers:
            types.add(mod.type)

        if mod.jsm:
            jsms.add(mod.jsm)

    cid_phf = PerfectHash(modules,
                          PHF_SIZE,
                          key=lambda module: module.cid.bytes)

    contract_phf = PerfectHash(contracts,
                               PHF_SIZE,
                               key=lambda entry: entry.contract)

    substs = {}

    gen_categories(substs, categories)

    substs['module_ids'] = ''.join('  %s,\n' % entry.name
                                   for entry in cid_phf.entries)

    substs['module_count'] = len(modules)
    substs['contract_count'] = len(contracts)

    gen_module_funcs(substs, module_funcs)

    gen_includes(substs, headers)

    substs['component_jsms'] = '\n'.join(' %s,' % strings.entry_to_cxx(jsm)
                                         for jsm in sorted(jsms)) + '\n'

    substs['decls'] = gen_decls(types)

    substs['constructors'] = gen_constructors(cid_phf.entries)

    substs['component_getters'] = gen_getters(cid_phf.entries)

    substs['module_cid_table'] = cid_phf.cxx_codegen(
        name='ModuleByCID',
        entry_type='StaticModule',
        entries_name='gStaticModules',
        lower_entry=lambda entry: entry.to_cxx(),
        return_type='const StaticModule*',
        return_entry=('return entry.CID().Equals(aKey) && entry.Active()'
                      ' ? &entry : nullptr;'),
        key_type='const nsID&',
        key_bytes='reinterpret_cast<const char*>(&aKey)',
        key_length='sizeof(nsID)')

    substs['module_contract_id_table'] = contract_phf.cxx_codegen(
        name='LookupContractID',
        entry_type='ContractEntry',
        entries_name='gContractEntries',
        lower_entry=lambda entry: entry.to_cxx(),
        return_type='const ContractEntry*',
        return_entry='return entry.Matches(aKey) ? &entry : nullptr;',
        key_type='const nsACString&',
        key_bytes='aKey.BeginReading()',
        key_length='aKey.Length()')

    # Do this only after everything else has been emitted so we're sure the
    # string table is complete.
    substs['strings'] = strings.to_cxx()
    return substs
Beispiel #9
0
def link_to_cpp(interfaces, 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 = []
    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)
            types.append(lower_type(type))
        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

        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 lower_method(method, ifacename):
        methodname = "%s::%s" % (ifacename, method['name'])

        isSymbol = 'symbol' in method['flags']

        if 'notxpcom' in method['flags'] or 'hidden' in method['flags']:
            paramidx = name = numparams = 0  # hide parameters
        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'])
            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))

        methods.append(
            nsXPTMethodInfo(
                "%d = %s" % (len(methods), methodname),

                # If our method is hidden, we can save some memory by not
                # generating parameter info about it.
                mName=name,
                mParams=paramidx,
                mNumParams=numparams,

                # Flags
                mGetter='getter' in method['flags'],
                mSetter='setter' in method['flags'],
                mNotXPCOM='notxpcom' in method['flags'],
                mHidden='hidden' in method['flags'],
                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 file
    fd.write(
        "/* THIS FILE WAS GENERATED BY xptcodegen.py - DO NOT EDIT */\n\n")

    # 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/TypeTraits.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::HandleValue 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::MutableHandleValue 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)
    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.iteritems():
        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
""")
Beispiel #10
0
def gen_substs(manifests):
    module_funcs = []

    headers = set()

    modules = []
    categories = defaultdict(list)

    for manifest in manifests:
        headers |= set(manifest.get("Headers", []))

        init_idx = None
        init = manifest.get("InitFunc")
        unload = manifest.get("UnloadFunc")
        if init or unload:
            init_idx = len(module_funcs)
            module_funcs.append((init, unload))

        for clas in manifest["Classes"]:
            modules.append(ModuleEntry(clas, init_idx))

        for category, entries in manifest.get("Categories", {}).items():
            for key, entry in entries.items():
                if isinstance(entry, tuple):
                    value, process = entry
                else:
                    value, process = entry, 0
                categories[category].append((key, value, process))

    cids = set()
    contracts = []
    contract_map = {}
    js_services = {}

    jsms = set()

    types = set()

    for mod in modules:
        headers |= set(mod.headers)

        for contract_id in mod.contract_ids:
            if contract_id in contract_map:
                raise Exception("Duplicate contract ID: %s" % contract_id)

            entry = ContractEntry(contract_id, mod)
            contracts.append(entry)
            contract_map[contract_id] = entry

        for category, entries in mod.categories.items():
            for entry in to_category_list(entries):
                categories[category].append(
                    (entry, mod.contract_id, mod.processes))

        if mod.type and not mod.headers:
            types.add(mod.type)

        if mod.jsm:
            jsms.add(mod.jsm)

        if mod.js_name:
            if mod.js_name in js_services:
                raise Exception("Duplicate JS service name: %s" % mod.js_name)
            js_services[mod.js_name] = mod

        if str(mod.cid) in cids:
            raise Exception("Duplicate cid: %s" % str(mod.cid))
        cids.add(str(mod.cid))

    cid_phf = PerfectHash(modules,
                          PHF_SIZE,
                          key=lambda module: module.cid.bytes)

    contract_phf = PerfectHash(contracts,
                               PHF_SIZE,
                               key=lambda entry: entry.contract)

    js_services_phf = PerfectHash(list(js_services.values()),
                                  PHF_SIZE,
                                  key=lambda entry: entry.js_name)

    js_services_json = {}
    for entry in js_services.values():
        for iface in entry.interfaces:
            js_services_json[iface] = entry.js_name

    substs = {}

    gen_categories(substs, categories)

    substs["module_ids"] = "".join("  %s,\n" % entry.name
                                   for entry in cid_phf.entries)

    substs["module_count"] = len(modules)
    substs["contract_count"] = len(contracts)

    gen_module_funcs(substs, module_funcs)

    gen_includes(substs, headers)

    substs["component_jsms"] = ("\n".join(" %s," % strings.entry_to_cxx(jsm)
                                          for jsm in sorted(jsms)) + "\n")

    substs["interfaces"] = gen_interfaces(interfaces)

    substs["decls"] = gen_decls(types)

    substs["constructors"] = gen_constructors(cid_phf.entries)

    substs["component_getters"] = gen_getters(cid_phf.entries)

    substs["module_cid_table"] = cid_phf.cxx_codegen(
        name="ModuleByCID",
        entry_type="StaticModule",
        entries_name="gStaticModules",
        lower_entry=lambda entry: entry.to_cxx(),
        return_type="const StaticModule*",
        return_entry=("return entry.CID().Equals(aKey) && entry.Active()"
                      " ? &entry : nullptr;"),
        key_type="const nsID&",
        key_bytes="reinterpret_cast<const char*>(&aKey)",
        key_length="sizeof(nsID)",
    )

    substs["module_contract_id_table"] = contract_phf.cxx_codegen(
        name="LookupContractID",
        entry_type="ContractEntry",
        entries_name="gContractEntries",
        lower_entry=lambda entry: entry.to_cxx(),
        return_type="const ContractEntry*",
        return_entry="return entry.Matches(aKey) ? &entry : nullptr;",
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
    )

    substs["js_services_table"] = js_services_phf.cxx_codegen(
        name="LookupJSService",
        entry_type="JSServiceEntry",
        entries_name="gJSServices",
        lower_entry=lambda entry: entry.lower_js_service(),
        return_type="const JSServiceEntry*",
        return_entry="return entry.Name() == aKey ? &entry : nullptr;",
        key_type="const nsACString&",
        key_bytes="aKey.BeginReading()",
        key_length="aKey.Length()",
    )

    substs["js_services_json"] = json.dumps(js_services_json,
                                            sort_keys=True,
                                            indent=4)

    # Do this only after everything else has been emitted so we're sure the
    # string table is complete.
    substs["strings"] = strings.to_cxx()
    return substs