Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: third_party/WebKit/Source/build/scripts/make_computed_style_base.py

Issue 2786883002: Generate subgroup StyleSurroundData in ComputedStyle. (Closed)
Patch Set: Rebase Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | third_party/WebKit/Source/build/scripts/name_utilities.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 join_name 15 class_name, join_name
16 ) 16 )
17 from collections import OrderedDict 17 from collections import defaultdict, OrderedDict
18 from itertools import chain
19
20 # TODO(shend): Improve documentation and add docstrings.
21
22
23 def _flatten_list(x):
24 """Flattens a list of lists into a single list."""
25 return list(chain.from_iterable(x))
26
27
28 def _num_32_bit_words_for_bit_fields(bit_fields):
29 """Gets the number of 32 bit unsigned integers needed store a list of bit fi elds."""
30 num_buckets, cur_bucket = 0, 0
31 for field in bit_fields:
32 if field.size + cur_bucket > 32:
33 num_buckets += 1
34 cur_bucket = 0
35 cur_bucket += field.size
36 return num_buckets + (cur_bucket > 0)
37
38
39 class Group(object):
40 """Represents a group of fields stored together in a class.
41
42 Attributes:
43 name: The name of the group as a string.
44 subgroups: List of Group instances that are stored as subgroups under th is group.
45 fields: List of Field instances stored directly under this group.
46 """
47 def __init__(self, name, subgroups, fields):
48 self.name = name
49 self.subgroups = subgroups
50 self.fields = fields
51 self.type_name = class_name(join_name('style', name, 'data'))
52 self.member_name = class_member_name(join_name(name, 'data'))
53 self.num_32_bit_words_for_bit_fields = _num_32_bit_words_for_bit_fields(
54 field for field in fields if field.is_bit_field
55 )
56
57 # Recursively get all the fields in the subgroups as well
58 self.all_fields = _flatten_list(subgroup.all_fields for subgroup in subg roups) + fields
18 59
19 60
20 class Field(object): 61 class Field(object):
21 """ 62 """
22 The generated ComputedStyle object is made up of a series of Fields. 63 The generated ComputedStyle object is made up of a series of Fields.
23 Each Field has a name, size, type, etc, and a bunch of attributes to 64 Each Field has a name, size, type, etc, and a bunch of attributes to
24 determine which methods it will be used in. 65 determine which methods it will be used in.
25 66
26 A Field also has enough information to use any storage type in C++, such as 67 A Field also has enough information to use any storage type in C++, such as
27 regular member variables, or more complex storage like vectors or hashmaps. 68 regular member variables, or more complex storage like vectors or hashmaps.
28 Almost all properties will have at least one Field, often more than one. 69 Almost all properties will have at least one Field, often more than one.
29 70
30 Most attributes in this class correspond to parameters in CSSProperties.json 5. 71 Most attributes in this class correspond to parameters in CSSProperties.json 5.
31 See that file for a more detailed explanation of each attribute. 72 See that file for a more detailed explanation of each attribute.
32 73
33 Attributes: 74 Attributes:
34 field_role: The semantic role of the field. Can be: 75 field_role: The semantic role of the field. Can be:
35 - 'property': for fields that store CSS properties 76 - 'property': for fields that store CSS properties
36 - 'inherited_flag': for single-bit flags that store whether a proper ty is 77 - 'inherited_flag': for single-bit flags that store whether a proper ty is
37 inherited by this style or set explicitly 78 inherited by this style or set explicitly
38 - 'nonproperty': for fields that are not CSS properties 79 - 'nonproperty': for fields that are not CSS properties
39 name_for_methods: String used to form the names of getters and setters. 80 name_for_methods: String used to form the names of getters and setters.
40 Should be in upper camel case. 81 Should be in upper camel case.
41 property_name: Name of the property that the field is part of. 82 property_name: Name of the property that the field is part of.
42 type_name: Name of the C++ type exposed by the generated interface (e.g. EClear, int). 83 type_name: Name of the C++ type exposed by the generated interface (e.g. EClear, int).
43 field_template: Determines the interface generated for the field. Can be one of: 84 field_template: Determines the interface generated for the field. Can be one of:
44 keyword, flag, or monotonic_flag. 85 keyword, flag, or monotonic_flag.
86 field_group: The name of the group that this field is inside.
45 size: Number of bits needed for storage. 87 size: Number of bits needed for storage.
46 default_value: Default value for this field when it is first initialized . 88 default_value: Default value for this field when it is first initialized .
47 """ 89 """
48 90
49 def __init__(self, field_role, name_for_methods, property_name, type_name, 91 def __init__(self, field_role, name_for_methods, property_name, type_name,
50 field_template, size, default_value, getter_method_name, setter _method_name, 92 field_template, field_group, size, default_value,
51 initial_method_name, **kwargs): 93 getter_method_name, setter_method_name, initial_method_name, ** kwargs):
52 """Creates a new field.""" 94 """Creates a new field."""
53 self.name = class_member_name(name_for_methods) 95 self.name = class_member_name(name_for_methods)
54 self.property_name = property_name 96 self.property_name = property_name
55 self.type_name = type_name 97 self.type_name = type_name
56 self.field_template = field_template 98 self.field_template = field_template
99 self.group_name = field_group
100 self.group_member_name = class_member_name(join_name(field_group, 'data' )) if field_group else None
57 self.size = size 101 self.size = size
58 self.default_value = default_value 102 self.default_value = default_value
59 103
60 # Field role: one of these must be true 104 # Field role: one of these must be true
61 self.is_property = field_role == 'property' 105 self.is_property = field_role == 'property'
62 self.is_inherited_flag = field_role == 'inherited_flag' 106 self.is_inherited_flag = field_role == 'inherited_flag'
63 self.is_nonproperty = field_role == 'nonproperty' 107 self.is_nonproperty = field_role == 'nonproperty'
64 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c ount(True) == 1, \ 108 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c ount(True) == 1, \
65 'Field role has to be exactly one of: property, inherited_flag, nonp roperty' 109 'Field role has to be exactly one of: property, inherited_flag, nonp roperty'
66 110
(...skipping 24 matching lines...) Expand all
91 """ 135 """
92 Get a list of paths that need to be included for ComputedStyleBase. 136 Get a list of paths that need to be included for ComputedStyleBase.
93 """ 137 """
94 include_paths = set() 138 include_paths = set()
95 for property_ in properties: 139 for property_ in properties:
96 if property_['field_type_path'] is not None: 140 if property_['field_type_path'] is not None:
97 include_paths.add(property_['field_type_path'] + '.h') 141 include_paths.add(property_['field_type_path'] + '.h')
98 return list(sorted(include_paths)) 142 return list(sorted(include_paths))
99 143
100 144
145 def _group_fields(fields):
146 """Groups a list of fields by their group_name and returns the root group."" "
147 groups = defaultdict(list)
148 for field in fields:
149 groups[field.group_name].append(field)
150
151 no_group = groups.pop(None)
152 subgroups = [Group(group_name, [], _reorder_fields(fields)) for group_name, fields in groups.items()]
153 return Group('', subgroups=subgroups, fields=_reorder_fields(no_group))
154
155
101 def _create_enums(properties): 156 def _create_enums(properties):
102 """ 157 """
103 Returns an OrderedDict of enums to be generated, enum name -> [list of enum values] 158 Returns an OrderedDict of enums to be generated, enum name -> [list of enum values]
104 """ 159 """
105 enums = {} 160 enums = {}
106 for property_ in properties: 161 for property_ in properties:
107 # Only generate enums for keyword properties that use the default field_ type_path. 162 # Only generate enums for keyword properties that use the default field_ type_path.
108 if property_['field_template'] == 'keyword' and property_['field_type_pa th'] is None: 163 if property_['field_template'] == 'keyword' and property_['field_type_pa th'] is None:
109 enum_name = property_['type_name'] 164 enum_name = property_['type_name']
110 enum_values = [enum_value_name(k) for k in property_['keywords']] 165 enum_values = [enum_value_name(k) for k in property_['keywords']]
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 size = 1 213 size = 1
159 214
160 return Field( 215 return Field(
161 field_role, 216 field_role,
162 name_for_methods, 217 name_for_methods,
163 property_name=property_['name'], 218 property_name=property_['name'],
164 inherited=property_['inherited'], 219 inherited=property_['inherited'],
165 independent=property_['independent'], 220 independent=property_['independent'],
166 type_name=type_name, 221 type_name=type_name,
167 field_template=property_['field_template'], 222 field_template=property_['field_template'],
223 field_group=property_['field_group'],
168 size=size, 224 size=size,
169 default_value=default_value, 225 default_value=default_value,
170 getter_method_name=property_['getter'], 226 getter_method_name=property_['getter'],
171 setter_method_name=property_['setter'], 227 setter_method_name=property_['setter'],
172 initial_method_name=property_['initial'], 228 initial_method_name=property_['initial'],
173 ) 229 )
174 230
175 231
176 def _create_inherited_flag_field(property_): 232 def _create_inherited_flag_field(property_):
177 """ 233 """
178 Create the field used for an inheritance fast path from an independent CSS p roperty, 234 Create the field used for an inheritance fast path from an independent CSS p roperty,
179 and return the Field object. 235 and return the Field object.
180 """ 236 """
181 name_for_methods = join_name(property_['name_for_methods'], 'is inherited') 237 name_for_methods = join_name(property_['name_for_methods'], 'is inherited')
182 return Field( 238 return Field(
183 'inherited_flag', 239 'inherited_flag',
184 name_for_methods, 240 name_for_methods,
185 property_name=property_['name'], 241 property_name=property_['name'],
186 type_name='bool', 242 type_name='bool',
187 field_template='primitive', 243 field_template='primitive',
244 field_group=property_['field_group'],
188 size=1, 245 size=1,
189 default_value='true', 246 default_value='true',
190 getter_method_name=method_name(name_for_methods), 247 getter_method_name=method_name(name_for_methods),
191 setter_method_name=method_name(join_name('set', name_for_methods)), 248 setter_method_name=method_name(join_name('set', name_for_methods)),
192 initial_method_name=method_name(join_name('initial', name_for_methods)), 249 initial_method_name=method_name(join_name('initial', name_for_methods)),
193 ) 250 )
194 251
195 252
196 def _create_fields(properties): 253 def _create_fields(properties):
197 """ 254 """
198 Create ComputedStyle fields from properties or nonproperties and return a li st of Field objects. 255 Create ComputedStyle fields from properties or nonproperties and return a li st of Field objects.
199 """ 256 """
200 fields = [] 257 fields = []
201 for property_ in properties: 258 for property_ in properties:
202 # Only generate properties that have a field template 259 # Only generate properties that have a field template
203 if property_['field_template'] is not None: 260 if property_['field_template'] is not None:
204 # If the property is independent, add the single-bit sized isInherit ed flag 261 # If the property is independent, add the single-bit sized isInherit ed flag
205 # to the list of Fields as well. 262 # to the list of Fields as well.
206 if property_['independent']: 263 if property_['independent']:
207 fields.append(_create_inherited_flag_field(property_)) 264 fields.append(_create_inherited_flag_field(property_))
208 265
209 # TODO(shend): Get rid of the property/nonproperty field roles. 266 # TODO(shend): Get rid of the property/nonproperty field roles.
267 # If the field has_custom_compare_and_copy, then it does not appear in
268 # ComputedStyle::operator== and ComputedStyle::CopyNonInheritedFromC ached.
210 field_role = 'nonproperty' if property_['has_custom_compare_and_copy '] else 'property' 269 field_role = 'nonproperty' if property_['has_custom_compare_and_copy '] else 'property'
211 fields.append(_create_field(field_role, property_)) 270 fields.append(_create_field(field_role, property_))
212 271
213 return fields 272 return fields
214 273
215 274
216 def _pack_fields(fields): 275 def _reorder_fields(fields):
217 """ 276 """
218 Group a list of fields into buckets to minimise padding. 277 Returns a list of fields ordered to minimise padding.
219 Returns a list of buckets, where each bucket is a list of Field objects.
220 """ 278 """
279 # Separate out bit fields from non bit fields
280 bit_fields = [field for field in fields if field.is_bit_field]
281 non_bit_fields = [field for field in fields if not field.is_bit_field]
282
221 # Since fields cannot cross word boundaries, in order to minimize 283 # Since fields cannot cross word boundaries, in order to minimize
222 # padding, group fields into buckets so that as many buckets as possible 284 # padding, group fields into buckets so that as many buckets as possible
223 # are exactly 32 bits. Although this greedy approach may not always 285 # are exactly 32 bits. Although this greedy approach may not always
224 # produce the optimal solution, we add a static_assert to the code to 286 # produce the optimal solution, we add a static_assert to the code to
225 # ensure ComputedStyleBase results in the expected size. If that 287 # ensure ComputedStyleBase results in the expected size. If that
226 # static_assert fails, this code is falling into the small number of 288 # static_assert fails, this code is falling into the small number of
227 # cases that are suboptimal, and may need to be rethought. 289 # cases that are suboptimal, and may need to be rethought.
228 # For more details on packing bit fields to reduce padding, see: 290 # For more details on packing bit fields to reduce padding, see:
229 # http://www.catb.org/esr/structure-packing/#_bitfields 291 # http://www.catb.org/esr/structure-packing/#_bitfields
230 field_buckets = [] 292 field_buckets = []
231 # Consider fields in descending order of size to reduce fragmentation 293 # Consider fields in descending order of size to reduce fragmentation
232 # when they are selected. Ties broken in alphabetical order by name. 294 # when they are selected. Ties broken in alphabetical order by name.
233 for field in sorted(fields, key=lambda f: (-f.size, f.name)): 295 for field in sorted(bit_fields, key=lambda f: (-f.size, f.name)):
234 added_to_bucket = False 296 added_to_bucket = False
235 # Go through each bucket and add this field if it will not increase 297 # Go through each bucket and add this field if it will not increase
236 # the bucket's size to larger than 32 bits. Otherwise, make a new 298 # the bucket's size to larger than 32 bits. Otherwise, make a new
237 # bucket containing only this field. 299 # bucket containing only this field.
238 for bucket in field_buckets: 300 for bucket in field_buckets:
239 if sum(f.size for f in bucket) + field.size <= 32: 301 if sum(f.size for f in bucket) + field.size <= 32:
240 bucket.append(field) 302 bucket.append(field)
241 added_to_bucket = True 303 added_to_bucket = True
242 break 304 break
243 if not added_to_bucket: 305 if not added_to_bucket:
244 field_buckets.append([field]) 306 field_buckets.append([field])
245 307
246 return field_buckets 308 # Non bit fields go first, then the bit fields.
309 return list(non_bit_fields) + _flatten_list(field_buckets)
247 310
248 311
249 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter): 312 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter):
250 def __init__(self, json5_file_paths): 313 def __init__(self, json5_file_paths):
251 # Read CSS properties 314 # Read CSS properties
252 super(ComputedStyleBaseWriter, self).__init__([json5_file_paths[0]]) 315 super(ComputedStyleBaseWriter, self).__init__([json5_file_paths[0]])
253 316
254 # Ignore shorthand properties 317 # Ignore shorthand properties
255 for property_ in self._properties.values(): 318 for property_ in self._properties.values():
256 if property_['field_template'] is not None: 319 if property_['field_template'] is not None:
(...skipping 21 matching lines...) Expand all
278 341
279 # Override the type name when field_type_path is specified 342 # Override the type name when field_type_path is specified
280 for property_ in all_properties: 343 for property_ in all_properties:
281 if property_['field_type_path']: 344 if property_['field_type_path']:
282 property_['type_name'] = property_['field_type_path'].split('/') [-1] 345 property_['type_name'] = property_['field_type_path'].split('/') [-1]
283 346
284 self._generated_enums = _create_enums(all_properties) 347 self._generated_enums = _create_enums(all_properties)
285 348
286 all_fields = _create_fields(all_properties) 349 all_fields = _create_fields(all_properties)
287 350
288 # Separate the normal fields from the bit fields 351 # Organise fields into a tree structure where the root group
289 bit_fields = [field for field in all_fields if field.is_bit_field] 352 # is ComputedStyleBase.
290 normal_fields = [field for field in all_fields if not field.is_bit_field ] 353 self._root_group = _group_fields(all_fields)
291
292 # Pack bit fields into buckets
293 field_buckets = _pack_fields(bit_fields)
294
295 # The expected size of ComputedStyleBase is equivalent to as many words
296 # as the total number of buckets.
297 self._expected_bit_field_bytes = len(field_buckets)
298
299 # The most optimal size of ComputedStyleBase is the total sum of all the
300 # field sizes, rounded up to the nearest word. If this produces the
301 # incorrect value, either the packing algorithm is not optimal or there
302 # is no way to pack the fields such that excess padding space is not
303 # added.
304 # If this fails, increase extra_padding_bytes by 1, but be aware that
305 # this also increases ComputedStyleBase by 1 word.
306 # We should be able to bring extra_padding_bytes back to 0 from time to
307 # time.
308 extra_padding_bytes = 0
309 optimal_bit_field_bytes = int(math.ceil(sum(f.size for f in bit_fields) / 32.0))
310 real_bit_field_bytes = optimal_bit_field_bytes + extra_padding_bytes
311 assert self._expected_bit_field_bytes == real_bit_field_bytes, \
312 ('The field packing algorithm produced %s bytes, optimal is %s bytes ' %
313 (self._expected_bit_field_bytes, real_bit_field_bytes))
314
315 # Normal fields go first, then the bit fields.
316 self._fields = list(normal_fields)
317
318 # Order the fields so fields in each bucket are adjacent.
319 for bucket in field_buckets:
320 for field in bucket:
321 self._fields.append(field)
322 354
323 self._include_paths = _get_include_paths(all_properties) 355 self._include_paths = _get_include_paths(all_properties)
324 self._outputs = { 356 self._outputs = {
325 'ComputedStyleBase.h': self.generate_base_computed_style_h, 357 'ComputedStyleBase.h': self.generate_base_computed_style_h,
326 'ComputedStyleBase.cpp': self.generate_base_computed_style_cpp, 358 'ComputedStyleBase.cpp': self.generate_base_computed_style_cpp,
327 'ComputedStyleBaseConstants.h': self.generate_base_computed_style_co nstants, 359 'ComputedStyleBaseConstants.h': self.generate_base_computed_style_co nstants,
328 } 360 }
329 361
330 @template_expander.use_jinja('ComputedStyleBase.h.tmpl') 362 @template_expander.use_jinja('ComputedStyleBase.h.tmpl')
331 def generate_base_computed_style_h(self): 363 def generate_base_computed_style_h(self):
332 return { 364 return {
333 'properties': self._properties, 365 'properties': self._properties,
334 'enums': self._generated_enums, 366 'enums': self._generated_enums,
335 'include_paths': self._include_paths, 367 'include_paths': self._include_paths,
336 'fields': self._fields, 368 'computed_style': self._root_group,
337 } 369 }
338 370
339 @template_expander.use_jinja('ComputedStyleBase.cpp.tmpl') 371 @template_expander.use_jinja('ComputedStyleBase.cpp.tmpl')
340 def generate_base_computed_style_cpp(self): 372 def generate_base_computed_style_cpp(self):
341 return { 373 return {
342 'properties': self._properties, 374 'properties': self._properties,
343 'enums': self._generated_enums, 375 'enums': self._generated_enums,
344 'fields': self._fields, 376 'computed_style': self._root_group,
345 'expected_bit_field_bytes': self._expected_bit_field_bytes,
346 } 377 }
347 378
348 @template_expander.use_jinja('ComputedStyleBaseConstants.h.tmpl') 379 @template_expander.use_jinja('ComputedStyleBaseConstants.h.tmpl')
349 def generate_base_computed_style_constants(self): 380 def generate_base_computed_style_constants(self):
350 return { 381 return {
351 'properties': self._properties, 382 'properties': self._properties,
352 'enums': self._generated_enums, 383 'enums': self._generated_enums,
353 'fields': self._fields,
354 } 384 }
355 385
356 if __name__ == '__main__': 386 if __name__ == '__main__':
357 json5_generator.Maker(ComputedStyleBaseWriter).main() 387 json5_generator.Maker(ComputedStyleBaseWriter).main()
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Source/build/scripts/name_utilities.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698