From 12fcb7a2e7acd802a4868caf6a1fb5fbf94e3ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 9 Aug 2016 21:30:17 -0700 Subject: [PATCH] stylo: Allow regenerating atoms as part of the normal generation of bindings. This configures the regeneration of atoms as part of the normal generation of bindings, so it stops being a whole different process. This also adds a generated file to components/style/generated with a convenience macro invocation for pseudo-elements, which comes handy in order to avoid duplication. --- ports/geckolib/binding_tools/regen.py | 40 +++- ports/geckolib/binding_tools/regen_atoms.py | 194 ++++++++++++++++++ .../geckolib/string_cache/regen_atom_macro.py | 71 ------- 3 files changed, 226 insertions(+), 79 deletions(-) create mode 100755 ports/geckolib/binding_tools/regen_atoms.py delete mode 100755 ports/geckolib/string_cache/regen_atom_macro.py diff --git a/ports/geckolib/binding_tools/regen.py b/ports/geckolib/binding_tools/regen.py index e2998f75223..aa6192dbc27 100755 --- a/ports/geckolib/binding_tools/regen.py +++ b/ports/geckolib/binding_tools/regen.py @@ -13,6 +13,8 @@ import copy import subprocess import tempfile +import regen_atoms + DESCRIPTION = 'Regenerate the rust version of the structs or the bindings file.' TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) COMMON_BUILD_KEY = "__common__" @@ -38,6 +40,7 @@ COMPILATION_TARGETS = { }, # Generation of style structs. "structs": { + "target_dir": "../gecko_bindings", "test": True, "flags": [ "-ignore-functions", @@ -108,6 +111,7 @@ COMPILATION_TARGETS = { }, # Generation of the ffi bindings. "bindings": { + "target_dir": "../gecko_bindings", "raw_lines": [ "use heapsize::HeapSizeOf;", ], @@ -140,6 +144,10 @@ COMPILATION_TARGETS = { "void_types": [ "nsINode", "nsIDocument", "nsIPrincipal", "nsIURI", ], + }, + + "atoms": { + "custom_build": regen_atoms.build, } } @@ -212,6 +220,17 @@ def build(objdir, target_name, kind_name=None, assert ((kind_name is None and "build_kinds" not in current_target) or (kind_name in current_target["build_kinds"])) + if "custom_build" in current_target: + print("[CUSTOM] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='') + sys.stdout.flush() + ret = current_target["custom_build"](objdir, verbose=True) + if ret != 0: + print("FAIL") + else: + print("OK") + + return ret + if bindgen is None: bindgen = os.path.join(TOOLS_DIR, "rust-bindgen") @@ -221,18 +240,23 @@ def build(objdir, target_name, kind_name=None, else: bindgen = [bindgen] - if output_filename is None: - filename = "{}.rs".format(target_name) - - if kind_name is not None: - filename = "{}_{}.rs".format(target_name, kind_name) - - output_filename = "{}/../{}".format(TOOLS_DIR, filename) - if kind_name is not None: current_target = copy.deepcopy(current_target) extend_object(current_target, current_target["build_kinds"][kind_name]) + target_dir = None + if output_filename is None and "target_dir" in current_target: + target_dir = current_target["target_dir"] + + if output_filename is None: + output_filename = "{}.rs".format(target_name) + + if kind_name is not None: + output_filename = "{}_{}.rs".format(target_name, kind_name) + + if target_dir: + output_filename = "{}/{}".format(target_dir, output_filename) + print("[BINDGEN] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='') sys.stdout.flush() diff --git a/ports/geckolib/binding_tools/regen_atoms.py b/ports/geckolib/binding_tools/regen_atoms.py new file mode 100755 index 00000000000..23b55b009da --- /dev/null +++ b/ports/geckolib/binding_tools/regen_atoms.py @@ -0,0 +1,194 @@ +#!/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): + return "?" + 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 diff --git a/ports/geckolib/string_cache/regen_atom_macro.py b/ports/geckolib/string_cache/regen_atom_macro.py deleted file mode 100755 index fad65b58901..00000000000 --- a/ports/geckolib/string_cache/regen_atom_macro.py +++ /dev/null @@ -1,71 +0,0 @@ -# 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 sys - -if len(sys.argv) != 2: - print "usage: ./%s PATH/TO/OBJDIR" % sys.argv[0] -objdir_path = sys.argv[1] - - -def line_to_atom(line): - result = re.match('^GK_ATOM\((.+),\s*"(.*)"\)', line) - return (result.group(1), result.group(2)) - - -def map_atom(ident): - if ident in {"box", "loop", "match", "mod", "ref", - "self", "type", "use", "where", "in"}: - return ident + "_" - return ident - - -def gnu_symbolify(ident): - return "_ZN9nsGkAtoms" + str(len(ident)) + ident + "E" - - -def msvc64_symbolify(ident): - return "?" + ident + "@nsGkAtoms@@2PEAVnsIAtom@@EA" - - -def msvc32_symbolify(ident): - return "?" + ident + "@nsGkAtoms@@2PAVnsIAtom@@A" - - -def write_items(f, func): - f.write(" extern {\n") - for atom in atoms: - f.write(TEMPLATE.format(name=map_atom(atom[0]), - link_name=func(atom[0]))) - f.write(" }\n") - - -with open(objdir_path + "/dist/include/nsGkAtomList.h") as f: - lines = [line for line in f.readlines() if line.startswith("GK_ATOM")] - atoms = [line_to_atom(line) for line in lines] - -TEMPLATE = """ - #[link_name = "{link_name}"] - pub static {name}: *mut nsIAtom; -"""[1:] - -with open("atom_macro.rs", "wb") as f: - f.write("use gecko_bindings::structs::nsIAtom;\n\n") - f.write("use Atom;\n\n") - f.write("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, gnu_symbolify) - f.write(" } else if #[cfg(target_pointer_width = \"64\")] {\n") - write_items(f, msvc64_symbolify) - f.write(" } else {\n") - write_items(f, msvc32_symbolify) - 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) };\n' - % (atom[1], map_atom(atom[0])) for atom in atoms]) - f.write("}\n")