| OLD | NEW |
| (Empty) |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 # TODO(vtl): "data" is a pretty vague name. Rename it? | |
| 6 | |
| 7 import copy | |
| 8 | |
| 9 import module as mojom | |
| 10 | |
| 11 # This module provides a mechanism to turn mojom Modules to dictionaries and | |
| 12 # back again. This can be used to persist a mojom Module created progromatically | |
| 13 # or to read a dictionary from code or a file. | |
| 14 # Example: | |
| 15 # test_dict = { | |
| 16 # 'name': 'test', | |
| 17 # 'namespace': 'testspace', | |
| 18 # 'structs': [{ | |
| 19 # 'name': 'teststruct', | |
| 20 # 'fields': [ | |
| 21 # {'name': 'testfield1', 'kind': 'i32'}, | |
| 22 # {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}], | |
| 23 # 'interfaces': [{ | |
| 24 # 'name': 'Server', | |
| 25 # 'methods': [{ | |
| 26 # 'name': 'Foo', | |
| 27 # 'parameters': [{ | |
| 28 # 'name': 'foo', 'kind': 'i32'}, | |
| 29 # {'name': 'bar', 'kind': 'a:x:teststruct'}], | |
| 30 # 'ordinal': 42}]}] | |
| 31 # } | |
| 32 # test_module = data.ModuleFromData(test_dict) | |
| 33 | |
| 34 # Used to create a subclass of str that supports sorting by index, to make | |
| 35 # pretty printing maintain the order. | |
| 36 def istr(index, string): | |
| 37 class IndexedString(str): | |
| 38 def __lt__(self, other): | |
| 39 return self.__index__ < other.__index__ | |
| 40 | |
| 41 rv = IndexedString(string) | |
| 42 rv.__index__ = index | |
| 43 return rv | |
| 44 | |
| 45 builtin_values = frozenset([ | |
| 46 "double.INFINITY", | |
| 47 "double.NEGATIVE_INFINITY", | |
| 48 "double.NAN", | |
| 49 "float.INFINITY", | |
| 50 "float.NEGATIVE_INFINITY", | |
| 51 "float.NAN"]) | |
| 52 | |
| 53 def IsBuiltinValue(value): | |
| 54 return value in builtin_values | |
| 55 | |
| 56 def LookupKind(kinds, spec, scope): | |
| 57 """Tries to find which Kind a spec refers to, given the scope in which its | |
| 58 referenced. Starts checking from the narrowest scope to most general. For | |
| 59 example, given a struct field like | |
| 60 Foo.Bar x; | |
| 61 Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner | |
| 62 type 'Bar' in the struct 'Foo' in the current namespace. | |
| 63 | |
| 64 |scope| is a tuple that looks like (namespace, struct/interface), referring | |
| 65 to the location where the type is referenced.""" | |
| 66 if spec.startswith('x:'): | |
| 67 name = spec[2:] | |
| 68 for i in xrange(len(scope), -1, -1): | |
| 69 test_spec = 'x:' | |
| 70 if i > 0: | |
| 71 test_spec += '.'.join(scope[:i]) + '.' | |
| 72 test_spec += name | |
| 73 kind = kinds.get(test_spec) | |
| 74 if kind: | |
| 75 return kind | |
| 76 | |
| 77 return kinds.get(spec) | |
| 78 | |
| 79 def LookupValue(values, name, scope, kind): | |
| 80 """Like LookupKind, but for constant values.""" | |
| 81 # If the type is an enum, the value can be specified as a qualified name, in | |
| 82 # which case the form EnumName.ENUM_VALUE must be used. We use the presence | |
| 83 # of a '.' in the requested name to identify this. Otherwise, we prepend the | |
| 84 # enum name. | |
| 85 if isinstance(kind, mojom.Enum) and '.' not in name: | |
| 86 name = '%s.%s' % (kind.spec.split(':', 1)[1], name) | |
| 87 for i in reversed(xrange(len(scope) + 1)): | |
| 88 test_spec = '.'.join(scope[:i]) | |
| 89 if test_spec: | |
| 90 test_spec += '.' | |
| 91 test_spec += name | |
| 92 value = values.get(test_spec) | |
| 93 if value: | |
| 94 return value | |
| 95 | |
| 96 return values.get(name) | |
| 97 | |
| 98 def FixupExpression(module, value, scope, kind): | |
| 99 """Translates an IDENTIFIER into a built-in value or structured NamedValue | |
| 100 object.""" | |
| 101 if isinstance(value, tuple) and value[0] == 'IDENTIFIER': | |
| 102 # Allow user defined values to shadow builtins. | |
| 103 result = LookupValue(module.values, value[1], scope, kind) | |
| 104 if result: | |
| 105 if isinstance(result, tuple): | |
| 106 raise Exception('Unable to resolve expression: %r' % value[1]) | |
| 107 return result | |
| 108 if IsBuiltinValue(value[1]): | |
| 109 return mojom.BuiltinValue(value[1]) | |
| 110 return value | |
| 111 | |
| 112 def KindToData(kind): | |
| 113 return kind.spec | |
| 114 | |
| 115 def KindFromData(kinds, data, scope): | |
| 116 kind = LookupKind(kinds, data, scope) | |
| 117 if kind: | |
| 118 return kind | |
| 119 | |
| 120 if data.startswith('?'): | |
| 121 kind = KindFromData(kinds, data[1:], scope).MakeNullableKind() | |
| 122 elif data.startswith('a:'): | |
| 123 kind = mojom.Array(KindFromData(kinds, data[2:], scope)) | |
| 124 elif data.startswith('a'): | |
| 125 colon = data.find(':') | |
| 126 length = int(data[1:colon]) | |
| 127 kind = mojom.Array(KindFromData(kinds, data[colon+1:], scope), length) | |
| 128 elif data.startswith('r:'): | |
| 129 kind = mojom.InterfaceRequest(KindFromData(kinds, data[2:], scope)) | |
| 130 elif data.startswith('m['): | |
| 131 # Isolate the two types from their brackets. | |
| 132 | |
| 133 # It is not allowed to use map as key, so there shouldn't be nested ']'s | |
| 134 # inside the key type spec. | |
| 135 key_end = data.find(']') | |
| 136 assert key_end != -1 and key_end < len(data) - 1 | |
| 137 assert data[key_end+1] == '[' and data[-1] == ']' | |
| 138 | |
| 139 first_kind = data[2:key_end] | |
| 140 second_kind = data[key_end+2:-1] | |
| 141 | |
| 142 kind = mojom.Map(KindFromData(kinds, first_kind, scope), | |
| 143 KindFromData(kinds, second_kind, scope)) | |
| 144 else: | |
| 145 kind = mojom.Kind(data) | |
| 146 | |
| 147 kinds[data] = kind | |
| 148 return kind | |
| 149 | |
| 150 def KindFromImport(original_kind, imported_from): | |
| 151 """Used with 'import module' - clones the kind imported from the given | |
| 152 module's namespace. Only used with Structs, Unions, Interfaces and Enums.""" | |
| 153 kind = copy.copy(original_kind) | |
| 154 # |shared_definition| is used to store various properties (see | |
| 155 # |AddSharedProperty()| in module.py), including |imported_from|. We don't | |
| 156 # want the copy to share these with the original, so copy it if necessary. | |
| 157 if hasattr(original_kind, 'shared_definition'): | |
| 158 kind.shared_definition = copy.copy(original_kind.shared_definition) | |
| 159 kind.imported_from = imported_from | |
| 160 return kind | |
| 161 | |
| 162 def ImportFromData(module, data): | |
| 163 import_module = data['module'] | |
| 164 | |
| 165 import_item = {} | |
| 166 import_item['module_name'] = import_module.name | |
| 167 import_item['namespace'] = import_module.namespace | |
| 168 import_item['module'] = import_module | |
| 169 | |
| 170 # Copy the struct kinds from our imports into the current module. | |
| 171 importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface) | |
| 172 for kind in import_module.kinds.itervalues(): | |
| 173 if (isinstance(kind, importable_kinds) and | |
| 174 kind.imported_from is None): | |
| 175 kind = KindFromImport(kind, import_item) | |
| 176 module.kinds[kind.spec] = kind | |
| 177 # Ditto for values. | |
| 178 for value in import_module.values.itervalues(): | |
| 179 if value.imported_from is None: | |
| 180 # Values don't have shared definitions (since they're not nullable), so no | |
| 181 # need to do anything special. | |
| 182 value = copy.copy(value) | |
| 183 value.imported_from = import_item | |
| 184 module.values[value.GetSpec()] = value | |
| 185 | |
| 186 return import_item | |
| 187 | |
| 188 def StructToData(struct): | |
| 189 return { | |
| 190 istr(0, 'name'): struct.name, | |
| 191 istr(1, 'fields'): map(FieldToData, struct.fields) | |
| 192 } | |
| 193 | |
| 194 def StructFromData(module, data): | |
| 195 struct = mojom.Struct(module=module) | |
| 196 struct.name = data['name'] | |
| 197 struct.attributes = data['attributes'] | |
| 198 struct.spec = 'x:' + module.namespace + '.' + struct.name | |
| 199 module.kinds[struct.spec] = struct | |
| 200 struct.enums = map(lambda enum: | |
| 201 EnumFromData(module, enum, struct), data['enums']) | |
| 202 struct.constants = map(lambda constant: | |
| 203 ConstantFromData(module, constant, struct), data['constants']) | |
| 204 # Stash fields data here temporarily. | |
| 205 struct.fields_data = data['fields'] | |
| 206 return struct | |
| 207 | |
| 208 def UnionToData(union): | |
| 209 return { | |
| 210 istr(0, 'name'): union.name, | |
| 211 istr(1, 'fields'): map(FieldToData, union.fields) | |
| 212 } | |
| 213 | |
| 214 def UnionFromData(module, data): | |
| 215 union = mojom.Union(module=module) | |
| 216 union.name = data['name'] | |
| 217 union.spec = 'x:' + module.namespace + '.' + union.name | |
| 218 module.kinds[union.spec] = union | |
| 219 # Stash fields data here temporarily. | |
| 220 union.fields_data = data['fields'] | |
| 221 return union | |
| 222 | |
| 223 def FieldToData(field): | |
| 224 data = { | |
| 225 istr(0, 'name'): field.name, | |
| 226 istr(1, 'kind'): KindToData(field.kind) | |
| 227 } | |
| 228 if field.ordinal != None: | |
| 229 data[istr(2, 'ordinal')] = field.ordinal | |
| 230 if field.default != None: | |
| 231 data[istr(3, 'default')] = field.default | |
| 232 return data | |
| 233 | |
| 234 def FieldFromData(module, data, struct): | |
| 235 field = mojom.Field() | |
| 236 field.name = data['name'] | |
| 237 field.kind = KindFromData( | |
| 238 module.kinds, data['kind'], (module.namespace, struct.name)) | |
| 239 field.ordinal = data.get('ordinal') | |
| 240 field.default = FixupExpression( | |
| 241 module, data.get('default'), (module.namespace, struct.name), field.kind) | |
| 242 return field | |
| 243 | |
| 244 def ParameterToData(parameter): | |
| 245 data = { | |
| 246 istr(0, 'name'): parameter.name, | |
| 247 istr(1, 'kind'): parameter.kind.spec | |
| 248 } | |
| 249 if parameter.ordinal != None: | |
| 250 data[istr(2, 'ordinal')] = parameter.ordinal | |
| 251 if parameter.default != None: | |
| 252 data[istr(3, 'default')] = parameter.default | |
| 253 return data | |
| 254 | |
| 255 def ParameterFromData(module, data, interface): | |
| 256 parameter = mojom.Parameter() | |
| 257 parameter.name = data['name'] | |
| 258 parameter.kind = KindFromData( | |
| 259 module.kinds, data['kind'], (module.namespace, interface.name)) | |
| 260 parameter.ordinal = data.get('ordinal') | |
| 261 parameter.default = data.get('default') | |
| 262 return parameter | |
| 263 | |
| 264 def MethodToData(method): | |
| 265 data = { | |
| 266 istr(0, 'name'): method.name, | |
| 267 istr(1, 'parameters'): map(ParameterToData, method.parameters) | |
| 268 } | |
| 269 if method.ordinal != None: | |
| 270 data[istr(2, 'ordinal')] = method.ordinal | |
| 271 if method.response_parameters != None: | |
| 272 data[istr(3, 'response_parameters')] = map( | |
| 273 ParameterToData, method.response_parameters) | |
| 274 return data | |
| 275 | |
| 276 def MethodFromData(module, data, interface): | |
| 277 method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal')) | |
| 278 method.default = data.get('default') | |
| 279 method.parameters = map(lambda parameter: | |
| 280 ParameterFromData(module, parameter, interface), data['parameters']) | |
| 281 if data.has_key('response_parameters'): | |
| 282 method.response_parameters = map( | |
| 283 lambda parameter: ParameterFromData(module, parameter, interface), | |
| 284 data['response_parameters']) | |
| 285 return method | |
| 286 | |
| 287 def InterfaceToData(interface): | |
| 288 return { | |
| 289 istr(0, 'name'): interface.name, | |
| 290 istr(1, 'client'): interface.client, | |
| 291 istr(2, 'methods'): map(MethodToData, interface.methods) | |
| 292 } | |
| 293 | |
| 294 def InterfaceFromData(module, data): | |
| 295 interface = mojom.Interface(module=module) | |
| 296 interface.name = data['name'] | |
| 297 interface.spec = 'x:' + module.namespace + '.' + interface.name | |
| 298 interface.client = data['client'] if data.has_key('client') else None | |
| 299 module.kinds[interface.spec] = interface | |
| 300 interface.enums = map(lambda enum: | |
| 301 EnumFromData(module, enum, interface), data['enums']) | |
| 302 interface.constants = map(lambda constant: | |
| 303 ConstantFromData(module, constant, interface), data['constants']) | |
| 304 # Stash methods data here temporarily. | |
| 305 interface.methods_data = data['methods'] | |
| 306 return interface | |
| 307 | |
| 308 def EnumFieldFromData(module, enum, data, parent_kind): | |
| 309 field = mojom.EnumField() | |
| 310 field.name = data['name'] | |
| 311 # TODO(mpcomplete): FixupExpression should be done in the second pass, | |
| 312 # so constants and enums can refer to each other. | |
| 313 # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or | |
| 314 # vice versa? | |
| 315 if parent_kind: | |
| 316 field.value = FixupExpression( | |
| 317 module, data['value'], (module.namespace, parent_kind.name), enum) | |
| 318 else: | |
| 319 field.value = FixupExpression( | |
| 320 module, data['value'], (module.namespace, ), enum) | |
| 321 value = mojom.EnumValue(module, enum, field) | |
| 322 module.values[value.GetSpec()] = value | |
| 323 return field | |
| 324 | |
| 325 def EnumFromData(module, data, parent_kind): | |
| 326 enum = mojom.Enum(module=module) | |
| 327 enum.name = data['name'] | |
| 328 name = enum.name | |
| 329 if parent_kind: | |
| 330 name = parent_kind.name + '.' + name | |
| 331 enum.spec = 'x:%s.%s' % (module.namespace, name) | |
| 332 enum.parent_kind = parent_kind | |
| 333 | |
| 334 enum.fields = map( | |
| 335 lambda field: EnumFieldFromData(module, enum, field, parent_kind), | |
| 336 data['fields']) | |
| 337 module.kinds[enum.spec] = enum | |
| 338 return enum | |
| 339 | |
| 340 def ConstantFromData(module, data, parent_kind): | |
| 341 constant = mojom.Constant() | |
| 342 constant.name = data['name'] | |
| 343 if parent_kind: | |
| 344 scope = (module.namespace, parent_kind.name) | |
| 345 else: | |
| 346 scope = (module.namespace, ) | |
| 347 # TODO(mpcomplete): maybe we should only support POD kinds. | |
| 348 constant.kind = KindFromData(module.kinds, data['kind'], scope) | |
| 349 constant.value = FixupExpression(module, data.get('value'), scope, None) | |
| 350 | |
| 351 value = mojom.ConstantValue(module, parent_kind, constant) | |
| 352 module.values[value.GetSpec()] = value | |
| 353 return constant | |
| 354 | |
| 355 def ModuleToData(module): | |
| 356 return { | |
| 357 istr(0, 'name'): module.name, | |
| 358 istr(1, 'namespace'): module.namespace, | |
| 359 istr(2, 'structs'): map(StructToData, module.structs), | |
| 360 istr(3, 'interfaces'): map(InterfaceToData, module.interfaces), | |
| 361 istr(4, 'unions'): map(UnionToData, module.unions), | |
| 362 } | |
| 363 | |
| 364 def ModuleFromData(data): | |
| 365 module = mojom.Module() | |
| 366 module.kinds = {} | |
| 367 for kind in mojom.PRIMITIVES: | |
| 368 module.kinds[kind.spec] = kind | |
| 369 | |
| 370 module.values = {} | |
| 371 | |
| 372 module.name = data['name'] | |
| 373 module.namespace = data['namespace'] | |
| 374 module.attributes = data['attributes'] | |
| 375 # Imports must come first, because they add to module.kinds which is used | |
| 376 # by by the others. | |
| 377 module.imports = map( | |
| 378 lambda import_data: ImportFromData(module, import_data), | |
| 379 data['imports']) | |
| 380 | |
| 381 # First pass collects kinds. | |
| 382 module.enums = map( | |
| 383 lambda enum: EnumFromData(module, enum, None), data['enums']) | |
| 384 module.structs = map( | |
| 385 lambda struct: StructFromData(module, struct), data['structs']) | |
| 386 module.unions = map( | |
| 387 lambda union: UnionFromData(module, struct), data.get('unions', [])) | |
| 388 module.interfaces = map( | |
| 389 lambda interface: InterfaceFromData(module, interface), | |
| 390 data['interfaces']) | |
| 391 module.constants = map( | |
| 392 lambda constant: ConstantFromData(module, constant, None), | |
| 393 data['constants']) | |
| 394 | |
| 395 # Second pass expands fields and methods. This allows fields and parameters | |
| 396 # to refer to kinds defined anywhere in the mojom. | |
| 397 for struct in module.structs: | |
| 398 struct.fields = map(lambda field: | |
| 399 FieldFromData(module, field, struct), struct.fields_data) | |
| 400 del struct.fields_data | |
| 401 for union in module.unions: | |
| 402 union.fields = map(lambda field: | |
| 403 FieldFromData(module, field, union), union.fields_data) | |
| 404 del union.fields_data | |
| 405 for interface in module.interfaces: | |
| 406 interface.methods = map(lambda method: | |
| 407 MethodFromData(module, method, interface), interface.methods_data) | |
| 408 del interface.methods_data | |
| 409 | |
| 410 return module | |
| 411 | |
| 412 def OrderedModuleFromData(data): | |
| 413 module = ModuleFromData(data) | |
| 414 for interface in module.interfaces: | |
| 415 next_ordinal = 0 | |
| 416 for method in interface.methods: | |
| 417 if method.ordinal is None: | |
| 418 method.ordinal = next_ordinal | |
| 419 next_ordinal = method.ordinal + 1 | |
| 420 return module | |
| OLD | NEW |