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,9 +136,8 @@ pub fn from_path(p: &str) -> Result<String> {
let mut map = CodeMap::new(); let mut map = CodeMap::new();
let file = map.add_file(p.into(), String::from_utf8(fs::read(p)?)?); let file = map.add_file(p.into(), String::from_utf8(fs::read(p)?)?);
let empty_span = file.span.subspan(0, 0); let empty_span = file.span.subspan(0, 0);
let mut extender = Extender::new(empty_span);
Css::from_stmts( let stmts = Parser {
Parser {
toks: &mut Lexer::new(&file) toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>() .collect::<Vec<Token>>()
.into_iter() .into_iter()
@ -155,14 +154,14 @@ pub fn from_path(p: &str) -> Result<String> {
in_control_flow: false, in_control_flow: false,
at_root: true, at_root: true,
at_root_has_selector: false, at_root_has_selector: false,
extender: &mut extender, extender: &mut Extender::new(empty_span),
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e))?, .map_err(|e| raw_to_parse_error(&map, *e))?;
&mut extender,
) Css::from_stmts(stmts)
.map_err(|e| raw_to_parse_error(&map, *e))? .map_err(|e| raw_to_parse_error(&map, *e))?
.pretty_print(&map, &mut extender) .pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, *e)) .map_err(|e| raw_to_parse_error(&map, *e))
} }
@ -182,9 +181,7 @@ pub fn from_string(p: String) -> Result<String> {
let mut map = CodeMap::new(); let mut map = CodeMap::new();
let file = map.add_file("stdin".into(), p); let file = map.add_file("stdin".into(), p);
let empty_span = file.span.subspan(0, 0); let empty_span = file.span.subspan(0, 0);
let mut extender = Extender::new(empty_span); let stmts = Parser {
Css::from_stmts(
Parser {
toks: &mut Lexer::new(&file) toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>() .collect::<Vec<Token>>()
.into_iter() .into_iter()
@ -201,14 +198,14 @@ pub fn from_string(p: String) -> Result<String> {
in_control_flow: false, in_control_flow: false,
at_root: true, at_root: true,
at_root_has_selector: false, at_root_has_selector: false,
extender: &mut extender, extender: &mut Extender::new(empty_span),
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e))?, .map_err(|e| raw_to_parse_error(&map, *e))?;
&mut extender,
) Css::from_stmts(stmts)
.map_err(|e| raw_to_parse_error(&map, *e))? .map_err(|e| raw_to_parse_error(&map, *e))?
.pretty_print(&map, &mut extender) .pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, *e)) .map_err(|e| raw_to_parse_error(&map, *e))
} }
@ -218,9 +215,8 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
let mut map = CodeMap::new(); let mut map = CodeMap::new();
let file = map.add_file("stdin".into(), p); let file = map.add_file("stdin".into(), p);
let empty_span = file.span.subspan(0, 0); let empty_span = file.span.subspan(0, 0);
let mut extender = Extender::new(empty_span);
Ok(Css::from_stmts( let stmts = Parser {
Parser {
toks: &mut Lexer::new(&file) toks: &mut Lexer::new(&file)
.collect::<Vec<Token>>() .collect::<Vec<Token>>()
.into_iter() .into_iter()
@ -237,13 +233,13 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
in_control_flow: false, in_control_flow: false,
at_root: true, at_root: true,
at_root_has_selector: false, at_root_has_selector: false,
extender: &mut extender, extender: &mut Extender::new(empty_span),
} }
.parse() .parse()
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?, .map_err(|e| raw_to_parse_error(&map, *e).to_string())?;
&mut extender,
) Ok(Css::from_stmts(stmts)
.map_err(|e| raw_to_parse_error(&map, *e).to_string())? .map_err(|e| raw_to_parse_error(&map, *e).to_string())?
.pretty_print(&map, &mut extender) .pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?) .map_err(|e| raw_to_parse_error(&map, *e).to_string())?)
} }

View File

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

View File

@ -37,7 +37,10 @@ impl ExtendedSelector {
} }
pub fn into_selector(self) -> Selector { 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) { pub fn set_inner(&mut self, selector: SelectorList) {