Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stylo: Use atoms as the pseudo-element back-end. #12815

Merged
merged 12 commits into from Aug 16, 2016

stylo: Allow regenerating atoms as part of the normal generation of b…

…indings.

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.
  • Loading branch information
emilio committed Aug 16, 2016
commit 12fcb7a2e7acd802a4868caf6a1fb5fbf94e3ccf
@@ -13,6 +13,8 @@
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 @@
},
# Generation of style structs.
"structs": {
"target_dir": "../gecko_bindings",
"test": True,
"flags": [
"-ignore-functions",
@@ -108,6 +111,7 @@
},
# Generation of the ffi bindings.
"bindings": {
"target_dir": "../gecko_bindings",
"raw_lines": [
"use heapsize::HeapSizeOf;",
],
@@ -140,6 +144,10 @@
"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,17 +240,22 @@ def build(objdir, target_name, kind_name=None,
else:
bindgen = [bindgen]

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:
filename = "{}.rs".format(target_name)
output_filename = "{}.rs".format(target_name)

if kind_name is not None:
filename = "{}_{}.rs".format(target_name, kind_name)
output_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])
if target_dir:
output_filename = "{}/{}".format(target_dir, output_filename)

print("[BINDGEN] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='')
sys.stdout.flush()
@@ -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"

This comment has been minimized.

@emilio

emilio Aug 16, 2016

Author Member

cc @upsuper, hopefully I got the msvc manglings fine.

This comment has been minimized.

@upsuper

upsuper Aug 17, 2016

Member

TBH, I have no idea about msvc manglings. Their mangling rule looks complicated, so I disassembled the object file and copied the pattern to here directly.

If the type is just pointers, I guess reusing this is probably fine, but I'm unconfident about that anyway.



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.

This comment has been minimized.

@bholley

bholley Aug 13, 2016

Contributor

Nit: put this above the 'expected usage' part.

*
* 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

This file was deleted.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.