Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import math | 6 import math |
| 7 import sys | 7 import sys |
| 8 | 8 |
| 9 import json5_generator | 9 import json5_generator |
| 10 import template_expander | 10 import template_expander |
| 11 import make_style_builder | 11 import make_style_builder |
| 12 | 12 |
| 13 from name_utilities import ( | 13 from name_utilities import ( |
| 14 enum_for_css_keyword, enum_value_name, class_member_name, method_name | 14 enum_for_css_keyword, enum_type_name, enum_value_name, class_member_name, me thod_name |
| 15 ) | 15 ) |
| 16 | 16 |
| 17 | 17 |
| 18 # Temporary hard-coded list of fields that are not CSS properties. | 18 # Temporary hard-coded list of fields that are not CSS properties. |
| 19 # Ideally these would be specified in a .json5 file. | 19 # TODO(shend): Put this into its own JSON5 file. |
| 20 NONPROPERTY_FIELDS = [ | 20 NONPROPERTIES = [ |
| 21 {'name': 'IsLink', 'field_template': 'monotonic_flag'}, | 21 {'name': 'IsLink', 'field_template': 'monotonic_flag', |
| 22 'inherited': False, 'independent': False}, | |
| 22 # Style can not be shared. | 23 # Style can not be shared. |
| 23 {'name': 'Unique', 'field_template': 'monotonic_flag'}, | 24 {'name': 'Unique', 'field_template': 'monotonic_flag', |
| 25 'inherited': False, 'independent': False}, | |
| 24 # Whether this style is affected by these pseudo-classes. | 26 # Whether this style is affected by these pseudo-classes. |
| 25 {'name': 'AffectedByFocus', 'field_template': 'monotonic_flag'}, | 27 {'name': 'AffectedByFocus', 'field_template': 'monotonic_flag', |
| 26 {'name': 'AffectedByHover', 'field_template': 'monotonic_flag'}, | 28 'inherited': False, 'independent': False}, |
| 27 {'name': 'AffectedByActive', 'field_template': 'monotonic_flag'}, | 29 {'name': 'AffectedByHover', 'field_template': 'monotonic_flag', |
| 28 {'name': 'AffectedByDrag', 'field_template': 'monotonic_flag'}, | 30 'inherited': False, 'independent': False}, |
| 31 {'name': 'AffectedByActive', 'field_template': 'monotonic_flag', | |
| 32 'inherited': False, 'independent': False}, | |
| 33 {'name': 'AffectedByDrag', 'field_template': 'monotonic_flag', | |
| 34 'inherited': False, 'independent': False}, | |
| 29 # A non-inherited property references a variable or @apply is used | 35 # A non-inherited property references a variable or @apply is used |
| 30 {'name': 'HasVariableReferenceFromNonInheritedProperty', 'field_template': ' monotonic_flag'}, | 36 {'name': 'HasVariableReferenceFromNonInheritedProperty', 'field_template': ' monotonic_flag', |
| 37 'inherited': False, 'independent': False}, | |
| 31 # Explicitly inherits a non-inherited property | 38 # Explicitly inherits a non-inherited property |
| 32 {'name': 'HasExplicitlyInheritedProperties', 'field_template': 'monotonic_fl ag'}, | 39 {'name': 'HasExplicitlyInheritedProperties', 'field_template': 'monotonic_fl ag', |
| 40 'inherited': False, 'independent': False}, | |
| 33 # These properties only have generated storage, and their methods are handwr itten in ComputedStyle. | 41 # These properties only have generated storage, and their methods are handwr itten in ComputedStyle. |
| 34 # TODO(shend): Remove these fields and delete the 'storage_only' template. | 42 # TODO(shend): Remove these fields and delete the 'storage_only' template. |
| 35 {'name': 'EmptyState', 'field_template': 'storage_only', 'size': 1} | 43 {'name': 'EmptyState', 'field_template': 'storage_only', 'size': 1, 'default _value': 'false', |
| 44 'type_name': 'bool', 'inherited': False, 'independent': False}, | |
| 36 ] | 45 ] |
| 37 | 46 |
| 38 | 47 |
| 39 class Field(object): | 48 class Field(object): |
| 40 """ | 49 """ |
| 41 The generated ComputedStyle object is made up of a series of Fields. | 50 The generated ComputedStyle object is made up of a series of Fields. |
| 42 Each Field has a name, size, type, etc, and a bunch of attributes to | 51 Each Field has a name, size, type, etc, and a bunch of attributes to |
| 43 determine which methods it will be used in. | 52 determine which methods it will be used in. |
| 44 | 53 |
| 45 A Field also has enough information to use any storage type in C++, such as | 54 A Field also has enough information to use any storage type in C++, such as |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 75 self.size = size | 84 self.size = size |
| 76 self.default_value = default_value | 85 self.default_value = default_value |
| 77 | 86 |
| 78 # Field role: one of these must be true | 87 # Field role: one of these must be true |
| 79 self.is_property = field_role == 'property' | 88 self.is_property = field_role == 'property' |
| 80 self.is_inherited_flag = field_role == 'inherited_flag' | 89 self.is_inherited_flag = field_role == 'inherited_flag' |
| 81 self.is_nonproperty = field_role == 'nonproperty' | 90 self.is_nonproperty = field_role == 'nonproperty' |
| 82 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c ount(True) == 1, \ | 91 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c ount(True) == 1, \ |
| 83 'Field role has to be exactly one of: property, inherited_flag, nonp roperty' | 92 'Field role has to be exactly one of: property, inherited_flag, nonp roperty' |
| 84 | 93 |
| 85 if self.is_property: | 94 if not self.is_inherited_flag: |
| 86 self.is_inherited = kwargs.pop('inherited') | 95 self.is_inherited = kwargs.pop('inherited') |
| 87 self.is_independent = kwargs.pop('independent') | 96 self.is_independent = kwargs.pop('independent') |
| 88 assert self.is_inherited or not self.is_independent, 'Only inherited fields can be independent' | 97 assert self.is_inherited or not self.is_independent, 'Only inherited fields can be independent' |
| 89 | 98 |
| 90 self.is_inherited_method_name = method_name(name_for_methods + 'IsIn herited') | 99 self.is_inherited_method_name = method_name(name_for_methods + 'IsIn herited') |
| 91 | 100 |
| 92 # Method names | 101 # Method names |
| 93 getter_prefix = 'Get' if name_for_methods == self.type_name else '' | 102 getter_prefix = 'Get' if name_for_methods == self.type_name else '' |
| 94 self.getter_method_name = method_name(getter_prefix + name_for_methods) | 103 self.getter_method_name = method_name(getter_prefix + name_for_methods) |
| 95 self.setter_method_name = method_name('Set' + name_for_methods) | 104 self.setter_method_name = method_name('Set' + name_for_methods) |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 126 assert set(enums[enum_name]) == set(enum_values), \ | 135 assert set(enums[enum_name]) == set(enum_values), \ |
| 127 ("'" + property_['name'] + "' can't have type_name '" + enum _name + "' " | 136 ("'" + property_['name'] + "' can't have type_name '" + enum _name + "' " |
| 128 "because it was used by a previous property, but with a dif ferent set of keywords. " | 137 "because it was used by a previous property, but with a dif ferent set of keywords. " |
| 129 "Either give it a different name or ensure the keywords are the same.") | 138 "Either give it a different name or ensure the keywords are the same.") |
| 130 | 139 |
| 131 enums[enum_name] = enum_values | 140 enums[enum_name] = enum_values |
| 132 | 141 |
| 133 return enums | 142 return enums |
| 134 | 143 |
| 135 | 144 |
| 136 def _create_property_field(property_): | 145 def _create_field(field_role, property_): |
| 137 """ | 146 """ |
| 138 Create a property field from a CSS property and return the Field object. | 147 Create a property or nonproperty field. |
| 139 """ | 148 """ |
| 140 bits_needed = math.log(len(property_['keywords']), 2) # TODO: implement for non-enums | 149 assert field_role in ('property', 'nonproperty') |
| 141 type_name = property_['type_name'] | |
| 142 | 150 |
| 143 assert property_['initial_keyword'] is not None, \ | 151 name_for_methods = property_['name_for_methods'] |
| 144 ('MakeComputedStyleBase requires an initial keyword for keyword fields, none specified ' | 152 |
| 145 'for property ' + property_['name']) | 153 if property_['field_template'] == 'keyword': |
| 146 default_value = type_name + '::' + enum_value_name(property_['initial_keywor d']) | 154 assert property_['initial_keyword'] is not None, \ |
| 155 ('MakeComputedStyleBase requires an initial keyword for keyword fiel ds, none specified ' | |
| 156 'for property ' + property_['name']) | |
| 157 type_name = property_['type_name'] | |
| 158 default_value = type_name + '::' + enum_value_name(property_['initial_ke yword']) | |
| 159 size = int(math.ceil(math.log(len(property_['keywords']), 2))) | |
| 160 elif property_['field_template'] == 'storage_only': | |
| 161 # 'storage_only' fields need to specify a size, type_name and default_va lue | |
| 162 type_name = property_['type_name'] | |
| 163 default_value = property_['default_value'] | |
| 164 size = property_['size'] | |
| 165 else: | |
| 166 assert property_['field_template'] in ('flag', 'monotonic_flag') | |
| 167 type_name = 'bool' | |
| 168 default_value = 'false' | |
| 169 size = 1 | |
| 147 | 170 |
| 148 return Field( | 171 return Field( |
| 149 'property', | 172 field_role, |
| 150 property_['name_for_methods'], | 173 name_for_methods, |
| 151 property_name=property_['name'], | 174 property_name=property_['name'], |
| 152 inherited=property_['inherited'], | 175 inherited=property_['inherited'], |
| 153 independent=property_['independent'], | 176 independent=property_['independent'], |
| 154 type_name=type_name, | 177 type_name=type_name, |
| 155 field_template=property_['field_template'], | 178 field_template=property_['field_template'], |
| 156 size=int(math.ceil(bits_needed)), | 179 size=size, |
| 157 default_value=default_value, | 180 default_value=default_value, |
| 158 ) | 181 ) |
| 159 | 182 |
| 160 | 183 |
| 161 def _create_inherited_flag_field(property_): | 184 def _create_inherited_flag_field(property_): |
| 162 """ | 185 """ |
| 163 Create the field used for an inheritance fast path from an independent CSS p roperty, | 186 Create the field used for an inheritance fast path from an independent CSS p roperty, |
| 164 and return the Field object. | 187 and return the Field object. |
| 165 """ | 188 """ |
| 166 return Field( | 189 return Field( |
| 167 'inherited_flag', | 190 'inherited_flag', |
| 168 property_['name_for_methods'] + 'IsInherited', | 191 property_['name_for_methods'] + 'IsInherited', |
| 169 property_name=property_['name'], | 192 property_name=property_['name'], |
| 170 type_name='bool', | 193 type_name='bool', |
| 171 field_template='flag', | 194 field_template='flag', |
| 172 size=1, | 195 size=1, |
| 173 default_value='true', | 196 default_value='true', |
| 174 ) | 197 ) |
| 175 | 198 |
| 176 | 199 |
| 177 def _create_nonproperty_field(property_): | 200 def _create_fields(field_role, properties): |
| 178 """ | 201 """ |
| 179 Create a nonproperty field from an entry in NONPROPERTY_FIELDS and return th e Field object. | 202 Create ComputedStyle fields from properties or nonproperties and return a li st of Field objects. |
| 180 """ | |
| 181 # TODO(shend): Make this work for nonflags | |
| 182 assert property_['field_template'] in ('flag', 'monotonic_flag', 'storage_on ly'), \ | |
| 183 "Nonproperties with arbitrary templates are not yet supported" | |
| 184 | |
| 185 if property_['field_template'] == 'storage_only': | |
| 186 assert 'size' in property_, 'storage_only fields need to specify a size' | |
| 187 size = property_['size'] | |
| 188 else: | |
| 189 # Otherwise the field must be some type of flag. | |
| 190 size = 1 | |
| 191 | |
| 192 return Field( | |
| 193 'nonproperty', | |
| 194 property_['name_for_methods'], | |
| 195 property_name=property_['name'], | |
| 196 type_name='bool', | |
| 197 field_template=property_['field_template'], | |
| 198 size=size, | |
| 199 default_value='false', | |
| 200 ) | |
| 201 | |
| 202 | |
| 203 def _create_fields(properties): | |
| 204 """ | |
| 205 Create ComputedStyle fields from CSS properties and return a list of Field o bjects. | |
| 206 """ | 203 """ |
| 207 fields = [] | 204 fields = [] |
| 208 for property_ in properties: | 205 for property_ in properties: |
| 209 # Only generate properties that have a field template | 206 # Only generate properties that have a field template |
| 210 if property_['field_template'] is not None: | 207 if property_['field_template'] is not None: |
| 211 # If the property is independent, add the single-bit sized isInherit ed flag | 208 # If the property is independent, add the single-bit sized isInherit ed flag |
| 212 # to the list of Fields as well. | 209 # to the list of Fields as well. |
| 213 if property_['independent']: | 210 if property_['independent']: |
| 214 fields.append(_create_inherited_flag_field(property_)) | 211 fields.append(_create_inherited_flag_field(property_)) |
| 215 | 212 |
| 216 fields.append(_create_property_field(property_)) | 213 fields.append(_create_field(field_role, property_)) |
| 217 | |
| 218 # TODO(shend): Merge NONPROPERTY_FIELDS with property_values so that propert ies and | |
| 219 # nonproperties can be treated uniformly. | |
| 220 for property_ in NONPROPERTY_FIELDS: | |
| 221 property_['name_for_methods'] = property_['name'] | |
| 222 fields.append(_create_nonproperty_field(property_)) | |
| 223 | 214 |
| 224 return fields | 215 return fields |
| 225 | 216 |
| 226 | 217 |
| 227 def _pack_fields(fields): | 218 def _pack_fields(fields): |
| 228 """ | 219 """ |
| 229 Group a list of fields into buckets to minimise padding. | 220 Group a list of fields into buckets to minimise padding. |
| 230 Returns a list of buckets, where each bucket is a list of Field objects. | 221 Returns a list of buckets, where each bucket is a list of Field objects. |
| 231 """ | 222 """ |
| 232 # Since fields cannot cross word boundaries, in order to minimize | 223 # Since fields cannot cross word boundaries, in order to minimize |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 260 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter): | 251 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter): |
| 261 def __init__(self, json5_file_path): | 252 def __init__(self, json5_file_path): |
| 262 super(ComputedStyleBaseWriter, self).__init__(json5_file_path) | 253 super(ComputedStyleBaseWriter, self).__init__(json5_file_path) |
| 263 self._outputs = { | 254 self._outputs = { |
| 264 'ComputedStyleBase.h': self.generate_base_computed_style_h, | 255 'ComputedStyleBase.h': self.generate_base_computed_style_h, |
| 265 'ComputedStyleBase.cpp': self.generate_base_computed_style_cpp, | 256 'ComputedStyleBase.cpp': self.generate_base_computed_style_cpp, |
| 266 'ComputedStyleBaseConstants.h': self.generate_base_computed_style_co nstants, | 257 'ComputedStyleBaseConstants.h': self.generate_base_computed_style_co nstants, |
| 267 'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings, | 258 'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings, |
| 268 } | 259 } |
| 269 | 260 |
| 261 # TODO(shend): Remove this once we move NONPROPERTIES to its own JSON fi le, | |
| 262 # since the JSON5 reader will handle missing fields and defaults. | |
| 263 for property_ in NONPROPERTIES: | |
| 264 property_['name_for_methods'] = property_['name'] | |
| 265 if 'field_type_path' not in property_: | |
| 266 property_['field_type_path'] = None | |
| 267 if 'type_name' not in property_: | |
| 268 property_['type_name'] = 'E' + enum_type_name(property_['name_fo r_methods']) | |
| 269 | |
| 270 property_values = self._properties.values() | 270 property_values = self._properties.values() |
| 271 | 271 |
| 272 # Override the type name when field_type_path is specified | 272 # Override the type name when field_type_path is specified |
| 273 for property_ in property_values: | 273 for property_ in property_values: |
| 274 if property_['field_type_path']: | 274 if property_['field_type_path']: |
| 275 property_['type_name'] = property_['field_type_path'].split('/') [-1] | 275 property_['type_name'] = property_['field_type_path'].split('/') [-1] |
| 276 | 276 |
| 277 # Create all the enums used by properties | 277 self._generated_enums = _create_enums(property_values + NONPROPERTIES) |
| 278 self._generated_enums = _create_enums(self._properties.values()) | |
| 279 | 278 |
| 280 # Create all the fields | 279 all_fields = (_create_fields('property', property_values) + |
| 281 all_fields = _create_fields(self._properties.values()) | 280 _create_fields('nonproperty', NONPROPERTIES)) |
| 282 | 281 |
| 283 # Group fields into buckets | 282 # Group fields into buckets |
| 284 field_buckets = _pack_fields(all_fields) | 283 field_buckets = _pack_fields(all_fields) |
| 285 | 284 |
| 286 # The expected size of ComputedStyleBase is equivalent to as many words | 285 # The expected size of ComputedStyleBase is equivalent to as many words |
| 287 # as the total number of buckets. | 286 # as the total number of buckets. |
| 288 self._expected_total_field_bytes = len(field_buckets) | 287 self._expected_total_field_bytes = len(field_buckets) |
| 289 | 288 |
| 290 # The most optimal size of ComputedStyleBase is the total sum of all the | 289 # The most optimal size of ComputedStyleBase is the total sum of all the |
| 291 # field sizes, rounded up to the nearest word. If this produces the | 290 # field sizes, rounded up to the nearest word. If this produces the |
| 292 # incorrect value, either the packing algorithm is not optimal or there | 291 # incorrect value, either the packing algorithm is not optimal or there |
| 293 # is no way to pack the fields such that excess padding space is not | 292 # is no way to pack the fields such that excess padding space is not |
| 294 # added. | 293 # added. |
| 295 # If this fails, increase extra_padding_bytes by 1, but be aware that | 294 # If this fails, increase padding_bytes by 1, but be aware that |
| 296 # this also increases ComputedStyleBase by 1 word. | 295 # this also increases ComputedStyleBase by 1 word. |
| 297 # We should be able to bring extra_padding_bytes back to 0 from time to | 296 # We should be able to bring padding_bytes back to 0 from time to |
| 298 # time. | 297 # time. |
| 299 extra_padding_bytes = 0 | 298 padding_bytes = 0 |
| 300 optimal_total_field_bytes = int(math.ceil(sum(f.size for f in all_fields ) / 32.0)) | 299 optimal_total_field_bytes = int(math.ceil(sum(f.size for f in all_fields ) / 32.0)) |
| 301 real_total_field_bytes = optimal_total_field_bytes + extra_padding_bytes | 300 real_total_field_bytes = optimal_total_field_bytes + padding_bytes |
| 302 assert self._expected_total_field_bytes == real_total_field_bytes, \ | 301 assert self._expected_total_field_bytes == real_total_field_bytes, \ |
| 303 ('The field packing algorithm produced %s bytes, optimal is %s bytes ' % | 302 ('The field packing algorithm produced %s bytes, optimal is %s bytes ' % |
| 304 (self._expected_total_field_bytes, real_total_field_bytes)) | 303 (self._expected_total_field_bytes, real_total_field_bytes)) |
| 305 | 304 |
| 306 # Order the fields so fields in each bucket are adjacent. | 305 # Order the fields so fields in each bucket are adjacent. |
| 307 self._fields = [] | 306 self._fields = [] |
| 308 for bucket in field_buckets: | 307 for bucket in field_buckets: |
| 309 for field in bucket: | 308 for field in bucket: |
| 310 self._fields.append(field) | 309 self._fields.append(field) |
| 311 | 310 |
| 312 self._include_paths = _get_include_paths(self._properties.values()) | 311 self._property_values = property_values |
|
alancutter (OOO until 2018)
2017/03/23 03:02:47
This member is now unused and can be removed.
shend
2017/03/23 04:52:07
Oops, done.
| |
| 312 self._include_paths = _get_include_paths(property_values + NONPROPERTIES ) | |
| 313 | 313 |
| 314 | 314 |
| 315 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') | 315 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') |
| 316 def generate_base_computed_style_h(self): | 316 def generate_base_computed_style_h(self): |
| 317 return { | 317 return { |
| 318 'properties': self._properties, | 318 'properties': self._properties, |
| 319 'enums': self._generated_enums, | 319 'enums': self._generated_enums, |
| 320 'include_paths': self._include_paths, | 320 'include_paths': self._include_paths, |
| 321 'fields': self._fields, | 321 'fields': self._fields, |
| 322 'expected_total_field_bytes': self._expected_total_field_bytes, | 322 'expected_total_field_bytes': self._expected_total_field_bytes, |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 351 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo r k in property_['keywords']], | 351 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo r k in property_['keywords']], |
| 352 } | 352 } |
| 353 | 353 |
| 354 return { | 354 return { |
| 355 'include_paths': self._include_paths, | 355 'include_paths': self._include_paths, |
| 356 'mappings': mappings, | 356 'mappings': mappings, |
| 357 } | 357 } |
| 358 | 358 |
| 359 if __name__ == '__main__': | 359 if __name__ == '__main__': |
| 360 json5_generator.Maker(ComputedStyleBaseWriter).main() | 360 json5_generator.Maker(ComputedStyleBaseWriter).main() |
| OLD | NEW |