#!/usr/bin/env python # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import re import os def gnu_symbolify(source, ident): return "_ZN" + str(len(source.CLASS)) + source.CLASS + str(len(ident)) + ident + "E" def msvc64_symbolify(source, ident): return "?" + ident + "@" + source.CLASS + "@@2PEAV" + source.TYPE + "@@EA" def msvc32_symbolify(source, ident): # Prepend "\x01" to avoid LLVM prefixing the mangled name with "_". # See https://github.com/rust-lang/rust/issues/36097 return "\\x01?" + ident + "@" + source.CLASS + "@@2PAV" + source.TYPE + "@@A" class GkAtomSource: PATTERN = re.compile('^GK_ATOM\((.+),\s*"(.*)"\)') FILE = "dist/include/nsGkAtomList.h" CLASS = "nsGkAtoms" TYPE = "nsIAtom" class CSSPseudoElementsAtomSource: PATTERN = re.compile('^CSS_PSEUDO_ELEMENT\((.+),\s*"(.*)",') FILE = "dist/include/nsCSSPseudoElementList.h" CLASS = "nsCSSPseudoElements" # NB: nsICSSPseudoElement is effectively the same as a nsIAtom, but we need # this for MSVC name mangling. TYPE = "nsICSSPseudoElement" class CSSAnonBoxesAtomSource: PATTERN = re.compile('^CSS_ANON_BOX\((.+),\s*"(.*)"\)') FILE = "dist/include/nsCSSAnonBoxList.h" CLASS = "nsCSSAnonBoxes" TYPE = "nsICSSAnonBoxPseudo" SOURCES = [ GkAtomSource, CSSPseudoElementsAtomSource, CSSAnonBoxesAtomSource, ] def map_atom(ident): if ident in {"box", "loop", "match", "mod", "ref", "self", "type", "use", "where", "in"}: return ident + "_" return ident class Atom: def __init__(self, source, ident, value): self.ident = "{}_{}".format(source.CLASS, ident) self._original_ident = ident self.value = value self.source = source def cpp_class(self): return self.source.CLASS def gnu_symbol(self): return gnu_symbolify(self.source, self._original_ident) def msvc32_symbol(self): return msvc32_symbolify(self.source, self._original_ident) def msvc64_symbol(self): return msvc64_symbolify(self.source, self._original_ident) def type(self): return self.source.TYPE def collect_atoms(objdir): atoms = [] for source in SOURCES: with open(os.path.join(objdir, source.FILE)) as f: for line in f.readlines(): result = re.match(source.PATTERN, line) if result: atoms.append(Atom(source, result.group(1), result.group(2))) return atoms PRELUDE = """ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Autogenerated file, DO NOT EDIT DIRECTLY */ """[1:] def write_atom_macro(atoms, file_name): ATOM_TEMPLATE = """ #[link_name = "{link_name}"] pub static {name}: *mut {type}; """[1:] def write_items(f, func): f.write(" extern {\n") for atom in atoms: f.write(ATOM_TEMPLATE.format(name=atom.ident, link_name=func(atom), type=atom.type())) f.write(" }\n") with open(file_name, "wb") as f: f.write(PRELUDE) f.write("use gecko_bindings::structs::nsIAtom;\n\n") f.write("use Atom;\n\n") for source in SOURCES: if source.TYPE != "nsIAtom": f.write("pub enum {} {{}}\n\n".format(source.TYPE)) f.write(""" #[inline(always)] pub fn unsafe_atom_from_static(ptr: *mut nsIAtom) -> Atom { unsafe { Atom::from_static(ptr) } }\n\n """) f.write("cfg_if! {\n") f.write(" if #[cfg(not(target_env = \"msvc\"))] {\n") write_items(f, Atom.gnu_symbol) f.write(" } else if #[cfg(target_pointer_width = \"64\")] {\n") write_items(f, Atom.msvc64_symbol) f.write(" } else {\n") write_items(f, Atom.msvc32_symbol) f.write(" }\n") f.write("}\n\n") f.write("#[macro_export]\n") f.write("macro_rules! atom {\n") f.writelines(['("%s") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::%s as *mut _) };\n' % (atom.value, atom.ident) for atom in atoms]) f.write("}\n") PSEUDO_ELEMENT_HEADER = """ /* * This file contains a helper macro invocation to aid Gecko's style system * pseudo-element integration. * * This file is NOT INTENDED to be compiled as a standalone module. * * Also, it guarantees the property that normal pseudo-elements are processed * before anonymous boxes. * * Expected usage is as follows: * * ``` * fn have_to_use_pseudo_elements() { * macro_rules pseudo_element! { * ($pseudo_str_with_colon:expr, $pseudo_atom:expr, $is_anon_box:true) => {{ * // Stuff stuff stuff. * }} * } * include!("path/to/helper.rs") * } * ``` * */ """ PSEUDO_ELEMENT_MACRO_INVOCATION = """ pseudo_element!(\"{}\", atom!(\"{}\"), {}); """[1:] def write_pseudo_element_helper(atoms, target_filename): with open(target_filename, "wb") as f: f.write(PRELUDE) f.write(PSEUDO_ELEMENT_HEADER) f.write("{\n") for atom in atoms: if atom.type() == "nsICSSPseudoElement": f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "false")) elif atom.type() == "nsICSSAnonBoxPseudo": f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "true")) f.write("}\n") def build(objdir, verbose=False): atoms = collect_atoms(objdir) write_atom_macro(atoms, "../string_cache/atom_macro.rs") write_pseudo_element_helper(atoms, "../../../components/style/generated/gecko_pseudo_element_helper.rs") return 0