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 import mojom | |
6 import copy | |
7 | |
8 # mojom_data provides a mechanism to turn mojom Modules to dictionaries and | |
9 # back again. This can be used to persist a mojom Module created progromatically | |
10 # or to read a dictionary from code or a file. | |
11 # Example: | |
12 # test_dict = { | |
13 # 'name': 'test', | |
14 # 'namespace': 'testspace', | |
15 # 'structs': [{ | |
16 # 'name': 'teststruct', | |
17 # 'fields': [ | |
18 # {'name': 'testfield1', 'kind': 'i32'}, | |
19 # {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}], | |
20 # 'interfaces': [{ | |
21 # 'name': 'Server', | |
22 # 'methods': [{ | |
23 # 'name': 'Foo', | |
24 # 'parameters': [{ | |
25 # 'name': 'foo', 'kind': 'i32'}, | |
26 # {'name': 'bar', 'kind': 'a:x:teststruct'}], | |
27 # 'ordinal': 42}]}] | |
28 # } | |
29 # test_module = mojom_data.ModuleFromData(test_dict) | |
30 | |
31 # Used to create a subclass of str that supports sorting by index, to make | |
32 # pretty printing maintain the order. | |
33 def istr(index, string): | |
34 class IndexedString(str): | |
35 def __lt__(self, other): | |
36 return self.__index__ < other.__index__ | |
37 | |
38 istr = IndexedString(string) | |
39 istr.__index__ = index | |
40 return istr | |
41 | |
42 def LookupKind(kinds, spec, scope): | |
43 """Tries to find which Kind a spec refers to, given the scope in which its | |
44 referenced. Starts checking from the narrowest scope to most general. For | |
45 example, given a struct field like | |
46 Foo.Bar x; | |
47 Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner | |
48 type 'Bar' in the struct 'Foo' in the current namespace. | |
49 | |
50 |scope| is a tuple that looks like (namespace, struct/interface), referring | |
51 to the location where the type is referenced.""" | |
52 if spec.startswith('x:'): | |
53 name = spec[2:] | |
54 for i in xrange(len(scope), -1, -1): | |
55 test_spec = 'x:' | |
56 if i > 0: | |
57 test_spec += '.'.join(scope[:i]) + '.' | |
58 test_spec += name | |
59 kind = kinds.get(test_spec) | |
60 if kind: | |
61 return kind | |
62 | |
63 return kinds.get(spec) | |
64 | |
65 def LookupConstant(constants, name, scope): | |
66 """Like LookupKind, but for constants.""" | |
67 for i in xrange(len(scope), -1, -1): | |
68 if i > 0: | |
69 test_spec = '.'.join(scope[:i]) + '.' | |
70 test_spec += name | |
71 constant = constants.get(test_spec) | |
72 if constant: | |
73 return constant | |
74 | |
75 return constants.get(name) | |
76 | |
77 def KindToData(kind): | |
78 return kind.spec | |
79 | |
80 def KindFromData(kinds, data, scope): | |
81 kind = LookupKind(kinds, data, scope) | |
82 if kind: | |
83 return kind | |
84 if data.startswith('a:'): | |
85 kind = mojom.Array() | |
86 kind.kind = KindFromData(kinds, data[2:], scope) | |
87 else: | |
88 kind = mojom.Kind() | |
89 kind.spec = data | |
90 kinds[data] = kind | |
91 return kind | |
92 | |
93 def KindFromImport(original_kind, imported_from): | |
94 """Used with 'import module' - clones the kind imported from the | |
95 given module's namespace. Only used with Structs and Enums.""" | |
96 kind = copy.deepcopy(original_kind) | |
97 kind.imported_from = imported_from | |
98 return kind | |
99 | |
100 def ImportFromData(module, data): | |
101 import_module = data['module'] | |
102 | |
103 import_item = {} | |
104 import_item['module_name'] = import_module.name | |
105 import_item['namespace'] = import_module.namespace | |
106 import_item['module'] = import_module | |
107 | |
108 # Copy the struct kinds from our imports into the current module. | |
109 for kind in import_module.kinds.itervalues(): | |
110 if (isinstance(kind, (mojom.Struct, mojom.Enum)) and | |
111 kind.imported_from is None): | |
112 kind = KindFromImport(kind, import_item) | |
113 module.kinds[kind.spec] = kind | |
114 # Ditto for constants. | |
115 for constant in import_module.constants.itervalues(): | |
116 if constant.imported_from is None: | |
117 constant = copy.deepcopy(constant) | |
118 constant.imported_from = import_item | |
119 module.constants[constant.GetSpec()] = constant | |
120 | |
121 return import_item | |
122 | |
123 def StructToData(struct): | |
124 return { | |
125 istr(0, 'name'): struct.name, | |
126 istr(1, 'fields'): map(FieldToData, struct.fields) | |
127 } | |
128 | |
129 def StructFromData(module, data): | |
130 struct = mojom.Struct() | |
131 struct.name = data['name'] | |
132 struct.spec = 'x:' + module.namespace + '.' + struct.name | |
133 module.kinds[struct.spec] = struct | |
134 struct.enums = map(lambda enum: | |
135 EnumFromData(module, enum, struct), data['enums']) | |
136 struct.fields = map(lambda field: | |
137 FieldFromData(module, field, struct), data['fields']) | |
138 return struct | |
139 | |
140 def FieldToData(field): | |
141 data = { | |
142 istr(0, 'name'): field.name, | |
143 istr(1, 'kind'): KindToData(field.kind) | |
144 } | |
145 if field.ordinal != None: | |
146 data[istr(2, 'ordinal')] = field.ordinal | |
147 if field.default != None: | |
148 data[istr(3, 'default')] = field.default | |
149 return data | |
150 | |
151 def FixupExpression(module, value, scope): | |
152 if isinstance(value, (tuple, list)): | |
153 for i in xrange(len(value)): | |
154 if isinstance(value, tuple): | |
155 FixupExpression(module, value[i], scope) | |
156 else: | |
157 value[i] = FixupExpression(module, value[i], scope) | |
158 elif value: | |
159 constant = LookupConstant(module.constants, value, scope) | |
160 if constant: | |
161 return constant | |
162 return value | |
163 | |
164 def FieldFromData(module, data, struct): | |
165 field = mojom.Field() | |
166 field.name = data['name'] | |
167 field.kind = KindFromData( | |
168 module.kinds, data['kind'], (module.namespace, struct.name)) | |
169 field.ordinal = data.get('ordinal') | |
170 field.default = FixupExpression( | |
171 module, data.get('default'), (module.namespace, struct.name)) | |
172 return field | |
173 | |
174 def ParameterToData(parameter): | |
175 data = { | |
176 istr(0, 'name'): parameter.name, | |
177 istr(1, 'kind'): parameter.kind.spec | |
178 } | |
179 if parameter.ordinal != None: | |
180 data[istr(2, 'ordinal')] = parameter.ordinal | |
181 if parameter.default != None: | |
182 data[istr(3, 'default')] = parameter.default | |
183 return data | |
184 | |
185 def ParameterFromData(module, data, interface): | |
186 parameter = mojom.Parameter() | |
187 parameter.name = data['name'] | |
188 parameter.kind = KindFromData( | |
189 module.kinds, data['kind'], (module.namespace, interface.name)) | |
190 parameter.ordinal = data.get('ordinal') | |
191 parameter.default = data.get('default') | |
192 return parameter | |
193 | |
194 def MethodToData(method): | |
195 data = { | |
196 istr(0, 'name'): method.name, | |
197 istr(1, 'parameters'): map(ParameterToData, method.parameters) | |
198 } | |
199 if method.ordinal != None: | |
200 data[istr(2, 'ordinal')] = method.ordinal | |
201 if method.response_parameters != None: | |
202 data[istr(3, 'response_parameters')] = map( | |
203 ParameterToData, method.response_parameters) | |
204 return data | |
205 | |
206 def MethodFromData(module, data, interface): | |
207 method = mojom.Method() | |
208 method.name = data['name'] | |
209 method.ordinal = data.get('ordinal') | |
210 method.default = data.get('default') | |
211 method.parameters = map(lambda parameter: | |
212 ParameterFromData(module, parameter, interface), data['parameters']) | |
213 if data.has_key('response_parameters'): | |
214 method.response_parameters = map( | |
215 lambda parameter: ParameterFromData(module, parameter, interface), | |
216 data['response_parameters']) | |
217 return method | |
218 | |
219 def InterfaceToData(interface): | |
220 return { | |
221 istr(0, 'name'): interface.name, | |
222 istr(1, 'peer'): interface.peer, | |
223 istr(2, 'methods'): map(MethodToData, interface.methods) | |
224 } | |
225 | |
226 def InterfaceFromData(module, data): | |
227 interface = mojom.Interface() | |
228 interface.name = data['name'] | |
229 interface.spec = 'x:' + module.namespace + '.' + interface.name | |
230 interface.peer = data['peer'] if data.has_key('peer') else None | |
231 module.kinds[interface.spec] = interface | |
232 interface.enums = map(lambda enum: | |
233 EnumFromData(module, enum, interface), data['enums']) | |
234 interface.methods = map(lambda method: | |
235 MethodFromData(module, method, interface), data['methods']) | |
236 return interface | |
237 | |
238 def EnumFieldFromData(module, enum, data, parent_kind): | |
239 field = mojom.EnumField() | |
240 field.name = data['name'] | |
241 if parent_kind: | |
242 field.value = FixupExpression( | |
243 module, data['value'], (module.namespace, parent_kind.name)) | |
244 else: | |
245 field.value = FixupExpression( | |
246 module, data['value'], (module.namespace, )) | |
247 constant = mojom.Constant(module, enum, field) | |
248 module.constants[constant.GetSpec()] = constant | |
249 return field | |
250 | |
251 def EnumFromData(module, data, parent_kind): | |
252 enum = mojom.Enum() | |
253 enum.name = data['name'] | |
254 name = enum.name | |
255 if parent_kind: | |
256 name = parent_kind.name + '.' + name | |
257 enum.spec = 'x:%s.%s' % (module.namespace, name) | |
258 enum.parent_kind = parent_kind | |
259 | |
260 enum.fields = map( | |
261 lambda field: EnumFieldFromData(module, enum, field, parent_kind), | |
262 data['fields']) | |
263 module.kinds[enum.spec] = enum | |
264 return enum | |
265 | |
266 def ModuleToData(module): | |
267 return { | |
268 istr(0, 'name'): module.name, | |
269 istr(1, 'namespace'): module.namespace, | |
270 istr(2, 'structs'): map(StructToData, module.structs), | |
271 istr(3, 'interfaces'): map(InterfaceToData, module.interfaces) | |
272 } | |
273 | |
274 def ModuleFromData(data): | |
275 module = mojom.Module() | |
276 module.kinds = {} | |
277 for kind in mojom.PRIMITIVES: | |
278 module.kinds[kind.spec] = kind | |
279 | |
280 module.constants = {} | |
281 | |
282 module.name = data['name'] | |
283 module.namespace = data['namespace'] | |
284 # Imports must come first, because they add to module.kinds which is used | |
285 # by by the others. | |
286 module.imports = map( | |
287 lambda import_data: ImportFromData(module, import_data), | |
288 data['imports']) | |
289 module.enums = map( | |
290 lambda enum: EnumFromData(module, enum, None), data['enums']) | |
291 module.structs = map( | |
292 lambda struct: StructFromData(module, struct), data['structs']) | |
293 module.interfaces = map( | |
294 lambda interface: InterfaceFromData(module, interface), | |
295 data['interfaces']) | |
296 | |
297 return module | |
298 | |
299 def OrderedModuleFromData(data): | |
300 module = ModuleFromData(data) | |
301 next_interface_ordinal = 0 | |
302 for interface in module.interfaces: | |
303 next_ordinal = 0 | |
304 for method in interface.methods: | |
305 if method.ordinal is None: | |
306 method.ordinal = next_ordinal | |
307 next_ordinal = method.ordinal + 1 | |
308 return module | |
OLD | NEW |