From c8e5b7f1b070374999ba05b8365b105251348dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 16 Aug 2018 16:38:56 +0200 Subject: [PATCH] style: Add a very simple use counter implementation. As simple as I could make it, for now. We can improve on this. Differential Revision: https://phabricator.services.mozilla.com/D3827 --- components/style/lib.rs | 1 + components/style/parser.rs | 12 ++- .../style/properties/properties.mako.rs | 97 +++++++++++-------- components/style/use_counters/mod.rs | 40 ++++++++ 4 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 components/style/use_counters/mod.rs diff --git a/components/style/lib.rs b/components/style/lib.rs index 1c3630b0986..03caab33e19 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -157,6 +157,7 @@ pub mod thread_state; pub mod timer; pub mod traversal; pub mod traversal_flags; +pub mod use_counters; #[macro_use] #[allow(non_camel_case_types)] pub mod values; diff --git a/components/style/parser.rs b/components/style/parser.rs index a4b7d816203..006d2ce6b88 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -9,6 +9,7 @@ use cssparser::{Parser, SourceLocation, UnicodeRange}; use error_reporting::{ContextualParseError, ParseErrorReporter}; use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator}; use stylesheets::{CssRuleType, Namespaces, Origin, UrlExtraData}; +use use_counters::UseCounters; /// Asserts that all ParsingMode flags have a matching ParsingMode value in gecko. #[cfg(feature = "gecko")] @@ -53,6 +54,8 @@ pub struct ParserContext<'a> { error_reporter: Option<&'a ParseErrorReporter>, /// The currently active namespaces. pub namespaces: Option<&'a Namespaces>, + /// The use counters we want to record while parsing style rules, if any. + pub use_counters: Option<&'a UseCounters>, } impl<'a> ParserContext<'a> { @@ -66,7 +69,7 @@ impl<'a> ParserContext<'a> { quirks_mode: QuirksMode, error_reporter: Option<&'a ParseErrorReporter>, ) -> Self { - ParserContext { + Self { stylesheet_origin, url_data, rule_type, @@ -74,6 +77,7 @@ impl<'a> ParserContext<'a> { quirks_mode, error_reporter, namespaces: None, + use_counters: None, } } @@ -96,14 +100,15 @@ impl<'a> ParserContext<'a> { ) } - /// Create a parser context based on a previous context, but with a modified rule type. + /// Create a parser context based on a previous context, but with a modified + /// rule type. #[inline] pub fn new_with_rule_type( context: &'a ParserContext, rule_type: CssRuleType, namespaces: &'a Namespaces, ) -> ParserContext<'a> { - ParserContext { + Self { stylesheet_origin: context.stylesheet_origin, url_data: context.url_data, rule_type: Some(rule_type), @@ -111,6 +116,7 @@ impl<'a> ParserContext<'a> { quirks_mode: context.quirks_mode, namespaces: Some(namespaces), error_reporter: context.error_reporter, + use_counters: context.use_counters, } } diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 64e99ba205d..eba290fc14d 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -425,6 +425,10 @@ pub mod animated_properties { #[derive(Clone, Copy, Debug)] pub struct NonCustomPropertyId(usize); +/// The length of all the non-custom properties. +pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize = + ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}; + % if product == "gecko": #[allow(dead_code)] unsafe fn static_assert_nscsspropertyid() { @@ -435,6 +439,11 @@ unsafe fn static_assert_nscsspropertyid() { % endif impl NonCustomPropertyId { + /// Returns the underlying index, used for use counter. + pub fn bit(self) -> usize { + self.0 + } + #[cfg(feature = "gecko")] #[inline] fn to_nscsspropertyid(self) -> nsCSSPropertyID { @@ -450,7 +459,7 @@ impl NonCustomPropertyId { if prop < 0 { return Err(()); } - if prop >= ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} { + if prop >= NON_CUSTOM_PROPERTY_ID_COUNT as i32 { return Err(()); } // unsafe: guaranteed by static_assert_nscsspropertyid above. @@ -460,7 +469,7 @@ impl NonCustomPropertyId { /// Get the property name. #[inline] pub fn name(self) -> &'static str { - static MAP: [&'static str; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [ + static MAP: [&'static str; NON_CUSTOM_PROPERTY_ID_COUNT] = [ % for property in data.longhands + data.shorthands + data.all_aliases(): "${property.name}", % endfor @@ -635,7 +644,7 @@ impl NonCustomPropertyId { PropertyId::Shorthand(transmute((self.0 - ${len(data.longhands)}) as u16)) } } - assert!(self.0 < ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}); + assert!(self.0 < NON_CUSTOM_PROPERTY_ID_COUNT); let alias_id: AliasId = unsafe { transmute((self.0 - ${len(data.longhands) + len(data.shorthands)}) as u16) }; @@ -671,7 +680,7 @@ impl From for NonCustomPropertyId { /// A set of all properties #[derive(Clone, PartialEq)] pub struct NonCustomPropertyIdSet { - storage: [u32; (${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} - 1 + 32) / 32] + storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32] } impl NonCustomPropertyIdSet { @@ -2198,7 +2207,7 @@ impl PropertyDeclaration { // FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774 // before adding skip_whitespace here. // This probably affects some test results. - let value = match input.try(|i| CSSWideKeyword::parse(i)) { + let value = match input.try(CSSWideKeyword::parse) { Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword), Err(()) => match ::custom_properties::SpecifiedValue::parse(input) { Ok(value) => DeclaredValueOwned::Value(value), @@ -2212,12 +2221,12 @@ impl PropertyDeclaration { name: property_name, value, })); - Ok(()) + return Ok(()); } PropertyId::LonghandAlias(id, _) | PropertyId::Longhand(id) => { input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. - input.try(|i| CSSWideKeyword::parse(i)).map(|keyword| { + input.try(CSSWideKeyword::parse).map(|keyword| { PropertyDeclaration::CSSWideKeyword( WideKeywordDeclaration { id, keyword }, ) @@ -2253,12 +2262,12 @@ impl PropertyDeclaration { }) }).map(|declaration| { declarations.push(declaration) - }) + })?; } PropertyId::ShorthandAlias(id, _) | PropertyId::Shorthand(id) => { input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. - if let Ok(keyword) = input.try(|i| CSSWideKeyword::parse(i)) { + if let Ok(keyword) = input.try(CSSWideKeyword::parse) { if id == ShorthandId::All { declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword) } else { @@ -2271,51 +2280,55 @@ impl PropertyDeclaration { )) } } - Ok(()) } else { input.look_for_var_functions(); // Not using parse_entirely here: each ${shorthand.ident}::parse_into function // needs to do so *before* pushing to `declarations`. id.parse_into(declarations, context, input).or_else(|err| { while let Ok(_) = input.next() {} // Look for var() after the error. - if input.seen_var_functions() { - input.reset(&start); - let (first_token_type, css) = - ::custom_properties::parse_non_custom_with_var(input).map_err(|e| { - StyleParseErrorKind::new_invalid( - non_custom_id.unwrap().name(), - e, - ) - })?; - let unparsed = Arc::new(UnparsedValue { - css: css.into_owned(), - first_token_type: first_token_type, - url_data: context.url_data.clone(), - from_shorthand: Some(id), - }); - if id == ShorthandId::All { - declarations.all_shorthand = AllShorthand::WithVariables(unparsed) - } else { - for id in id.longhands() { - declarations.push( - PropertyDeclaration::WithVariables(VariableDeclaration { - id, - value: unparsed.clone(), - }) - ) - } - } - Ok(()) - } else { - Err(StyleParseErrorKind::new_invalid( + if !input.seen_var_functions() { + return Err(StyleParseErrorKind::new_invalid( non_custom_id.unwrap().name(), err, - )) + )); } - }) + + input.reset(&start); + let (first_token_type, css) = + ::custom_properties::parse_non_custom_with_var(input).map_err(|e| { + StyleParseErrorKind::new_invalid( + non_custom_id.unwrap().name(), + e, + ) + })?; + let unparsed = Arc::new(UnparsedValue { + css: css.into_owned(), + first_token_type: first_token_type, + url_data: context.url_data.clone(), + from_shorthand: Some(id), + }); + if id == ShorthandId::All { + declarations.all_shorthand = AllShorthand::WithVariables(unparsed) + } else { + for id in id.longhands() { + declarations.push( + PropertyDeclaration::WithVariables(VariableDeclaration { + id, + value: unparsed.clone(), + }) + ) + } + } + Ok(()) + })?; } } } + debug_assert!(non_custom_id.is_some(), "Custom properties should've returned earlier"); + if let Some(use_counters) = context.use_counters { + use_counters.non_custom_properties.record(non_custom_id.unwrap()); + } + Ok(()) } } diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs new file mode 100644 index 00000000000..02ae4ea1ab2 --- /dev/null +++ b/components/style/use_counters/mod.rs @@ -0,0 +1,40 @@ +/* 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/. */ + +//! Various stuff for CSS property use counters. + +use properties::{NonCustomPropertyId, NON_CUSTOM_PROPERTY_ID_COUNT}; +// FIXME(emilio): We need AtomicU32 on stable ASAP... +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +#[cfg(target_pointer_width = "64")] +const BITS_PER_ENTRY: usize = 64; + +#[cfg(target_pointer_width = "32")] +const BITS_PER_ENTRY: usize = 32; + +/// One bit per each non-custom CSS property. +#[derive(Default)] +pub struct NonCustomPropertyUseCounters { + storage: [AtomicUsize; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY], +} + +impl NonCustomPropertyUseCounters { + /// Record that a given non-custom property ID has been parsed. + #[inline] + pub fn record(&self, id: NonCustomPropertyId) { + let bit = id.bit(); + let bucket = bit / BITS_PER_ENTRY; + let bit_in_bucket = bit % BITS_PER_ENTRY; + self.storage[bucket].fetch_or(1 << bit_in_bucket, Ordering::Relaxed); + } +} + +/// The use-counter data related to a given document we want to store. +pub struct UseCounters { + /// The counters for non-custom properties that have been parsed in the + /// document's stylesheets. + pub non_custom_properties: NonCustomPropertyUseCounters, +}