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