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)
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")
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")
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")
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 """)
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
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 """)
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