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_type_name, enum_value_name, class_member_name, me
thod_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 # TODO(shend): Put this into its own JSON5 file. | 19 # TODO(shend): Put this into its own JSON5 file. |
20 NONPROPERTIES = [ | 20 NONPROPERTIES = [ |
21 {'name': 'IsLink', 'field_template': 'monotonic_flag', | 21 {'name': 'IsLink', 'field_template': 'monotonic_flag', |
22 'inherited': False, 'independent': False}, | 22 'inherited': False, 'independent': False, 'default_value': False}, |
23 {'name': 'OriginalDisplay', 'field_template': 'keyword', 'initial_keyword':
'inline', | 23 {'name': 'OriginalDisplay', 'field_template': 'keyword', 'default_value': 'i
nline', |
24 'type_name': 'EDisplay', 'inherited': False, 'independent': False, | 24 'type_name': 'EDisplay', 'inherited': False, 'independent': False, |
25 'keywords': [ | 25 'keywords': [ |
26 "inline", "block", "list-item", "inline-block", "table", "inline-table"
, "table-row-group", "table-header-group", | 26 "inline", "block", "list-item", "inline-block", "table", "inline-table"
, "table-row-group", "table-header-group", |
27 "table-footer-group", "table-row", "table-column-group", "table-column"
, "table-cell", "table-caption", "-webkit-box", | 27 "table-footer-group", "table-row", "table-column-group", "table-column"
, "table-cell", "table-caption", "-webkit-box", |
28 "-webkit-inline-box", "flex", "inline-flex", "grid", "inline-grid", "co
ntents", "flow-root", "none" | 28 "-webkit-inline-box", "flex", "inline-flex", "grid", "inline-grid", "co
ntents", "flow-root", "none" |
29 ]}, | 29 ]}, |
30 {'name': 'InsideLink', 'field_template': 'keyword', 'initial_keyword': 'not-
inside-link', | 30 {'name': 'InsideLink', 'field_template': 'keyword', 'default_value': 'not-in
side-link', |
31 'keywords': ['not-inside-link', 'inside-unvisited-link', 'inside-visited-li
nk'], | 31 'keywords': ['not-inside-link', 'inside-unvisited-link', 'inside-visited-li
nk'], |
32 'inherited': True, 'independent': False}, | 32 'inherited': True, 'independent': False}, |
33 # Style can not be shared. | 33 # Style can not be shared. |
34 {'name': 'Unique', 'field_template': 'monotonic_flag', | 34 {'name': 'Unique', 'field_template': 'monotonic_flag', |
35 'inherited': False, 'independent': False}, | 35 'inherited': False, 'independent': False, 'default_value': False}, |
36 # Whether this style is affected by these pseudo-classes. | 36 # Whether this style is affected by these pseudo-classes. |
37 {'name': 'AffectedByFocus', 'field_template': 'monotonic_flag', | 37 {'name': 'AffectedByFocus', 'field_template': 'monotonic_flag', |
38 'inherited': False, 'independent': False}, | 38 'inherited': False, 'independent': False, 'default_value': False}, |
39 {'name': 'AffectedByHover', 'field_template': 'monotonic_flag', | 39 {'name': 'AffectedByHover', 'field_template': 'monotonic_flag', |
40 'inherited': False, 'independent': False}, | 40 'inherited': False, 'independent': False, 'default_value': False}, |
41 {'name': 'AffectedByActive', 'field_template': 'monotonic_flag', | 41 {'name': 'AffectedByActive', 'field_template': 'monotonic_flag', |
42 'inherited': False, 'independent': False}, | 42 'inherited': False, 'independent': False, 'default_value': False}, |
43 {'name': 'AffectedByDrag', 'field_template': 'monotonic_flag', | 43 {'name': 'AffectedByDrag', 'field_template': 'monotonic_flag', |
44 'inherited': False, 'independent': False}, | 44 'inherited': False, 'independent': False, 'default_value': False}, |
45 # A non-inherited property references a variable or @apply is used | 45 # A non-inherited property references a variable or @apply is used |
46 {'name': 'HasVariableReferenceFromNonInheritedProperty', 'field_template': '
monotonic_flag', | 46 {'name': 'HasVariableReferenceFromNonInheritedProperty', 'field_template': '
monotonic_flag', |
47 'inherited': False, 'independent': False}, | 47 'inherited': False, 'independent': False, 'default_value': False}, |
48 # Explicitly inherits a non-inherited property | 48 # Explicitly inherits a non-inherited property |
49 {'name': 'HasExplicitlyInheritedProperties', 'field_template': 'monotonic_fl
ag', | 49 {'name': 'HasExplicitlyInheritedProperties', 'field_template': 'monotonic_fl
ag', |
50 'inherited': False, 'independent': False}, | 50 'inherited': False, 'independent': False, 'default_value': False}, |
51 # These properties only have generated storage, and their methods are handwr
itten in ComputedStyle. | 51 # These properties only have generated storage, and their methods are handwr
itten in ComputedStyle. |
52 # TODO(shend): Remove these fields and delete the 'storage_only' template. | 52 # TODO(shend): Remove these fields and delete the 'storage_only' template. |
53 {'name': 'EmptyState', 'field_template': 'storage_only', 'size': 1, 'default
_value': 'false', | 53 {'name': 'EmptyState', 'field_template': 'storage_only', 'size': 1, 'default
_value': 'false', |
54 'type_name': 'bool', 'inherited': False, 'independent': False}, | 54 'type_name': 'bool', 'inherited': False, 'independent': False}, |
55 {'name': 'StyleType', 'field_template': 'storage_only', 'size': 6, 'default_
value': '0', | 55 {'name': 'StyleType', 'field_template': 'storage_only', 'size': 6, 'default_
value': '0', |
56 'type_name': 'PseudoId', 'inherited': False, 'independent': False}, | 56 'type_name': 'PseudoId', 'inherited': False, 'independent': False}, |
57 {'name': 'PseudoBits', 'field_template': 'storage_only', 'size': 8, 'default
_value': 'PseudoIdNone', | 57 {'name': 'PseudoBits', 'field_template': 'storage_only', 'size': 8, 'default
_value': 'PseudoIdNone', |
58 'type_name': 'PseudoId', 'inherited': False, 'independent': False}, | 58 'type_name': 'PseudoId', 'inherited': False, 'independent': False}, |
59 # True if 'underline solid' is the only text decoration on this element. | 59 # True if 'underline solid' is the only text decoration on this element. |
60 {'name': 'HasSimpleUnderline', 'field_template': 'storage_only', 'size': 1,
'default_value': 'false', | 60 {'name': 'HasSimpleUnderline', 'field_template': 'storage_only', 'size': 1,
'default_value': 'false', |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 | 115 |
116 self.is_inherited_method_name = method_name(name_for_methods + 'IsIn
herited') | 116 self.is_inherited_method_name = method_name(name_for_methods + 'IsIn
herited') |
117 | 117 |
118 # Method names | 118 # Method names |
119 getter_prefix = 'Get' if name_for_methods == self.type_name else '' | 119 getter_prefix = 'Get' if name_for_methods == self.type_name else '' |
120 self.getter_method_name = method_name(getter_prefix + name_for_methods) | 120 self.getter_method_name = method_name(getter_prefix + name_for_methods) |
121 self.setter_method_name = method_name('Set' + name_for_methods) | 121 self.setter_method_name = method_name('Set' + name_for_methods) |
122 self.initial_method_name = method_name('Initial' + name_for_methods) | 122 self.initial_method_name = method_name('Initial' + name_for_methods) |
123 self.resetter_method_name = method_name('Reset' + name_for_methods) | 123 self.resetter_method_name = method_name('Reset' + name_for_methods) |
124 | 124 |
| 125 # If the size of the field is not None, it means it is a bit field |
| 126 self.is_bit_field = self.size is not None |
| 127 |
125 assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + st
r(kwargs) | 128 assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + st
r(kwargs) |
126 | 129 |
127 | 130 |
128 def _get_include_paths(properties): | 131 def _get_include_paths(properties): |
129 """ | 132 """ |
130 Get a list of paths that need to be included for ComputedStyleBase. | 133 Get a list of paths that need to be included for ComputedStyleBase. |
131 """ | 134 """ |
132 include_paths = set() | 135 include_paths = set() |
133 for property_ in properties: | 136 for property_ in properties: |
134 if property_['field_type_path'] is not None: | 137 if property_['field_type_path'] is not None: |
(...skipping 25 matching lines...) Expand all Loading... |
160 | 163 |
161 | 164 |
162 def _create_field(field_role, property_): | 165 def _create_field(field_role, property_): |
163 """ | 166 """ |
164 Create a property or nonproperty field. | 167 Create a property or nonproperty field. |
165 """ | 168 """ |
166 assert field_role in ('property', 'nonproperty') | 169 assert field_role in ('property', 'nonproperty') |
167 | 170 |
168 name_for_methods = property_['name_for_methods'] | 171 name_for_methods = property_['name_for_methods'] |
169 | 172 |
| 173 assert property_['default_value'] is not None, \ |
| 174 ('MakeComputedStyleBase requires an default value for all fields, none s
pecified ' |
| 175 'for property ' + property_['name']) |
| 176 |
170 if property_['field_template'] == 'keyword': | 177 if property_['field_template'] == 'keyword': |
171 assert property_['initial_keyword'] is not None, \ | |
172 ('MakeComputedStyleBase requires an initial keyword for keyword fiel
ds, none specified ' | |
173 'for property ' + property_['name']) | |
174 type_name = property_['type_name'] | 178 type_name = property_['type_name'] |
175 default_value = type_name + '::' + enum_value_name(property_['initial_ke
yword']) | 179 default_value = type_name + '::' + enum_value_name(property_['default_va
lue']) |
176 size = int(math.ceil(math.log(len(property_['keywords']), 2))) | 180 size = int(math.ceil(math.log(len(property_['keywords']), 2))) |
177 elif property_['field_template'] == 'storage_only': | 181 elif property_['field_template'] == 'storage_only': |
178 # 'storage_only' fields need to specify a size, type_name and default_va
lue | 182 # 'storage_only' fields need to specify a size, type_name and default_va
lue |
179 type_name = property_['type_name'] | 183 type_name = property_['type_name'] |
180 default_value = property_['default_value'] | 184 default_value = property_['default_value'] |
181 size = property_['size'] | 185 size = property_['size'] |
| 186 elif property_['field_template'] == 'external': |
| 187 type_name = property_['type_name'] |
| 188 default_value = property_['default_value'] |
| 189 size = None |
182 else: | 190 else: |
183 assert property_['field_template'] in ('flag', 'monotonic_flag') | 191 assert property_['field_template'] in ('flag', 'monotonic_flag') |
184 type_name = 'bool' | 192 type_name = 'bool' |
185 default_value = 'false' | 193 default_value = 'false' |
186 size = 1 | 194 size = 1 |
187 | 195 |
188 return Field( | 196 return Field( |
189 field_role, | 197 field_role, |
190 name_for_methods, | 198 name_for_methods, |
191 property_name=property_['name'], | 199 property_name=property_['name'], |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 Group a list of fields into buckets to minimise padding. | 245 Group a list of fields into buckets to minimise padding. |
238 Returns a list of buckets, where each bucket is a list of Field objects. | 246 Returns a list of buckets, where each bucket is a list of Field objects. |
239 """ | 247 """ |
240 # Since fields cannot cross word boundaries, in order to minimize | 248 # Since fields cannot cross word boundaries, in order to minimize |
241 # padding, group fields into buckets so that as many buckets as possible | 249 # padding, group fields into buckets so that as many buckets as possible |
242 # are exactly 32 bits. Although this greedy approach may not always | 250 # are exactly 32 bits. Although this greedy approach may not always |
243 # produce the optimal solution, we add a static_assert to the code to | 251 # produce the optimal solution, we add a static_assert to the code to |
244 # ensure ComputedStyleBase results in the expected size. If that | 252 # ensure ComputedStyleBase results in the expected size. If that |
245 # static_assert fails, this code is falling into the small number of | 253 # static_assert fails, this code is falling into the small number of |
246 # cases that are suboptimal, and may need to be rethought. | 254 # cases that are suboptimal, and may need to be rethought. |
247 # For more details on packing bitfields to reduce padding, see: | 255 # For more details on packing bit fields to reduce padding, see: |
248 # http://www.catb.org/esr/structure-packing/#_bitfields | 256 # http://www.catb.org/esr/structure-packing/#_bitfields |
249 field_buckets = [] | 257 field_buckets = [] |
250 # Consider fields in descending order of size to reduce fragmentation | 258 # Consider fields in descending order of size to reduce fragmentation |
251 # when they are selected. | 259 # when they are selected. |
252 for field in sorted(fields, key=lambda f: f.size, reverse=True): | 260 for field in sorted(fields, key=lambda f: f.size, reverse=True): |
253 added_to_bucket = False | 261 added_to_bucket = False |
254 # Go through each bucket and add this field if it will not increase | 262 # Go through each bucket and add this field if it will not increase |
255 # the bucket's size to larger than 32 bits. Otherwise, make a new | 263 # the bucket's size to larger than 32 bits. Otherwise, make a new |
256 # bucket containing only this field. | 264 # bucket containing only this field. |
257 for bucket in field_buckets: | 265 for bucket in field_buckets: |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 # Override the type name when field_type_path is specified | 297 # Override the type name when field_type_path is specified |
290 for property_ in property_values: | 298 for property_ in property_values: |
291 if property_['field_type_path']: | 299 if property_['field_type_path']: |
292 property_['type_name'] = property_['field_type_path'].split('/')
[-1] | 300 property_['type_name'] = property_['field_type_path'].split('/')
[-1] |
293 | 301 |
294 self._generated_enums = _create_enums(property_values + NONPROPERTIES) | 302 self._generated_enums = _create_enums(property_values + NONPROPERTIES) |
295 | 303 |
296 all_fields = (_create_fields('property', property_values) + | 304 all_fields = (_create_fields('property', property_values) + |
297 _create_fields('nonproperty', NONPROPERTIES)) | 305 _create_fields('nonproperty', NONPROPERTIES)) |
298 | 306 |
299 # Group fields into buckets | 307 # Separate the normal fields from the bit fields |
300 field_buckets = _pack_fields(all_fields) | 308 bit_fields = [field for field in all_fields if field.is_bit_field] |
| 309 normal_fields = [field for field in all_fields if not field.is_bit_field
] |
| 310 |
| 311 # Pack bit fields into buckets |
| 312 field_buckets = _pack_fields(bit_fields) |
301 | 313 |
302 # The expected size of ComputedStyleBase is equivalent to as many words | 314 # The expected size of ComputedStyleBase is equivalent to as many words |
303 # as the total number of buckets. | 315 # as the total number of buckets. |
304 self._expected_total_field_bytes = len(field_buckets) | 316 self._expected_bit_field_bytes = len(field_buckets) |
305 | 317 |
306 # The most optimal size of ComputedStyleBase is the total sum of all the | 318 # The most optimal size of ComputedStyleBase is the total sum of all the |
307 # field sizes, rounded up to the nearest word. If this produces the | 319 # field sizes, rounded up to the nearest word. If this produces the |
308 # incorrect value, either the packing algorithm is not optimal or there | 320 # incorrect value, either the packing algorithm is not optimal or there |
309 # is no way to pack the fields such that excess padding space is not | 321 # is no way to pack the fields such that excess padding space is not |
310 # added. | 322 # added. |
311 # If this fails, increase padding_bytes by 1, but be aware that | 323 # If this fails, increase extra_padding_bytes by 1, but be aware that |
312 # this also increases ComputedStyleBase by 1 word. | 324 # this also increases ComputedStyleBase by 1 word. |
313 # We should be able to bring padding_bytes back to 0 from time to | 325 # We should be able to bring extra_padding_bytes back to 0 from time to |
314 # time. | 326 # time. |
315 padding_bytes = 0 | 327 extra_padding_bytes = 0 |
316 optimal_total_field_bytes = int(math.ceil(sum(f.size for f in all_fields
) / 32.0)) | 328 optimal_bit_field_bytes = int(math.ceil(sum(f.size for f in bit_fields)
/ 32.0)) |
317 real_total_field_bytes = optimal_total_field_bytes + padding_bytes | 329 real_bit_field_bytes = optimal_bit_field_bytes + extra_padding_bytes |
318 assert self._expected_total_field_bytes == real_total_field_bytes, \ | 330 assert self._expected_bit_field_bytes == real_bit_field_bytes, \ |
319 ('The field packing algorithm produced %s bytes, optimal is %s bytes
' % | 331 ('The field packing algorithm produced %s bytes, optimal is %s bytes
' % |
320 (self._expected_total_field_bytes, real_total_field_bytes)) | 332 (self._expected_bit_field_bytes, real_bit_field_bytes)) |
| 333 |
| 334 # Normal fields go first, then the bit fields. |
| 335 self._fields = list(normal_fields) |
321 | 336 |
322 # Order the fields so fields in each bucket are adjacent. | 337 # Order the fields so fields in each bucket are adjacent. |
323 self._fields = [] | |
324 for bucket in field_buckets: | 338 for bucket in field_buckets: |
325 for field in bucket: | 339 for field in bucket: |
326 self._fields.append(field) | 340 self._fields.append(field) |
327 | 341 |
328 self._include_paths = _get_include_paths(property_values + NONPROPERTIES
) | 342 self._include_paths = _get_include_paths(property_values + NONPROPERTIES
) |
329 | 343 |
330 | 344 |
331 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') | 345 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') |
332 def generate_base_computed_style_h(self): | 346 def generate_base_computed_style_h(self): |
333 return { | 347 return { |
334 'properties': self._properties, | 348 'properties': self._properties, |
335 'enums': self._generated_enums, | 349 'enums': self._generated_enums, |
336 'include_paths': self._include_paths, | 350 'include_paths': self._include_paths, |
337 'fields': self._fields, | 351 'fields': self._fields, |
338 'expected_total_field_bytes': self._expected_total_field_bytes, | |
339 } | 352 } |
340 | 353 |
341 @template_expander.use_jinja('ComputedStyleBase.cpp.tmpl') | 354 @template_expander.use_jinja('ComputedStyleBase.cpp.tmpl') |
342 def generate_base_computed_style_cpp(self): | 355 def generate_base_computed_style_cpp(self): |
343 return { | 356 return { |
344 'properties': self._properties, | 357 'properties': self._properties, |
345 'enums': self._generated_enums, | 358 'enums': self._generated_enums, |
346 'fields': self._fields, | 359 'fields': self._fields, |
347 'expected_total_field_bytes': self._expected_total_field_bytes, | 360 'expected_bit_field_bytes': self._expected_bit_field_bytes, |
348 } | 361 } |
349 | 362 |
350 @template_expander.use_jinja('ComputedStyleBaseConstants.h.tmpl') | 363 @template_expander.use_jinja('ComputedStyleBaseConstants.h.tmpl') |
351 def generate_base_computed_style_constants(self): | 364 def generate_base_computed_style_constants(self): |
352 return { | 365 return { |
353 'properties': self._properties, | 366 'properties': self._properties, |
354 'enums': self._generated_enums, | 367 'enums': self._generated_enums, |
355 'fields': self._fields, | 368 'fields': self._fields, |
356 'expected_total_field_bytes': self._expected_total_field_bytes, | |
357 } | 369 } |
358 | 370 |
359 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') | 371 @template_expander.use_jinja('CSSValueIDMappingsGenerated.h.tmpl') |
360 def generate_css_value_mappings(self): | 372 def generate_css_value_mappings(self): |
361 mappings = {} | 373 mappings = {} |
362 | 374 |
363 for property_ in self._properties.values(): | 375 for property_ in self._properties.values(): |
364 if property_['field_template'] == 'keyword': | 376 if property_['field_template'] == 'keyword': |
365 mappings[property_['type_name']] = { | 377 mappings[property_['type_name']] = { |
366 'default_value': enum_value_name(property_['initial_keyword'
]), | 378 'default_value': enum_value_name(property_['default_value'])
, |
367 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo
r k in property_['keywords']], | 379 'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) fo
r k in property_['keywords']], |
368 } | 380 } |
369 | 381 |
370 return { | 382 return { |
371 'include_paths': self._include_paths, | 383 'include_paths': self._include_paths, |
372 'mappings': mappings, | 384 'mappings': mappings, |
373 } | 385 } |
374 | 386 |
375 if __name__ == '__main__': | 387 if __name__ == '__main__': |
376 json5_generator.Maker(ComputedStyleBaseWriter).main() | 388 json5_generator.Maker(ComputedStyleBaseWriter).main() |
OLD | NEW |