| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Generates Python source files from a mojom.Module.""" | 5 """Generates Python source files from a mojom.Module.""" |
| 6 | 6 |
| 7 import re | 7 import re |
| 8 from itertools import ifilter | 8 from itertools import ifilter |
| 9 | 9 |
| 10 import mojom.generate.generator as generator | 10 import mojom.generate.generator as generator |
| 11 import mojom.generate.module as mojom | 11 import mojom.generate.module as mojom |
| 12 from mojom.generate.template_expander import UseJinja | 12 from mojom.generate.template_expander import UseJinja |
| 13 | 13 |
| 14 _kind_to_type = { |
| 15 mojom.BOOL: "_descriptor.TYPE_BOOL", |
| 16 mojom.INT8: "_descriptor.TYPE_INT8", |
| 17 mojom.UINT8: "_descriptor.TYPE_UINT8", |
| 18 mojom.INT16: "_descriptor.TYPE_INT16", |
| 19 mojom.UINT16: "_descriptor.TYPE_UINT16", |
| 20 mojom.INT32: "_descriptor.TYPE_INT32", |
| 21 mojom.UINT32: "_descriptor.TYPE_UINT32", |
| 22 mojom.INT64: "_descriptor.TYPE_INT64", |
| 23 mojom.UINT64: "_descriptor.TYPE_UINT64", |
| 24 mojom.FLOAT: "_descriptor.TYPE_FLOAT", |
| 25 mojom.DOUBLE: "_descriptor.TYPE_DOUBLE", |
| 26 mojom.STRING: "_descriptor.TYPE_STRING", |
| 27 mojom.NULLABLE_STRING: "_descriptor.TYPE_NULLABLE_STRING", |
| 28 mojom.HANDLE: "_descriptor.TYPE_HANDLE", |
| 29 mojom.DCPIPE: "_descriptor.TYPE_HANDLE", |
| 30 mojom.DPPIPE: "_descriptor.TYPE_HANDLE", |
| 31 mojom.MSGPIPE: "_descriptor.TYPE_HANDLE", |
| 32 mojom.SHAREDBUFFER: "_descriptor.TYPE_HANDLE", |
| 33 mojom.NULLABLE_HANDLE: "_descriptor.TYPE_NULLABLE_HANDLE", |
| 34 mojom.NULLABLE_DCPIPE: "_descriptor.TYPE_NULLABLE_HANDLE", |
| 35 mojom.NULLABLE_DPPIPE: "_descriptor.TYPE_NULLABLE_HANDLE", |
| 36 mojom.NULLABLE_MSGPIPE: "_descriptor.TYPE_NULLABLE_HANDLE", |
| 37 mojom.NULLABLE_SHAREDBUFFER: "_descriptor.TYPE_NULLABLE_HANDLE", |
| 38 } |
| 39 |
| 14 | 40 |
| 15 def NameToComponent(name): | 41 def NameToComponent(name): |
| 16 # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar -> | 42 # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar -> |
| 17 # HTTP_Entry2_FooBar) | 43 # HTTP_Entry2_FooBar) |
| 18 name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name) | 44 name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name) |
| 19 # insert '_' between non upper and start of upper blocks (e.g., | 45 # insert '_' between non upper and start of upper blocks (e.g., |
| 20 # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar) | 46 # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar) |
| 21 name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name) | 47 name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name) |
| 22 return [x.lower() for x in name.split('_')] | 48 return [x.lower() for x in name.split('_')] |
| 23 | 49 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 41 if isinstance(element, mojom.EnumValue): | 67 if isinstance(element, mojom.EnumValue): |
| 42 return (GetNameForElement(element.enum) + '.' + | 68 return (GetNameForElement(element.enum) + '.' + |
| 43 ConstantStyle(element.name)) | 69 ConstantStyle(element.name)) |
| 44 if isinstance(element, (mojom.NamedValue, | 70 if isinstance(element, (mojom.NamedValue, |
| 45 mojom.Constant)): | 71 mojom.Constant)): |
| 46 return ConstantStyle(element.name) | 72 return ConstantStyle(element.name) |
| 47 raise Exception('Unexpected element: ' % element) | 73 raise Exception('Unexpected element: ' % element) |
| 48 | 74 |
| 49 def ExpressionToText(token): | 75 def ExpressionToText(token): |
| 50 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): | 76 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): |
| 51 # Both variable and enum constants are constructed like: | 77 return str(token.computed_value) |
| 52 # PythonModule[.Struct][.Enum].CONSTANT_NAME | |
| 53 name = [] | |
| 54 if token.imported_from: | |
| 55 name.append(token.imported_from['python_module']) | |
| 56 if token.parent_kind: | |
| 57 name.append(GetNameForElement(token.parent_kind)) | |
| 58 name.append(GetNameForElement(token)) | |
| 59 return '.'.join(name) | |
| 60 | 78 |
| 61 if isinstance(token, mojom.BuiltinValue): | 79 if isinstance(token, mojom.BuiltinValue): |
| 62 if token.value == 'double.INFINITY' or token.value == 'float.INFINITY': | 80 if token.value == 'double.INFINITY' or token.value == 'float.INFINITY': |
| 63 return 'float(\'inf\')'; | 81 return 'float(\'inf\')'; |
| 64 if (token.value == 'double.NEGATIVE_INFINITY' or | 82 if (token.value == 'double.NEGATIVE_INFINITY' or |
| 65 token.value == 'float.NEGATIVE_INFINITY'): | 83 token.value == 'float.NEGATIVE_INFINITY'): |
| 66 return 'float(\'-inf\')' | 84 return 'float(\'-inf\')' |
| 67 if token.value == 'double.NAN' or token.value == 'float.NAN': | 85 if token.value == 'double.NAN' or token.value == 'float.NAN': |
| 68 return 'float(\'nan\')'; | 86 return 'float(\'nan\')'; |
| 69 | 87 |
| 88 if token in ["true", "false"]: |
| 89 return str(token == "true") |
| 90 |
| 70 return token | 91 return token |
| 71 | 92 |
| 93 def GetStructClass(kind): |
| 94 name = [] |
| 95 if kind.imported_from: |
| 96 name.append(kind.imported_from['python_module']) |
| 97 name.append(GetNameForElement(kind)) |
| 98 return '.'.join(name) |
| 72 | 99 |
| 73 def ComputeConstantValues(module): | 100 def GetFieldType(kind, field=None): |
| 101 if mojom.IsAnyArrayKind(kind): |
| 102 arguments = [ GetFieldType(kind.kind) ] |
| 103 if mojom.IsNullableKind(kind): |
| 104 arguments.append("nullable=True") |
| 105 if mojom.IsFixedArrayKind(kind): |
| 106 arguments.append("length=%d" % kind.length) |
| 107 return "_descriptor.ArrayType(%s)" % ", ".join(arguments) |
| 108 |
| 109 if mojom.IsStructKind(kind): |
| 110 arguments = [ GetStructClass(kind) ] |
| 111 if mojom.IsNullableKind(kind): |
| 112 arguments.append("nullable=True") |
| 113 return "_descriptor.StructType(%s)" % ", ".join(arguments) |
| 114 |
| 115 if mojom.IsEnumKind(kind): |
| 116 return GetFieldType(mojom.INT32) |
| 117 |
| 118 return _kind_to_type.get(kind, "_descriptor.TYPE_NONE") |
| 119 |
| 120 def GetFieldDescriptor(packed_field): |
| 121 field = packed_field.field |
| 122 arguments = [ '\'%s\'' % field.name ] |
| 123 arguments.append(GetFieldType(field.kind, field)) |
| 124 arguments.append(str(packed_field.offset)) |
| 125 if field.kind == mojom.BOOL: |
| 126 arguments.append('bit_offset=%d' % packed_field.bit) |
| 127 if field.default: |
| 128 if mojom.IsStructKind(field.kind): |
| 129 arguments.append('default_value=True') |
| 130 else: |
| 131 arguments.append('default_value=%s' % ExpressionToText(field.default)) |
| 132 return '_descriptor.FieldDescriptor(%s)' % ', '.join(arguments) |
| 133 |
| 134 def ComputeStaticValues(module): |
| 74 in_progress = set() | 135 in_progress = set() |
| 75 computed = set() | 136 computed = set() |
| 76 | 137 |
| 77 def ResolveEnum(enum): | 138 def GetComputedValue(named_value): |
| 78 def GetComputedValue(enum_value): | 139 if isinstance(named_value, mojom.EnumValue): |
| 79 field = next(ifilter(lambda field: field.name == enum_value.name, | 140 field = next(ifilter(lambda field: field.name == named_value.name, |
| 80 enum_value.enum.fields), None) | 141 named_value.enum.fields), None) |
| 81 if not field: | 142 if not field: |
| 82 raise RuntimeError( | 143 raise RuntimeError( |
| 83 'Unable to get computed value for field %s of enum %s' % | 144 'Unable to get computed value for field %s of enum %s' % |
| 84 (enum_value.name, enum_value.enum.name)) | 145 (named_value.name, named_value.enum.name)) |
| 85 if field not in computed: | 146 if field not in computed: |
| 86 ResolveEnum(enum_value.enum) | 147 ResolveEnum(named_value.enum) |
| 87 return field.computed_value | 148 return field.computed_value |
| 149 elif isinstance(named_value, mojom.ConstantValue): |
| 150 ResolveConstant(named_value.constant) |
| 151 named_value.computed_value = named_value.constant.computed_value |
| 152 return named_value.computed_value |
| 153 else: |
| 154 print named_value |
| 88 | 155 |
| 156 def ResolveConstant(constant): |
| 157 if constant in computed: |
| 158 return |
| 159 if constant in in_progress: |
| 160 raise RuntimeError('Circular dependency for constant: %s' % constant.name) |
| 161 in_progress.add(constant) |
| 162 if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)): |
| 163 computed_value = GetComputedValue(constant.value) |
| 164 else: |
| 165 computed_value = ExpressionToText(constant.value) |
| 166 constant.computed_value = computed_value |
| 167 in_progress.remove(constant) |
| 168 computed.add(constant) |
| 169 |
| 170 def ResolveEnum(enum): |
| 89 def ResolveEnumField(enum, field, default_value): | 171 def ResolveEnumField(enum, field, default_value): |
| 90 if field in computed: | 172 if field in computed: |
| 91 return | 173 return |
| 92 if field in in_progress: | 174 if field in in_progress: |
| 93 raise RuntimeError('Circular dependency for enum: %s' % enum.name) | 175 raise RuntimeError('Circular dependency for enum: %s' % enum.name) |
| 94 in_progress.add(field) | 176 in_progress.add(field) |
| 95 if field.value: | 177 if field.value: |
| 96 if isinstance(field.value, mojom.EnumValue): | 178 if isinstance(field.value, mojom.EnumValue): |
| 97 computed_value = GetComputedValue(field.value) | 179 computed_value = GetComputedValue(field.value) |
| 98 elif isinstance(field.value, str): | 180 elif isinstance(field.value, str): |
| 99 computed_value = int(field.value, 0) | 181 computed_value = int(field.value, 0) |
| 100 else: | 182 else: |
| 101 raise RuntimeError('Unexpected value: %r' % field.value) | 183 raise RuntimeError('Unexpected value: %s' % field.value) |
| 102 else: | 184 else: |
| 103 computed_value = default_value | 185 computed_value = default_value |
| 104 field.computed_value = computed_value | 186 field.computed_value = computed_value |
| 105 in_progress.remove(field) | 187 in_progress.remove(field) |
| 106 computed.add(field) | 188 computed.add(field) |
| 107 | 189 |
| 108 current_value = 0 | 190 current_value = 0 |
| 109 for field in enum.fields: | 191 for field in enum.fields: |
| 110 ResolveEnumField(enum, field, current_value) | 192 ResolveEnumField(enum, field, current_value) |
| 111 current_value = field.computed_value + 1 | 193 current_value = field.computed_value + 1 |
| 112 | 194 |
| 195 for constant in module.constants: |
| 196 ResolveConstant(constant) |
| 197 |
| 113 for enum in module.enums: | 198 for enum in module.enums: |
| 114 ResolveEnum(enum) | 199 ResolveEnum(enum) |
| 115 | 200 |
| 116 for struct in module.structs: | 201 for struct in module.structs: |
| 202 for constant in struct.constants: |
| 203 ResolveConstant(constant) |
| 117 for enum in struct.enums: | 204 for enum in struct.enums: |
| 118 ResolveEnum(enum) | 205 ResolveEnum(enum) |
| 206 for field in struct.fields: |
| 207 if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)): |
| 208 field.default.computed_value = GetComputedValue(field.default) |
| 119 | 209 |
| 120 return module | 210 return module |
| 121 | 211 |
| 122 class Generator(generator.Generator): | 212 class Generator(generator.Generator): |
| 123 | 213 |
| 124 python_filters = { | 214 python_filters = { |
| 125 'expression_to_text': ExpressionToText, | 215 'expression_to_text': ExpressionToText, |
| 216 'field_descriptor': GetFieldDescriptor, |
| 126 'name': GetNameForElement, | 217 'name': GetNameForElement, |
| 127 } | 218 } |
| 128 | 219 |
| 129 @UseJinja('python_templates/module.py.tmpl', filters=python_filters) | 220 @UseJinja('python_templates/module.py.tmpl', filters=python_filters) |
| 130 def GeneratePythonModule(self): | 221 def GeneratePythonModule(self): |
| 131 return { | 222 return { |
| 132 'imports': self.GetImports(), | 223 'imports': self.GetImports(), |
| 133 'enums': self.module.enums, | 224 'enums': self.module.enums, |
| 134 'module': ComputeConstantValues(self.module), | 225 'module': ComputeStaticValues(self.module), |
| 135 'structs': self.GetStructs(), | 226 'structs': self.GetStructs(), |
| 136 } | 227 } |
| 137 | 228 |
| 138 def GenerateFiles(self, args): | 229 def GenerateFiles(self, args): |
| 139 self.Write(self.GeneratePythonModule(), | 230 self.Write(self.GeneratePythonModule(), |
| 140 '%s.py' % self.module.name.replace('.mojom', '_mojom')) | 231 '%s.py' % self.module.name.replace('.mojom', '_mojom')) |
| 141 | 232 |
| 142 def GetImports(self): | 233 def GetImports(self): |
| 143 for each in self.module.imports: | 234 for each in self.module.imports: |
| 144 each['python_module'] = each['module_name'].replace('.mojom', '_mojom') | 235 each['python_module'] = each['module_name'].replace('.mojom', '_mojom') |
| 145 return self.module.imports | 236 return self.module.imports |
| 146 | 237 |
| 147 def GetJinjaParameters(self): | 238 def GetJinjaParameters(self): |
| 148 return { | 239 return { |
| 149 'lstrip_blocks': True, | 240 'lstrip_blocks': True, |
| 150 'trim_blocks': True, | 241 'trim_blocks': True, |
| 151 } | 242 } |
| OLD | NEW |