OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
3 # for details. All rights reserved. Use of this source code is governed by a | |
4 # BSD-style license that can be found in the LICENSE file. | |
5 | |
6 import os | |
7 import sys | |
8 | |
9 | |
10 class IDLNode(object): | |
11 """Base class for all IDL elements. | |
12 IDLNode may contain various child nodes, and have properties. Examples | |
13 of IDLNode are interfaces, interface members, function arguments, | |
14 etc. | |
15 """ | |
16 | |
17 def __init__(self, ast): | |
18 """Initializes an IDLNode from a PegParser AST output.""" | |
19 self.id = self._find_first(ast, 'Id') if ast is not None else None | |
20 | |
21 def __repr__(self): | |
22 """Generates string of the form <class id extra extra ... 0x12345678>.""" | |
23 extras = self._extra_repr() | |
24 if isinstance(extras, list): | |
25 extras = ' '.join([str(e) for e in extras]) | |
26 try: | |
27 if self.id: | |
28 return '<%s %s 0x%x>' % ( | |
29 type(self).__name__, | |
30 ('%s %s' % (self.id, extras)).strip(), | |
31 hash(self)) | |
32 return '<%s %s 0x%x>' % ( | |
33 type(self).__name__, | |
34 extras, | |
35 hash(self)) | |
36 except Exception, e: | |
37 return "can't convert to string: %s" % e | |
38 | |
39 def _extra_repr(self): | |
40 """Returns string of extra info for __repr__().""" | |
41 return '' | |
42 | |
43 def __cmp__(self, other): | |
44 """Override default compare operation. | |
45 IDLNodes are equal if all their properties are equal.""" | |
46 if other is None or not isinstance(other, IDLNode): | |
47 return 1 | |
48 return self.__dict__.__cmp__(other.__dict__) | |
49 | |
50 def reset_id(self, newId): | |
51 """Reset the id of the Node. This is typically done during a normalization | |
52 phase (e.g., "DOMWindow" -> "Window").""" | |
53 self.id = newId | |
54 | |
55 def all(self, type_filter=None): | |
56 """Returns a list containing this node and all it child nodes | |
57 (recursive). | |
58 | |
59 Args: | |
60 type_filter -- can be used to limit the results to a specific | |
61 node type (e.g. IDLOperation). | |
62 """ | |
63 res = [] | |
64 if type_filter is None or isinstance(self, type_filter): | |
65 res.append(self) | |
66 for v in self._all_subnodes(): | |
67 if isinstance(v, IDLNode): | |
68 res.extend(v.all(type_filter)) | |
69 elif isinstance(v, list): | |
70 for item in v: | |
71 if isinstance(item, IDLNode): | |
72 res.extend(item.all(type_filter)) | |
73 return res | |
74 | |
75 def _all_subnodes(self): | |
76 """Accessor used by all() to find subnodes.""" | |
77 return self.__dict__.values() | |
78 | |
79 def to_dict(self): | |
80 """Converts the IDLNode and its children into a dictionary. | |
81 This method is useful mostly for debugging and pretty printing. | |
82 """ | |
83 res = {} | |
84 for (k, v) in self.__dict__.items(): | |
85 if v == None or v == False or v == [] or v == {}: | |
86 # Skip empty/false members. | |
87 continue | |
88 elif isinstance(v, IDLDictNode) and not len(v): | |
89 # Skip empty dict sub-nodes. | |
90 continue | |
91 elif isinstance(v, list): | |
92 # Convert lists: | |
93 new_v = [] | |
94 for sub_node in v: | |
95 if isinstance(sub_node, IDLNode): | |
96 # Convert sub-node: | |
97 new_v.append(sub_node.to_dict()) | |
98 else: | |
99 new_v.append(sub_node) | |
100 v = new_v | |
101 elif isinstance(v, IDLNode): | |
102 # Convert sub-node: | |
103 v = v.to_dict() | |
104 res[k] = v | |
105 return res | |
106 | |
107 def _find_all(self, ast, label, max_results=sys.maxint): | |
108 """Searches the AST for tuples with a given label. The PegParser | |
109 output is composed of lists and tuples, where the tuple 1st argument | |
110 is a label. If ast root is a list, will search recursively inside each | |
111 member in the list. | |
112 | |
113 Args: | |
114 ast -- the AST to search. | |
115 label -- the label to look for. | |
116 res -- results are put into this list. | |
117 max_results -- maximum number of results. | |
118 """ | |
119 res = [] | |
120 if max_results <= 0: | |
121 return res | |
122 | |
123 if isinstance(ast, list): | |
124 for childAst in ast: | |
125 sub_res = self._find_all(childAst, label, | |
126 max_results - len(res)) | |
127 res.extend(sub_res) | |
128 elif isinstance(ast, tuple): | |
129 (nodeLabel, value) = ast | |
130 if nodeLabel == label: | |
131 res.append(value) | |
132 return res | |
133 | |
134 def _find_first(self, ast, label): | |
135 """Convenience method for _find_all(..., max_results=1). | |
136 Returns a single element instead of a list, or None if nothing | |
137 is found.""" | |
138 res = self._find_all(ast, label, max_results=1) | |
139 if len(res): | |
140 return res[0] | |
141 return None | |
142 | |
143 def _has(self, ast, label): | |
144 """Returns true if an element with the given label is | |
145 in the AST by searching for it.""" | |
146 return len(self._find_all(ast, label, max_results=1)) == 1 | |
147 | |
148 def _convert_all(self, ast, label, idlnode_ctor): | |
149 """Converts AST elements into IDLNode elements. | |
150 Uses _find_all to find elements with a given label and converts | |
151 them into IDLNodes with a given constructor. | |
152 Returns: | |
153 A list of the converted nodes. | |
154 Args: | |
155 ast -- the ast element to start a search at. | |
156 label -- the element label to look for. | |
157 idlnode_ctor -- a constructor function of one of the IDLNode | |
158 sub-classes. | |
159 """ | |
160 res = [] | |
161 found = self._find_all(ast, label) | |
162 if not found: | |
163 return res | |
164 if not isinstance(found, list): | |
165 raise RuntimeError("Expected list but %s found" % type(found)) | |
166 for childAst in found: | |
167 converted = idlnode_ctor(childAst) | |
168 res.append(converted) | |
169 return res | |
170 | |
171 def _convert_first(self, ast, label, idlnode_ctor): | |
172 """Like _convert_all, but only converts the first found results.""" | |
173 childAst = self._find_first(ast, label) | |
174 if not childAst: | |
175 return None | |
176 return idlnode_ctor(childAst) | |
177 | |
178 def _convert_ext_attrs(self, ast): | |
179 """Helper method for uniform conversion of extended attributes.""" | |
180 self.ext_attrs = IDLExtAttrs(ast) | |
181 | |
182 def _convert_annotations(self, ast): | |
183 """Helper method for uniform conversion of annotations.""" | |
184 self.annotations = IDLAnnotations(ast) | |
185 | |
186 | |
187 class IDLDictNode(IDLNode): | |
188 """Base class for dictionary-like IDL nodes such as extended attributes | |
189 and annotations. The base class implements various dict interfaces.""" | |
190 | |
191 def __init__(self, ast): | |
192 IDLNode.__init__(self, None) | |
193 if ast is not None and isinstance(ast, dict): | |
194 self.__map = ast | |
195 else: | |
196 self.__map = {} | |
197 | |
198 def __len__(self): | |
199 return len(self.__map) | |
200 | |
201 def __getitem__(self, key): | |
202 return self.__map[key] | |
203 | |
204 def __setitem__(self, key, value): | |
205 self.__map[key] = value | |
206 | |
207 def __delitem__(self, key): | |
208 del self.__map[key] | |
209 | |
210 def __contains__(self, key): | |
211 return key in self.__map | |
212 | |
213 def __iter__(self): | |
214 return self.__map.__iter__() | |
215 | |
216 def get(self, key, default=None): | |
217 return self.__map.get(key, default) | |
218 | |
219 def items(self): | |
220 return self.__map.items() | |
221 | |
222 def keys(self): | |
223 return self.__map.keys() | |
224 | |
225 def values(self): | |
226 return self.__map.values() | |
227 | |
228 def clear(self): | |
229 self.__map = {} | |
230 | |
231 def to_dict(self): | |
232 """Overrides the default IDLNode.to_dict behavior. | |
233 The IDLDictNode members are copied into a new dictionary, and | |
234 IDLNode members are recursively converted into dicts as well. | |
235 """ | |
236 res = {} | |
237 for (k, v) in self.__map.items(): | |
238 if isinstance(v, IDLNode): | |
239 v = v.to_dict() | |
240 res[k] = v | |
241 return res | |
242 | |
243 def _all_subnodes(self): | |
244 # Usually an IDLDictNode does not contain further IDLNodes. | |
245 return [] | |
246 | |
247 | |
248 class IDLFile(IDLNode): | |
249 """IDLFile is the top-level node in each IDL file. It may contain interfaces."
"" | |
250 | |
251 def __init__(self, ast, filename=None): | |
252 IDLNode.__init__(self, ast) | |
253 self.filename = filename | |
254 self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) | |
255 modules = self._convert_all(ast, 'Module', IDLModule) | |
256 self.implementsStatements = self._convert_all(ast, 'ImplStmt', | |
257 IDLImplementsStatement) | |
258 for module in modules: | |
259 self.interfaces.extend(module.interfaces) | |
260 self.implementsStatements.extend(module.implementsStatements) | |
261 | |
262 | |
263 class IDLModule(IDLNode): | |
264 """IDLModule has an id, and may contain interfaces, type defs and | |
265 implements statements.""" | |
266 def __init__(self, ast): | |
267 IDLNode.__init__(self, ast) | |
268 self._convert_ext_attrs(ast) | |
269 self._convert_annotations(ast) | |
270 self.interfaces = self._convert_all(ast, 'Interface', IDLInterface) | |
271 self.typeDefs = self._convert_all(ast, 'TypeDef', IDLTypeDef) | |
272 self.implementsStatements = self._convert_all(ast, 'ImplStmt', | |
273 IDLImplementsStatement) | |
274 | |
275 | |
276 class IDLExtAttrs(IDLDictNode): | |
277 """IDLExtAttrs is an IDLDictNode that stores IDL Extended Attributes. | |
278 Modules, interfaces, members and arguments can all own IDLExtAttrs.""" | |
279 def __init__(self, ast=None): | |
280 IDLDictNode.__init__(self, None) | |
281 if not ast: | |
282 return | |
283 ext_attrs_ast = self._find_first(ast, 'ExtAttrs') | |
284 if not ext_attrs_ast: | |
285 return | |
286 for ext_attr in self._find_all(ext_attrs_ast, 'ExtAttr'): | |
287 name = self._find_first(ext_attr, 'Id') | |
288 value = self._find_first(ext_attr, 'ExtAttrValue') | |
289 | |
290 func_value = self._find_first(value, 'ExtAttrFunctionValue') | |
291 if func_value: | |
292 # E.g. NamedConstructor=Audio(in [Optional] DOMString src) | |
293 self[name] = IDLExtAttrFunctionValue( | |
294 func_value, | |
295 self._find_first(func_value, 'ExtAttrArgList')) | |
296 continue | |
297 | |
298 ctor_args = not value and self._find_first(ext_attr, 'ExtAttrArgList') | |
299 if ctor_args: | |
300 # E.g. Constructor(Element host) | |
301 self[name] = IDLExtAttrFunctionValue(None, ctor_args) | |
302 continue | |
303 | |
304 self[name] = value | |
305 | |
306 def _all_subnodes(self): | |
307 # Extended attributes may contain IDLNodes, e.g. IDLExtAttrFunctionValue | |
308 return self.values() | |
309 | |
310 | |
311 class IDLExtAttrFunctionValue(IDLNode): | |
312 """IDLExtAttrFunctionValue.""" | |
313 def __init__(self, func_value_ast, arg_list_ast): | |
314 IDLNode.__init__(self, func_value_ast) | |
315 self.arguments = self._convert_all(arg_list_ast, 'Argument', IDLArgument) | |
316 | |
317 | |
318 class IDLType(IDLNode): | |
319 """IDLType is used to describe constants, attributes and operations' | |
320 return and input types. IDLType matches AST labels such as ScopedName, | |
321 StringType, VoidType, IntegerType, etc.""" | |
322 | |
323 def __init__(self, ast): | |
324 IDLNode.__init__(self, ast) | |
325 # Search for a 'ScopedName' or any label ending with 'Type'. | |
326 if isinstance(ast, list): | |
327 self.id = self._find_first(ast, 'ScopedName') | |
328 if not self.id: | |
329 # FIXME: use regexp search instead | |
330 def findType(ast): | |
331 for label, childAst in ast: | |
332 if label.endswith('Type'): | |
333 type = self._label_to_type(label, ast) | |
334 if type != 'sequence': | |
335 return type | |
336 type_ast = self._find_first(childAst, 'Type') | |
337 if not type_ast: | |
338 return type | |
339 return 'sequence<%s>' % findType(type_ast) | |
340 raise Exception('No type declaration found in %s' % ast) | |
341 self.id = findType(ast) | |
342 array_modifiers = self._find_first(ast, 'ArrayModifiers') | |
343 if array_modifiers: | |
344 self.id += array_modifiers | |
345 elif isinstance(ast, tuple): | |
346 (label, value) = ast | |
347 if label == 'ScopedName': | |
348 self.id = value | |
349 else: | |
350 self.id = self._label_to_type(label, ast) | |
351 elif isinstance(ast, str): | |
352 self.id = ast | |
353 if not self.id: | |
354 raise SyntaxError('Could not parse type %s' % (ast)) | |
355 | |
356 def _label_to_type(self, label, ast): | |
357 if label == 'LongLongType': | |
358 label = 'long long' | |
359 elif label.endswith('Type'): | |
360 # Omit 'Type' suffix and lowercase the rest. | |
361 label = '%s%s' % (label[0].lower(), label[1:-4]) | |
362 | |
363 # Add unsigned qualifier. | |
364 if self._has(ast, 'Unsigned'): | |
365 label = 'unsigned %s' % label | |
366 return label | |
367 | |
368 | |
369 class IDLTypeDef(IDLNode): | |
370 """IDLNode for 'typedef [type] [id]' declarations.""" | |
371 def __init__(self, ast): | |
372 IDLNode.__init__(self, ast) | |
373 self._convert_annotations(ast) | |
374 self.type = self._convert_first(ast, 'Type', IDLType) | |
375 | |
376 | |
377 class IDLInterface(IDLNode): | |
378 """IDLInterface node contains operations, attributes, constants, | |
379 as well as parent references.""" | |
380 | |
381 def __init__(self, ast): | |
382 IDLNode.__init__(self, ast) | |
383 self._convert_ext_attrs(ast) | |
384 self._convert_annotations(ast) | |
385 self.parents = self._convert_all(ast, 'ParentInterface', | |
386 IDLParentInterface) | |
387 javascript_interface_name = self.ext_attrs.get('InterfaceName', self.id) | |
388 self.javascript_binding_name = javascript_interface_name | |
389 self.doc_js_name = javascript_interface_name | |
390 self.operations = self._convert_all(ast, 'Operation', | |
391 lambda ast: IDLOperation(ast, self.doc_js_name)) | |
392 self.attributes = self._convert_all(ast, 'Attribute', | |
393 lambda ast: IDLAttribute(ast, self.doc_js_name)) | |
394 self.constants = self._convert_all(ast, 'Const', | |
395 lambda ast: IDLConstant(ast, self.doc_js_name)) | |
396 self.is_supplemental = 'Supplemental' in self.ext_attrs | |
397 self.is_no_interface_object = 'NoInterfaceObject' in self.ext_attrs | |
398 self.is_fc_suppressed = 'Suppressed' in self.ext_attrs | |
399 | |
400 def reset_id(self, new_id): | |
401 """Reset the id of the Interface and corresponding the JS names.""" | |
402 if self.id != new_id: | |
403 self.id = new_id | |
404 self.doc_js_name = new_id | |
405 self.javascript_binding_name = new_id | |
406 for member in self.operations: | |
407 member.doc_js_interface_name = new_id | |
408 for member in self.attributes: | |
409 member.doc_js_interface_name = new_id | |
410 for member in self.constants: | |
411 member.doc_js_interface_name = new_id | |
412 | |
413 def has_attribute(self, candidate): | |
414 for attribute in self.attributes: | |
415 if (attribute.id == candidate.id and | |
416 attribute.is_read_only == candidate.is_read_only): | |
417 return True | |
418 return False | |
419 | |
420 | |
421 class IDLParentInterface(IDLNode): | |
422 """This IDLNode specialization is for 'Interface Child : Parent {}' | |
423 declarations.""" | |
424 def __init__(self, ast): | |
425 IDLNode.__init__(self, ast) | |
426 self._convert_annotations(ast) | |
427 self.type = self._convert_first(ast, 'InterfaceType', IDLType) | |
428 | |
429 | |
430 class IDLMember(IDLNode): | |
431 """A base class for constants, attributes and operations.""" | |
432 | |
433 def __init__(self, ast, doc_js_interface_name): | |
434 IDLNode.__init__(self, ast) | |
435 self.type = self._convert_first(ast, 'Type', IDLType) | |
436 self._convert_ext_attrs(ast) | |
437 self._convert_annotations(ast) | |
438 self.doc_js_interface_name = doc_js_interface_name | |
439 self.is_fc_suppressed = 'Suppressed' in self.ext_attrs | |
440 self.is_static = self._has(ast, 'Static') | |
441 | |
442 | |
443 class IDLOperation(IDLMember): | |
444 """IDLNode specialization for 'type name(args)' declarations.""" | |
445 def __init__(self, ast, doc_js_interface_name): | |
446 IDLMember.__init__(self, ast, doc_js_interface_name) | |
447 self.type = self._convert_first(ast, 'ReturnType', IDLType) | |
448 self.arguments = self._convert_all(ast, 'Argument', IDLArgument) | |
449 self.raises = self._convert_first(ast, 'Raises', IDLType) | |
450 self.specials = self._find_all(ast, 'Special') | |
451 self.is_stringifier = self._has(ast, 'Stringifier') | |
452 def _extra_repr(self): | |
453 return [self.arguments] | |
454 | |
455 | |
456 class IDLAttribute(IDLMember): | |
457 """IDLNode specialization for 'attribute type name' declarations.""" | |
458 def __init__(self, ast, doc_js_interface_name): | |
459 IDLMember.__init__(self, ast, doc_js_interface_name) | |
460 self.is_read_only = self._has(ast, 'ReadOnly') | |
461 # There are various ways to define exceptions for attributes: | |
462 self.raises = self._convert_first(ast, 'Raises', IDLType) | |
463 self.get_raises = self.raises \ | |
464 or self._convert_first(ast, 'GetRaises', IDLType) | |
465 self.set_raises = self.raises \ | |
466 or self._convert_first(ast, 'SetRaises', IDLType) | |
467 def _extra_repr(self): | |
468 extra = [] | |
469 if self.is_read_only: extra.append('readonly') | |
470 if self.raises: extra.append('raises') | |
471 return extra | |
472 | |
473 class IDLConstant(IDLMember): | |
474 """IDLNode specialization for 'const type name = value' declarations.""" | |
475 def __init__(self, ast, doc_js_interface_name): | |
476 IDLMember.__init__(self, ast, doc_js_interface_name) | |
477 self.value = self._find_first(ast, 'ConstExpr') | |
478 | |
479 | |
480 class IDLArgument(IDLNode): | |
481 """IDLNode specialization for operation arguments.""" | |
482 def __init__(self, ast): | |
483 IDLNode.__init__(self, ast) | |
484 self.type = self._convert_first(ast, 'Type', IDLType) | |
485 self._convert_ext_attrs(ast) | |
486 | |
487 def __repr__(self): | |
488 return '<IDLArgument(type = %s, id = %s)>' % (self.type, self.id) | |
489 | |
490 | |
491 class IDLImplementsStatement(IDLNode): | |
492 """IDLNode specialization for 'X implements Y' declarations.""" | |
493 def __init__(self, ast): | |
494 IDLNode.__init__(self, ast) | |
495 self.implementor = self._convert_first(ast, 'ImplStmtImplementor', | |
496 IDLType) | |
497 self.implemented = self._convert_first(ast, 'ImplStmtImplemented', | |
498 IDLType) | |
499 | |
500 | |
501 class IDLAnnotations(IDLDictNode): | |
502 """IDLDictNode specialization for a list of FremontCut annotations.""" | |
503 def __init__(self, ast=None): | |
504 IDLDictNode.__init__(self, ast) | |
505 self.id = None | |
506 if not ast: | |
507 return | |
508 for annotation in self._find_all(ast, 'Annotation'): | |
509 name = self._find_first(annotation, 'Id') | |
510 value = IDLAnnotation(annotation) | |
511 self[name] = value | |
512 | |
513 | |
514 class IDLAnnotation(IDLDictNode): | |
515 """IDLDictNode specialization for one annotation.""" | |
516 def __init__(self, ast=None): | |
517 IDLDictNode.__init__(self, ast) | |
518 self.id = None | |
519 if not ast: | |
520 return | |
521 for arg in self._find_all(ast, 'AnnotationArg'): | |
522 name = self._find_first(arg, 'Id') | |
523 value = self._find_first(arg, 'AnnotationArgValue') | |
524 self[name] = value | |
OLD | NEW |