| 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 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 class Field(object): | 24 class Field(object): |
| 25 """ | 25 """ |
| 26 The generated ComputedStyle object is made up of a series of Fields. | 26 The generated ComputedStyle object is made up of a series of Fields. |
| 27 Each Field has a name, size, type, etc, and a bunch of attributes to | 27 Each Field has a name, size, type, etc, and a bunch of attributes to |
| 28 determine which methods it will be used in. | 28 determine which methods it will be used in. |
| 29 | 29 |
| 30 A Field also has enough information to use any storage type in C++, such as | 30 A Field also has enough information to use any storage type in C++, such as |
| 31 regular member variables, or more complex storage like vectors or hashmaps. | 31 regular member variables, or more complex storage like vectors or hashmaps. |
| 32 Almost all properties will have at least one Field, often more than one. | 32 Almost all properties will have at least one Field, often more than one. |
| 33 | 33 |
| 34 Fields also fall into various families, which determine the logic that is | 34 Fields also fall into various roles, which determine the logic that is |
| 35 used to generate them. The available field families are: | 35 used to generate them. The available field roles are: |
| 36 - 'property', for fields that store CSS properties | 36 - 'property', for fields that store CSS properties |
| 37 - 'inherited_flag', for single-bit flags that store whether a property is | 37 - 'inherited_flag', for single-bit flags that store whether a property is |
| 38 inherited by this style or set explicitly | 38 inherited by this style or set explicitly |
| 39 - 'nonproperty', for fields that are not CSS properties |
| 39 """ | 40 """ |
| 40 | 41 |
| 41 # List of required attributes for a field which need to be passed in by | 42 # List of required attributes for a field which need to be passed in by |
| 42 # keyword arguments | 43 # keyword arguments. See CSSProperties.json5 for an explanation of each |
| 44 # attribute. |
| 43 REQUIRED_ATTRIBUTES = set([ | 45 REQUIRED_ATTRIBUTES = set([ |
| 44 # Name of field | 46 # Name of field |
| 45 'name', | 47 'name', |
| 46 # Name of property field is for | 48 # Name of property field is for |
| 47 'property_name', | 49 'property_name', |
| 48 # Internal field storage type (storage_type_path can be None) | 50 # Name of the type (e.g. EClear, int) |
| 49 'storage_type', | 51 'type_name', |
| 50 'storage_type_path', | 52 # Path to predefined class for overriding generated types. |
| 53 'field_type_path', |
| 54 # Affects how the field is generated (keyword, flag) |
| 55 'field_template', |
| 51 # Bits needed for storage | 56 # Bits needed for storage |
| 52 'size', | 57 'size', |
| 53 # Default value for field | 58 # Default value for field |
| 54 'default_value', | 59 'default_value', |
| 55 # Method names | 60 # Method names |
| 56 'getter_method_name', | 61 'getter_method_name', |
| 57 'setter_method_name', | 62 'setter_method_name', |
| 58 'initial_method_name', | 63 'initial_method_name', |
| 59 'resetter_method_name', | 64 'resetter_method_name', |
| 60 ]) | 65 ]) |
| 61 | 66 |
| 62 def __init__(self, field_family, **kwargs): | 67 def __init__(self, field_role, **kwargs): |
| 63 # Values common to all fields | 68 # Values common to all fields |
| 64 # Set attributes from the keyword arguments | 69 # Set attributes from the keyword arguments |
| 65 for attrib in Field.REQUIRED_ATTRIBUTES: | 70 for attrib in Field.REQUIRED_ATTRIBUTES: |
| 66 setattr(self, attrib, kwargs.pop(attrib)) | 71 setattr(self, attrib, kwargs.pop(attrib)) |
| 67 | 72 |
| 68 # Field family: one of these must be true | 73 # Field role: one of these must be true |
| 69 self.is_property = field_family == 'property' | 74 self.is_property = field_role == 'property' |
| 70 self.is_inherited_flag = field_family == 'inherited_flag' | 75 self.is_inherited_flag = field_role == 'inherited_flag' |
| 71 self.is_nonproperty = field_family == 'nonproperty' | 76 self.is_nonproperty = field_role == 'nonproperty' |
| 72 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c
ount(True) == 1, \ | 77 assert (self.is_property, self.is_inherited_flag, self.is_nonproperty).c
ount(True) == 1, \ |
| 73 'Field family has to be exactly one of: property, inherited_flag, no
nproperty' | 78 'Field role has to be exactly one of: property, inherited_flag, nonp
roperty' |
| 74 | 79 |
| 75 if self.is_property: | 80 if self.is_property: |
| 76 self.is_inherited = kwargs.pop('inherited') | 81 self.is_inherited = kwargs.pop('inherited') |
| 77 self.is_independent = kwargs.pop('independent') | 82 self.is_independent = kwargs.pop('independent') |
| 78 assert self.is_inherited or not self.is_independent, 'Only inherited
fields can be independent' | 83 assert self.is_inherited or not self.is_independent, 'Only inherited
fields can be independent' |
| 79 | 84 |
| 80 self.is_inherited_method_name = kwargs.pop('is_inherited_method_name
') | 85 self.is_inherited_method_name = kwargs.pop('is_inherited_method_name
') |
| 81 elif self.is_inherited_flag: | 86 elif self.is_inherited_flag: |
| 82 # Inherited flag-only fields | 87 # Inherited flag-only fields |
| 83 pass | 88 pass |
| 84 | 89 |
| 85 assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + st
r(kwargs) | 90 assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + st
r(kwargs) |
| 86 | 91 |
| 87 | 92 |
| 88 def _create_enums(properties): | 93 def _create_enums(properties): |
| 89 """ | 94 """ |
| 90 Returns a dictionary of enums to be generated, enum name -> [list of enum va
lues] | 95 Returns a dictionary of enums to be generated, enum name -> [list of enum va
lues] |
| 91 """ | 96 """ |
| 92 enums = {} | 97 enums = {} |
| 93 for property_ in properties: | 98 for property_ in properties: |
| 94 # Only generate enums for keyword properties that use the default field_
storage_type. | 99 # Only generate enums for keyword properties that use the default field_
type_path. |
| 95 if property_['keyword_only'] and property_['field_storage_type'] is None
: | 100 if property_['field_template'] == 'keyword' and property_['field_type_pa
th'] is None: |
| 96 enum_name = property_['type_name'] | 101 enum_name = property_['type_name'] |
| 97 # From the Blink style guide: Enum members should use InterCaps with
an initial capital letter. [names-enum-members] | 102 # From the Blink style guide: Enum members should use InterCaps with
an initial capital letter. [names-enum-members] |
| 98 enum_values = [('k' + camel_case(k)) for k in property_['keywords']] | 103 enum_values = [('k' + camel_case(k)) for k in property_['keywords']] |
| 99 | 104 |
| 100 if enum_name in enums: | 105 if enum_name in enums: |
| 101 # There's an enum with the same name, check if the enum values a
re the same | 106 # There's an enum with the same name, check if the enum values a
re the same |
| 102 assert set(enums[enum_name]) == set(enum_values), \ | 107 assert set(enums[enum_name]) == set(enum_values), \ |
| 103 ("'" + property_['name'] + "' can't have type_name '" + enum
_name + "' " | 108 ("'" + property_['name'] + "' can't have type_name '" + enum
_name + "' " |
| 104 "because it was used by a previous property, but with a dif
ferent set of keywords. " | 109 "because it was used by a previous property, but with a dif
ferent set of keywords. " |
| 105 "Either give it a different name or ensure the keywords are
the same.") | 110 "Either give it a different name or ensure the keywords are
the same.") |
| 106 | 111 |
| 107 enums[enum_name] = enum_values | 112 enums[enum_name] = enum_values |
| 108 | 113 |
| 109 return enums | 114 return enums |
| 110 | 115 |
| 111 | 116 |
| 112 def _create_property_field(property_): | 117 def _create_property_field(property_): |
| 113 """ | 118 """ |
| 114 Create a property field from a CSS property and return the Field object. | 119 Create a property field from a CSS property and return the Field object. |
| 115 """ | 120 """ |
| 116 property_name = property_['name_for_methods'] | 121 property_name = property_['name_for_methods'] |
| 117 property_name_lower = lower_first(property_name) | 122 property_name_lower = lower_first(property_name) |
| 118 | 123 |
| 119 # From the Blink style guide: Other data members should be prefixed by "m_".
[names-data-members] | 124 # From the Blink style guide: Other data members should be prefixed by "m_".
[names-data-members] |
| 120 field_name = 'm_' + property_name_lower | 125 field_name = 'm_' + property_name_lower |
| 121 bits_needed = math.log(len(property_['keywords']), 2) | 126 bits_needed = math.log(len(property_['keywords']), 2) # TODO: implement for
non-enums |
| 122 | 127 |
| 123 # Separate the type path from the type name, if specified. | 128 # Separate the type path from the type name, if specified. |
| 124 if property_['field_storage_type']: | 129 if property_['field_type_path']: |
| 125 type_path = property_['field_storage_type'] | 130 field_type_path = property_['field_type_path'] |
| 126 type_name = type_path.split('/')[-1] | 131 type_name = field_type_path.split('/')[-1] |
| 127 else: | 132 else: |
| 133 field_type_path = None |
| 128 type_name = property_['type_name'] | 134 type_name = property_['type_name'] |
| 129 type_path = None | |
| 130 | 135 |
| 131 # For now, the getter name should match the field name. Later, getter names | 136 # For now, the getter name should match the field name. Later, getter names |
| 132 # will start with an uppercase letter, so if they conflict with the type nam
e, | 137 # will start with an uppercase letter, so if they conflict with the type nam
e, |
| 133 # add 'get' to the front. | 138 # add 'get' to the front. |
| 134 getter_method_name = property_name_lower | 139 getter_method_name = property_name_lower |
| 135 if type_name == property_name: | 140 if type_name == property_name: |
| 136 getter_method_name = 'get' + property_name | 141 getter_method_name = 'get' + property_name |
| 137 | 142 |
| 138 assert property_['initial_keyword'] is not None, \ | 143 assert property_['initial_keyword'] is not None, \ |
| 139 ('MakeComputedStyleBase requires an initial keyword for keyword_only val
ues, none specified ' | 144 ('MakeComputedStyleBase requires an initial keyword for keyword fields,
none specified ' |
| 140 'for property ' + property_['name']) | 145 'for property ' + property_['name']) |
| 141 default_value = type_name + '::k' + camel_case(property_['initial_keyword']) | 146 default_value = type_name + '::k' + camel_case(property_['initial_keyword']) |
| 142 | 147 |
| 143 # Add the property itself as a member variable. | |
| 144 return Field( | 148 return Field( |
| 145 'property', | 149 'property', |
| 146 name=field_name, | 150 name=field_name, |
| 147 property_name=property_['name'], | 151 property_name=property_['name'], |
| 148 inherited=property_['inherited'], | 152 inherited=property_['inherited'], |
| 149 independent=property_['independent'], | 153 independent=property_['independent'], |
| 150 storage_type=type_name, | 154 type_name=type_name, |
| 151 storage_type_path=type_path, | 155 field_type_path=field_type_path, |
| 156 field_template=property_['field_template'], |
| 152 size=int(math.ceil(bits_needed)), | 157 size=int(math.ceil(bits_needed)), |
| 153 default_value=default_value, | 158 default_value=default_value, |
| 154 getter_method_name=getter_method_name, | 159 getter_method_name=getter_method_name, |
| 155 setter_method_name='set' + property_name, | 160 setter_method_name='set' + property_name, |
| 156 initial_method_name='initial' + property_name, | 161 initial_method_name='initial' + property_name, |
| 157 resetter_method_name='reset' + property_name, | 162 resetter_method_name='reset' + property_name, |
| 158 is_inherited_method_name=property_name_lower + 'IsInherited', | 163 is_inherited_method_name=property_name_lower + 'IsInherited', |
| 159 ) | 164 ) |
| 160 | 165 |
| 161 | 166 |
| 162 def _create_inherited_flag_field(property_): | 167 def _create_inherited_flag_field(property_): |
| 163 """ | 168 """ |
| 164 Create the field used for an inheritance fast path from an independent CSS p
roperty, | 169 Create the field used for an inheritance fast path from an independent CSS p
roperty, |
| 165 and return the Field object. | 170 and return the Field object. |
| 166 """ | 171 """ |
| 167 property_name = property_['name_for_methods'] | 172 property_name = property_['name_for_methods'] |
| 168 property_name_lower = lower_first(property_name) | 173 property_name_lower = lower_first(property_name) |
| 169 | 174 |
| 170 field_name_suffix_upper = property_name + 'IsInherited' | 175 field_name_suffix_upper = property_name + 'IsInherited' |
| 171 field_name_suffix_lower = property_name_lower + 'IsInherited' | 176 field_name_suffix_lower = property_name_lower + 'IsInherited' |
| 172 | 177 |
| 173 return Field( | 178 return Field( |
| 174 'inherited_flag', | 179 'inherited_flag', |
| 175 name='m_' + field_name_suffix_lower, | 180 name='m_' + field_name_suffix_lower, |
| 176 property_name=property_['name'], | 181 property_name=property_['name'], |
| 177 storage_type='bool', | 182 type_name='bool', |
| 178 storage_type_path=None, | 183 field_type_path=None, |
| 184 field_template='flag', |
| 179 size=1, | 185 size=1, |
| 180 default_value='true', | 186 default_value='true', |
| 181 getter_method_name=field_name_suffix_lower, | 187 getter_method_name=field_name_suffix_lower, |
| 182 setter_method_name='set' + field_name_suffix_upper, | 188 setter_method_name='set' + field_name_suffix_upper, |
| 183 initial_method_name='initial' + field_name_suffix_upper, | 189 initial_method_name='initial' + field_name_suffix_upper, |
| 184 resetter_method_name='reset' + field_name_suffix_upper, | 190 resetter_method_name='reset' + field_name_suffix_upper, |
| 185 ) | 191 ) |
| 186 | 192 |
| 187 | 193 |
| 188 def _create_nonproperty_field(field_name): | 194 def _create_nonproperty_field(field_name): |
| 189 """ | 195 """ |
| 190 Create a nonproperty field from its name and return the Field object. | 196 Create a nonproperty field from its name and return the Field object. |
| 191 """ | 197 """ |
| 192 member_name = 'm_' + field_name | 198 member_name = 'm_' + field_name |
| 193 field_name_upper = upper_first_letter(field_name) | 199 field_name_upper = upper_first_letter(field_name) |
| 194 | 200 |
| 195 return Field( | 201 return Field( |
| 196 'nonproperty', | 202 'nonproperty', |
| 197 name=member_name, | 203 name=member_name, |
| 198 property_name=field_name, | 204 property_name=field_name, |
| 199 storage_type='bool', | 205 type_name='bool', |
| 200 storage_type_path=None, | 206 field_type_path=None, |
| 207 field_template='flag', |
| 201 size=1, | 208 size=1, |
| 202 default_value='false', | 209 default_value='false', |
| 203 getter_method_name=field_name, | 210 getter_method_name=field_name, |
| 204 setter_method_name='set' + field_name_upper, | 211 setter_method_name='set' + field_name_upper, |
| 205 initial_method_name='initial' + field_name_upper, | 212 initial_method_name='initial' + field_name_upper, |
| 206 resetter_method_name='reset' + field_name_upper, | 213 resetter_method_name='reset' + field_name_upper, |
| 207 ) | 214 ) |
| 208 | 215 |
| 209 | 216 |
| 210 def _create_fields(properties): | 217 def _create_fields(properties): |
| 211 """ | 218 """ |
| 212 Create ComputedStyle fields from CSS properties and return a list of Field o
bjects. | 219 Create ComputedStyle fields from CSS properties and return a list of Field o
bjects. |
| 213 """ | 220 """ |
| 214 fields = [] | 221 fields = [] |
| 215 for property_ in properties: | 222 for property_ in properties: |
| 216 # Keywords only means we generate an enum field. | 223 # Only generate properties that have a field template |
| 217 if property_['keyword_only']: | 224 if property_['field_template'] is not None: |
| 218 # If the property is independent, add the single-bit sized isInherit
ed flag | 225 # If the property is independent, add the single-bit sized isInherit
ed flag |
| 219 # to the list of Fields as well. | 226 # to the list of Fields as well. |
| 220 if property_['independent']: | 227 if property_['independent']: |
| 221 fields.append(_create_inherited_flag_field(property_)) | 228 fields.append(_create_inherited_flag_field(property_)) |
| 222 | 229 |
| 223 fields.append(_create_property_field(property_)) | 230 fields.append(_create_property_field(property_)) |
| 224 | 231 |
| 225 for field_name in NONPROPERTY_FIELDS: | 232 for field_name in NONPROPERTY_FIELDS: |
| 226 fields.append(_create_nonproperty_field(field_name)) | 233 fields.append(_create_nonproperty_field(field_name)) |
| 227 | 234 |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 def generate_base_computed_style_constants(self): | 335 def generate_base_computed_style_constants(self): |
| 329 return { | 336 return { |
| 330 'properties': self._properties, | 337 'properties': self._properties, |
| 331 'enums': self._generated_enums, | 338 'enums': self._generated_enums, |
| 332 'fields': self._fields, | 339 'fields': self._fields, |
| 333 'expected_total_field_bytes': self._expected_total_field_bytes, | 340 'expected_total_field_bytes': self._expected_total_field_bytes, |
| 334 } | 341 } |
| 335 | 342 |
| 336 if __name__ == '__main__': | 343 if __name__ == '__main__': |
| 337 json5_generator.Maker(ComputedStyleBaseWriter).main() | 344 json5_generator.Maker(ComputedStyleBaseWriter).main() |
| OLD | NEW |