Bug 1425700 - Add a very simple use counter implementation. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 16 Aug 2018 16:38:56 +0200
changeset 488734 8efef28be72c1ab7e433248924b9459beb2e1b33
parent 488733 4c623a756c06017d341d956ce5a8633baf3f7fc8
child 488735 5449572186472bfc8ed0a48e6b08e6bc4e2424a6
push id9734
push usershindli@mozilla.com
push dateThu, 30 Aug 2018 12:18:07 +0000
treeherdermozilla-beta@71c71ab3afae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1425700
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1425700 - Add a very simple use counter implementation. r=heycam As simple as I could make it, for now. We can improve on this. Differential Revision: https://phabricator.services.mozilla.com/D3827
servo/components/style/lib.rs
servo/components/style/parser.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/use_counters/mod.rs
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -152,16 +152,17 @@ pub mod style_adjuster;
 pub mod style_resolver;
 pub mod stylesheet_set;
 pub mod stylesheets;
 pub mod stylist;
 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;
 
 #[cfg(feature = "gecko")]
 pub use gecko_string_cache as string_cache;
 #[cfg(feature = "gecko")]
 pub use gecko_string_cache::Atom;
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -4,16 +4,17 @@
 
 //! The context within which CSS code is parsed.
 
 use context::QuirksMode;
 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")]
 #[inline]
 pub fn assert_parsing_mode_match() {
     use gecko_bindings::structs;
 
     macro_rules! check_parsing_modes {
@@ -48,37 +49,40 @@ pub struct ParserContext<'a> {
     /// The mode to use when parsing.
     pub parsing_mode: ParsingMode,
     /// The quirks mode of this stylesheet.
     pub quirks_mode: QuirksMode,
     /// The active error reporter, or none if error reporting is disabled.
     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> {
     /// Create a parser context.
     #[inline]
     pub fn new(
         stylesheet_origin: Origin,
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
         error_reporter: Option<&'a ParseErrorReporter>,
     ) -> Self {
-        ParserContext {
+        Self {
             stylesheet_origin,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
             error_reporter,
             namespaces: None,
+            use_counters: None,
         }
     }
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     #[inline]
     pub fn new_for_cssom(
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
@@ -91,31 +95,33 @@ impl<'a> ParserContext<'a> {
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
             error_reporter,
         )
     }
 
-    /// 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),
             parsing_mode: context.parsing_mode,
             quirks_mode: context.quirks_mode,
             namespaces: Some(namespaces),
             error_reporter: context.error_reporter,
+            use_counters: context.use_counters,
         }
     }
 
     /// Whether we're in a @page rule.
     #[inline]
     pub fn in_page_rule(&self) -> bool {
         self.rule_type
             .map_or(false, |rule_type| rule_type == CssRuleType::Page)
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -420,52 +420,61 @@ impl PropertyDeclaration {
 pub mod animated_properties {
     <%include file="/helpers/animated_properties.mako.rs" />
 }
 
 /// A longhand or shorthand property.
 #[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() {
     % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
     ::std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
     % endfor
 }
 % 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 {
         // unsafe: guaranteed by static_assert_nscsspropertyid above.
         unsafe { ::std::mem::transmute(self.0 as i32) }
     }
 
     /// Convert an `nsCSSPropertyID` into a `NonCustomPropertyId`.
     #[cfg(feature = "gecko")]
     #[inline]
     pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
         let prop = prop as i32;
         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.
         Ok(unsafe { ::std::mem::transmute(prop as usize) })
     }
 
     /// 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
         ];
         MAP[self.0]
     }
 
     #[inline]
@@ -630,17 +639,17 @@ impl NonCustomPropertyId {
                 PropertyId::Longhand(transmute(self.0 as u16))
             }
         }
         if self.0 < ${len(data.longhands) + len(data.shorthands)} {
             return unsafe {
                 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)
         };
 
         match alias_id.aliased_property() {
             AliasedPropertyId::Longhand(longhand) => PropertyId::LonghandAlias(longhand, alias_id),
             AliasedPropertyId::Shorthand(shorthand) => PropertyId::ShorthandAlias(shorthand, alias_id),
         }
@@ -666,17 +675,17 @@ impl From<AliasId> for NonCustomProperty
     fn from(id: AliasId) -> Self {
         NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)})
     }
 }
 
 /// 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 {
     /// Creates an empty `NonCustomPropertyIdSet`.
     pub fn new() -> Self {
         Self {
             storage: Default::default(),
         }
@@ -2193,36 +2202,36 @@ impl PropertyDeclaration {
 
         let non_custom_id = id.non_custom_id();
         let start = input.state();
         match id {
             PropertyId::Custom(property_name) => {
                 // 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),
                         Err(e) => return Err(StyleParseErrorKind::new_invalid(
                             format!("--{}", property_name),
                             e,
                         )),
                     }
                 };
                 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
                     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 },
                     )
                 }).or_else(|()| {
                     input.look_for_var_functions();
                     input.parse_entirely(|input| id.parse_value(context, input))
                     .or_else(|err| {
                         while let Ok(_) = input.next() {}  // Look for var() after the error.
@@ -2248,79 +2257,83 @@ impl PropertyDeclaration {
                             Err(StyleParseErrorKind::new_invalid(
                                 non_custom_id.unwrap().name(),
                                 err,
                             ))
                         }
                     })
                 }).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 {
                         for longhand in id.longhands() {
                             declarations.push(PropertyDeclaration::CSSWideKeyword(
                                 WideKeywordDeclaration {
                                     id: longhand,
                                     keyword,
                                 },
                             ))
                         }
                     }
-                    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(())
     }
 }
 
 type SubpropertiesArray<T> =
     [T; ${max(len(s.sub_properties) for s in data.shorthands_except_all())}];
 
 type SubpropertiesVec<T> = ArrayVec<SubpropertiesArray<T>>;
 
new file mode 100644
--- /dev/null
+++ b/servo/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,
+}