#!/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 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 gnu_symbolify(source, ident): return "_ZN{}{}{}{}E".format(len(source.CLASS), source.CLASS, len(ident), ident) def msvc64_symbolify(source, ident): return "?{}@{}@@2PEAV{}@@EA".format(ident, source.CLASS, source.TYPE) 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?{}@{}@@2PAV{}@@A".format(ident, source.CLASS, source.TYPE) class GkAtomSource: PATTERN = re.compile('^GK_ATOM\((?P.+),\s*"(?P.*)"\)', re.M) FILE = "dist/include/nsGkAtomList.h" CLASS = "nsGkAtoms" TYPE = "nsIAtom" class CSSPseudoElementsAtomSource: PATTERN = re.compile('^CSS_PSEUDO_ELEMENT\((?P.+),\s*"(?P.*)",', re.M) 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\((?P.+),\s*"(?P.*)"\)', re.M) FILE = "dist/include/nsCSSAnonBoxList.h" CLASS = "nsCSSAnonBoxes" TYPE = "nsICSSAnonBoxPseudo" class CSSPropsAtomSource: PATTERN = re.compile('^CSS_PROP_[A-Z]+\(\s*(?P[^,]+),\s*(?P[^,]+)', re.M) FILE = "dist/include/nsCSSPropList.h" CLASS = "nsCSSProps" TYPE = "nsICSSProperty" SOURCES = [ GkAtomSource, CSSPseudoElementsAtomSource, CSSAnonBoxesAtomSource, CSSPropsAtomSource, ] 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: content = f.read() found = set() for match in source.PATTERN.finditer(content): ident = match.group('ident') if ident in found: continue found.add(ident) atoms.append(Atom(source, ident, match.group('value'))) return atoms IMPORTS = ("\nuse gecko_bindings::structs::nsIAtom;" "\nuse string_cache::Atom;\n\n") ATOM_TEMPLATE = (" #[link_name = \"{link_name}\"]\n" " pub static {name}: *mut {type};") UNSAFE_STATIC = ("#[inline(always)]\n" "pub unsafe fn atom_from_static(ptr: *mut nsIAtom) -> Atom {\n" " Atom::from_static(ptr)\n" "}\n\n") CFG_IF = ''' cfg_if! {{ if #[cfg(not(target_env = "msvc"))] {{ extern {{ {gnu} }} }} else if #[cfg(target_pointer_width = "64")] {{ extern {{ {msvc64} }} }} else {{ extern {{ {msvc32} }} }} }} ''' RULE_TEMPLATE = ('("{atom}") =>\n ' '{{ ' # FIXME(bholley): Uncomment this when rust 1.14 is released. # See the comment in components/style/lib.rs. # ' #[allow(unsafe_code)] #[allow(unused_unsafe)] ' 'unsafe {{ $crate::string_cache::atom_macro::atom_from_static' '($crate::string_cache::atom_macro::{name} as *mut _) }}' ' }};') MACRO = ''' #[macro_export] macro_rules! atom {{ {} }} ''' def write_atom_macro(atoms, file_name): def get_symbols(func): return '\n'.join([ATOM_TEMPLATE.format(name=atom.ident, link_name=func(atom), type=atom.type()) for atom in atoms]) with open(file_name, "wb") as f: f.write(PRELUDE) f.write(IMPORTS) for source in SOURCES: if source.TYPE != "nsIAtom": f.write("pub enum {} {{}}\n\n".format(source.TYPE)) f.write(UNSAFE_STATIC) gnu_symbols = get_symbols(Atom.gnu_symbol) msvc32_symbols = get_symbols(Atom.msvc32_symbol) msvc64_symbols = get_symbols(Atom.msvc64_symbol) f.write(CFG_IF.format(gnu=gnu_symbols, msvc32=msvc32_symbols, msvc64=msvc64_symbols)) macro_rules = [RULE_TEMPLATE.format(atom=atom.value, name=atom.ident) for atom in atoms] f.write(MACRO.format('\n'.join(macro_rules))) 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, "../gecko_string_cache/atom_macro.rs") write_pseudo_element_helper(atoms, "../gecko/generated/gecko_pseudo_element_helper.rs") return 0