optimize ExtendedSelector::into_selector

when there is only one reference to an `ExtendedSelector`, the selector
will no longer do unnecessary cloning. this is a significant improvement
as previously we were cloning *every* selector multiple times. note that
this is optimization only occurs when the selector is being emitted.
This commit is contained in:
Connor Skees 2020-07-04 12:38:09 -04:00
parent ca861d488b
commit 52ecd1e2d0
3 changed files with 96 additions and 114 deletions

View File

@ -136,34 +136,33 @@ pub fn from_path(p: &str) -> Result<String> {
let mut map = CodeMap::new();
let file = map.add_file(p.into(), String::from_utf8(fs::read(p)?)?);
let empty_span = file.span.subspan(0, 0);
let mut extender = Extender::new(empty_span);
Css::from_stmts(
Parser {
toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>()
.into_iter()
.peekmore(),
map: &mut map,
path: p.as_ref(),
scopes: &mut NeverEmptyVec::new(Scope::new()),
global_scope: &mut Scope::new(),
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span,
content: &mut Vec::new(),
in_mixin: false,
in_function: false,
in_control_flow: false,
at_root: true,
at_root_has_selector: false,
extender: &mut extender,
}
.parse()
.map_err(|e| raw_to_parse_error(&map, *e))?,
&mut extender,
)
.map_err(|e| raw_to_parse_error(&map, *e))?
.pretty_print(&map, &mut extender)
.map_err(|e| raw_to_parse_error(&map, *e))
let stmts = Parser {
toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>()
.into_iter()
.peekmore(),
map: &mut map,
path: p.as_ref(),
scopes: &mut NeverEmptyVec::new(Scope::new()),
global_scope: &mut Scope::new(),
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span,
content: &mut Vec::new(),
in_mixin: false,
in_function: false,
in_control_flow: false,
at_root: true,
at_root_has_selector: false,
extender: &mut Extender::new(empty_span),
}
.parse()
.map_err(|e| raw_to_parse_error(&map, *e))?;
Css::from_stmts(stmts)
.map_err(|e| raw_to_parse_error(&map, *e))?
.pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, *e))
}
/// Write CSS to `buf`, constructed from a string
@ -182,34 +181,32 @@ pub fn from_string(p: String) -> Result<String> {
let mut map = CodeMap::new();
let file = map.add_file("stdin".into(), p);
let empty_span = file.span.subspan(0, 0);
let mut extender = Extender::new(empty_span);
Css::from_stmts(
Parser {
toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>()
.into_iter()
.peekmore(),
map: &mut map,
path: Path::new(""),
scopes: &mut NeverEmptyVec::new(Scope::new()),
global_scope: &mut Scope::new(),
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span,
content: &mut Vec::new(),
in_mixin: false,
in_function: false,
in_control_flow: false,
at_root: true,
at_root_has_selector: false,
extender: &mut extender,
}
.parse()
.map_err(|e| raw_to_parse_error(&map, *e))?,
&mut extender,
)
.map_err(|e| raw_to_parse_error(&map, *e))?
.pretty_print(&map, &mut extender)
.map_err(|e| raw_to_parse_error(&map, *e))
let stmts = Parser {
toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>()
.into_iter()
.peekmore(),
map: &mut map,
path: Path::new(""),
scopes: &mut NeverEmptyVec::new(Scope::new()),
global_scope: &mut Scope::new(),
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span,
content: &mut Vec::new(),
in_mixin: false,
in_function: false,
in_control_flow: false,
at_root: true,
at_root_has_selector: false,
extender: &mut Extender::new(empty_span),
}
.parse()
.map_err(|e| raw_to_parse_error(&map, *e))?;
Css::from_stmts(stmts)
.map_err(|e| raw_to_parse_error(&map, *e))?
.pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, *e))
}
#[cfg(feature = "wasm")]
@ -218,32 +215,31 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
let mut map = CodeMap::new();
let file = map.add_file("stdin".into(), p);
let empty_span = file.span.subspan(0, 0);
let mut extender = Extender::new(empty_span);
Ok(Css::from_stmts(
Parser {
toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>()
.into_iter()
.peekmore(),
map: &mut map,
path: Path::new(""),
scopes: &mut NeverEmptyVec::new(Scope::new()),
global_scope: &mut Scope::new(),
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span,
content: &mut Vec::new(),
in_mixin: false,
in_function: false,
in_control_flow: false,
at_root: true,
at_root_has_selector: false,
extender: &mut extender,
}
.parse()
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?,
&mut extender,
)
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?
.pretty_print(&map, &mut extender)
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?)
let stmts = Parser {
toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>()
.into_iter()
.peekmore(),
map: &mut map,
path: Path::new(""),
scopes: &mut NeverEmptyVec::new(Scope::new()),
global_scope: &mut Scope::new(),
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
span_before: empty_span,
content: &mut Vec::new(),
in_mixin: false,
in_function: false,
in_control_flow: false,
at_root: true,
at_root_has_selector: false,
extender: &mut Extender::new(empty_span),
}
.parse()
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?;
Ok(Css::from_stmts(stmts)
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?
.pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?)
}

View File

@ -7,7 +7,6 @@ use crate::{
atrule::{media::MediaRule, SupportsRule, UnknownAtRule},
error::SassResult,
parse::Stmt,
selector::Extender,
selector::Selector,
style::Style,
};
@ -76,11 +75,11 @@ impl Css {
Css { blocks: Vec::new() }
}
pub(crate) fn from_stmts(s: Vec<Stmt>, extender: &mut Extender) -> SassResult<Self> {
Css::new().parse_stylesheet(s, extender)
pub(crate) fn from_stmts(s: Vec<Stmt>) -> SassResult<Self> {
Css::new().parse_stylesheet(s)
}
fn parse_stmt(&mut self, stmt: Stmt, extender: &mut Extender) -> SassResult<Vec<Toplevel>> {
fn parse_stmt(&mut self, stmt: Stmt) -> SassResult<Vec<Toplevel>> {
Ok(match stmt {
Stmt::RuleSet { selector, body } => {
if body.is_empty() {
@ -93,7 +92,7 @@ impl Css {
let mut vals = vec![Toplevel::new_rule(selector)];
for rule in body {
match rule {
Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule, extender)?),
Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule)?),
Stmt::Style(s) => vals.get_mut(0).unwrap().push_style(s),
Stmt::Comment(s) => vals.get_mut(0).unwrap().push_comment(s),
Stmt::Media(m) => {
@ -117,7 +116,7 @@ impl Css {
Stmt::Return(..) => unreachable!(),
Stmt::AtRoot { body } => {
body.into_iter().try_for_each(|r| -> SassResult<()> {
vals.append(&mut self.parse_stmt(r, extender)?);
vals.append(&mut self.parse_stmt(r)?);
Ok(())
})?
}
@ -150,10 +149,10 @@ impl Css {
})
}
fn parse_stylesheet(mut self, stmts: Vec<Stmt>, extender: &mut Extender) -> SassResult<Css> {
fn parse_stylesheet(mut self, stmts: Vec<Stmt>) -> SassResult<Css> {
let mut is_first = true;
for stmt in stmts {
let v = self.parse_stmt(stmt, extender)?;
let v = self.parse_stmt(stmt)?;
// this is how we print newlines between unrelated styles
// it could probably be refactored
if !v.is_empty() {
@ -169,9 +168,9 @@ impl Css {
Ok(self)
}
pub fn pretty_print(self, map: &CodeMap, extender: &mut Extender) -> SassResult<String> {
pub fn pretty_print(self, map: &CodeMap) -> SassResult<String> {
let mut string = Vec::new();
self._inner_pretty_print(&mut string, map, extender, 0)?;
self._inner_pretty_print(&mut string, map, 0)?;
if string.iter().any(|s| !s.is_ascii()) {
return Ok(format!("@charset \"UTF-8\";\n{}", unsafe {
String::from_utf8_unchecked(string)
@ -184,7 +183,6 @@ impl Css {
self,
buf: &mut Vec<u8>,
map: &CodeMap,
extender: &mut Extender,
nesting: usize,
) -> SassResult<()> {
let mut has_written = false;
@ -231,12 +229,7 @@ impl Css {
writeln!(buf, " {{")?;
}
Css::from_stmts(body, extender)?._inner_pretty_print(
buf,
map,
extender,
nesting + 1,
)?;
Css::from_stmts(body)?._inner_pretty_print(buf, map, nesting + 1)?;
writeln!(buf, "{}}}", padding)?;
}
Toplevel::Supports { params, body } => {
@ -258,12 +251,7 @@ impl Css {
writeln!(buf, " {{")?;
}
Css::from_stmts(body, extender)?._inner_pretty_print(
buf,
map,
extender,
nesting + 1,
)?;
Css::from_stmts(body)?._inner_pretty_print(buf, map, nesting + 1)?;
writeln!(buf, "{}}}", padding)?;
}
Toplevel::Media { query, body } => {
@ -275,12 +263,7 @@ impl Css {
writeln!(buf)?;
}
writeln!(buf, "{}@media {} {{", padding, query)?;
Css::from_stmts(body, extender)?._inner_pretty_print(
buf,
map,
extender,
nesting + 1,
)?;
Css::from_stmts(body)?._inner_pretty_print(buf, map, nesting + 1)?;
writeln!(buf, "{}}}", padding)?;
}
Toplevel::Style(s) => {

View File

@ -37,7 +37,10 @@ impl ExtendedSelector {
}
pub fn into_selector(self) -> Selector {
Selector(self.0.borrow().clone())
Selector(match Rc::try_unwrap(self.0) {
Ok(v) => v.into_inner(),
Err(v) => v.borrow().clone(),
})
}
pub fn set_inner(&mut self, selector: SelectorList) {