Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(756)

Side by Side Diff: lib/dom/scripts/databasebuilder.py

Issue 10913125: Remove lib/dom directory! (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 copy
7 import database
8 import idlparser
9 import logging
10 import os
11 import os.path
12 import re
13
14 from idlnode import *
15
16 _logger = logging.getLogger('databasebuilder')
17
18 # Used in source annotations to specify the parent interface declaring
19 # a displaced declaration. The 'via' attribute specifies the parent interface
20 # which implements a displaced declaration.
21 _VIA_ANNOTATION_ATTR_NAME = 'via'
22
23 # Used in source annotations to specify the module that the interface was
24 # imported from.
25 _MODULE_ANNOTATION_ATTR_NAME = 'module'
26
27
28 class DatabaseBuilderOptions(object):
29 """Used in specifying options when importing new interfaces"""
30
31 def __init__(self,
32 idl_syntax=idlparser.WEBIDL_SYNTAX,
33 idl_defines=[],
34 source=None, source_attributes={},
35 type_rename_map={},
36 rename_operation_arguments_on_merge=False,
37 add_new_interfaces=True,
38 obsolete_old_declarations=False):
39 """Constructor.
40 Args:
41 idl_syntax -- the syntax of the IDL file that is imported.
42 idl_defines -- list of definitions for the idl gcc pre-processor
43 source -- the origin of the IDL file, used for annotating the
44 database.
45 source_attributes -- this map of attributes is used as
46 annotation attributes.
47 rename_operation_arguments_on_merge -- if True, will rename
48 operation arguments when merging using the new name rather
49 than the old.
50 add_new_interfaces -- when False, if an interface is a new
51 addition, it will be ignored.
52 obsolete_old_declarations -- when True, if a declaration
53 from a certain source is not re-declared, it will be removed.
54 """
55 self.source = source
56 self.source_attributes = source_attributes
57 self.idl_syntax = idl_syntax
58 self.idl_defines = idl_defines
59 self.type_rename_map = type_rename_map
60 self.rename_operation_arguments_on_merge = \
61 rename_operation_arguments_on_merge
62 self.add_new_interfaces = add_new_interfaces
63 self.obsolete_old_declarations = obsolete_old_declarations
64
65
66 class DatabaseBuilder(object):
67 def __init__(self, database):
68 """DatabaseBuilder is used for importing and merging interfaces into
69 the Database"""
70 self._database = database
71 self._imported_interfaces = []
72 self._impl_stmts = []
73
74 def _load_idl_file(self, file_name, import_options):
75 """Loads an IDL file intor memory"""
76 idl_parser = idlparser.IDLParser(import_options.idl_syntax)
77
78 try:
79 f = open(file_name, 'r')
80 content = f.read()
81 f.close()
82
83 idl_ast = idl_parser.parse(content,
84 defines=import_options.idl_defines)
85 return IDLFile(idl_ast, file_name)
86 except SyntaxError, e:
87 raise RuntimeError('Failed to load file %s: %s' % (file_name, e))
88
89 def _resolve_type_defs(self, idl_file):
90 type_def_map = {}
91 # build map
92 for type_def in idl_file.all(IDLTypeDef):
93 if type_def.type.id != type_def.id: # sanity check
94 type_def_map[type_def.id] = type_def.type.id
95 # use the map
96 for type_node in idl_file.all(IDLType):
97 while type_node.id in type_def_map:
98 type_node.id = type_def_map[type_node.id]
99
100 def _strip_ext_attributes(self, idl_file):
101 """Strips unuseful extended attributes."""
102 for ext_attrs in idl_file.all(IDLExtAttrs):
103 # TODO: Decide which attributes are uninteresting.
104 pass
105
106 def _rename_types(self, idl_file, import_options):
107 """Rename interface and type names with names provided in the
108 options. Also clears scopes from scoped names"""
109
110 def rename(name):
111 name_parts = name.split('::')
112 name = name_parts[-1]
113 if name in import_options.type_rename_map:
114 name = import_options.type_rename_map[name]
115 return name
116
117 def rename_node(idl_node):
118 idl_node.id = rename(idl_node.id)
119
120 def rename_ext_attrs(ext_attrs_node):
121 for type_valued_attribute_name in ['Supplemental']:
122 if type_valued_attribute_name in ext_attrs_node:
123 value = ext_attrs_node[type_valued_attribute_name]
124 if isinstance(value, str):
125 ext_attrs_node[type_valued_attribute_name] = rename(value)
126
127 map(rename_node, idl_file.all(IDLInterface))
128 map(rename_node, idl_file.all(IDLType))
129 map(rename_ext_attrs, idl_file.all(IDLExtAttrs))
130
131 def _annotate(self, interface, module_name, import_options):
132 """Adds @ annotations based on the source and source_attributes
133 members of import_options."""
134
135 source = import_options.source
136 if not source:
137 return
138
139 def add_source_annotation(idl_node):
140 annotation = IDLAnnotation(
141 copy.deepcopy(import_options.source_attributes))
142 idl_node.annotations[source] = annotation
143 if ((isinstance(idl_node, IDLInterface) or
144 isinstance(idl_node, IDLMember)) and
145 idl_node.is_fc_suppressed):
146 annotation['suppressed'] = None
147
148 add_source_annotation(interface)
149 interface.annotations[source][_MODULE_ANNOTATION_ATTR_NAME] = module_name
150
151 map(add_source_annotation, interface.parents)
152 map(add_source_annotation, interface.constants)
153 map(add_source_annotation, interface.attributes)
154 map(add_source_annotation, interface.operations)
155
156 def _sign(self, node):
157 """Computes a unique signature for the node, for merging purposed, by
158 concatenating types and names in the declaration."""
159 if isinstance(node, IDLType):
160 res = node.id
161 if res.startswith('unsigned '):
162 res = res[len('unsigned '):]
163 return res
164
165 res = []
166 if isinstance(node, IDLInterface):
167 res = ['interface', node.id]
168 elif isinstance(node, IDLParentInterface):
169 res = ['parent', self._sign(node.type)]
170 elif isinstance(node, IDLOperation):
171 res = ['op']
172 for special in node.specials:
173 res.append(special)
174 if node.id is not None:
175 res.append(node.id)
176 for arg in node.arguments:
177 res.append(self._sign(arg.type))
178 res.append(self._sign(node.type))
179 elif isinstance(node, IDLAttribute):
180 res = []
181 if node.is_read_only:
182 res.append('readonly')
183 res.append(node.id)
184 res.append(self._sign(node.type))
185 elif isinstance(node, IDLConstant):
186 res = []
187 res.append('const')
188 res.append(node.id)
189 res.append(node.value)
190 res.append(self._sign(node.type))
191 else:
192 raise TypeError("Can't sign input of type %s" % type(node))
193 return ':'.join(res)
194
195 def _build_signatures_map(self, idl_node_list):
196 """Creates a hash table mapping signatures to idl_nodes for the
197 given list of nodes"""
198 res = {}
199 for idl_node in idl_node_list:
200 sig = self._sign(idl_node)
201 if sig is None:
202 continue
203 if sig in res:
204 raise RuntimeError('Warning: Multiple members have the same '
205 'signature: "%s"' % sig)
206 res[sig] = idl_node
207 return res
208
209 def _get_parent_interfaces(self, interface):
210 """Return a list of all the parent interfaces of a given interface"""
211 res = []
212
213 def recurse(current_interface):
214 if current_interface in res:
215 return
216 res.append(current_interface)
217 for parent in current_interface.parents:
218 parent_name = parent.type.id
219 if self._database.HasInterface(parent_name):
220 recurse(self._database.GetInterface(parent_name))
221
222 recurse(interface)
223 return res[1:]
224
225 def _merge_ext_attrs(self, old_attrs, new_attrs):
226 """Merges two sets of extended attributes.
227
228 Returns: True if old_attrs has changed.
229 """
230 changed = False
231 for (name, value) in new_attrs.items():
232 if name in old_attrs and old_attrs[name] == value:
233 pass # Identical
234 else:
235 old_attrs[name] = value
236 changed = True
237 return changed
238
239 def _merge_nodes(self, old_list, new_list, import_options):
240 """Merges two lists of nodes. Annotates nodes with the source of each
241 node.
242
243 Returns:
244 True if the old_list has changed.
245
246 Args:
247 old_list -- the list to merge into.
248 new_list -- list containing more nodes.
249 import_options -- controls how merging is done.
250 """
251 changed = False
252
253 source = import_options.source
254
255 old_signatures_map = self._build_signatures_map(old_list)
256 new_signatures_map = self._build_signatures_map(new_list)
257
258 # Merge new items
259 for (sig, new_node) in new_signatures_map.items():
260 if sig not in old_signatures_map:
261 # New node:
262 old_list.append(new_node)
263 changed = True
264 else:
265 # Merge old and new nodes:
266 old_node = old_signatures_map[sig]
267 if (source not in old_node.annotations
268 and source in new_node.annotations):
269 old_node.annotations[source] = new_node.annotations[source]
270 changed = True
271 # Maybe rename arguments:
272 if isinstance(old_node, IDLOperation):
273 for i in range(0, len(old_node.arguments)):
274 old_arg = old_node.arguments[i]
275 new_arg = new_node.arguments[i]
276
277 old_arg_name = old_arg.id
278 new_arg_name = new_arg.id
279 if (old_arg_name != new_arg_name
280 and (old_arg_name == 'arg'
281 or old_arg_name.endswith('Arg')
282 or import_options.rename_operation_arguments_on_merge)):
283 old_node.arguments[i].id = new_arg_name
284 changed = True
285
286 if self._merge_ext_attrs(old_arg.ext_attrs, new_arg.ext_attrs):
287 changed = True
288 # Maybe merge annotations:
289 if (isinstance(old_node, IDLAttribute) or
290 isinstance(old_node, IDLOperation)):
291 if self._merge_ext_attrs(old_node.ext_attrs, new_node.ext_attrs):
292 changed = True
293
294 # Remove annotations on obsolete items from the same source
295 if import_options.obsolete_old_declarations:
296 for (sig, old_node) in old_signatures_map.items():
297 if (source in old_node.annotations
298 and sig not in new_signatures_map):
299 _logger.warn('%s not available in %s anymore' %
300 (sig, source))
301 del old_node.annotations[source]
302 changed = True
303
304 return changed
305
306 def _merge_interfaces(self, old_interface, new_interface, import_options):
307 """Merges the new_interface into the old_interface, annotating the
308 interface with the sources of each change."""
309
310 changed = False
311
312 source = import_options.source
313 if (source and source not in old_interface.annotations and
314 source in new_interface.annotations and
315 not new_interface.is_supplemental):
316 old_interface.annotations[source] = new_interface.annotations[source]
317 changed = True
318
319 def merge_list(what):
320 old_list = old_interface.__dict__[what]
321 new_list = new_interface.__dict__[what]
322
323 if what != 'parents' and old_interface.id != new_interface.id:
324 for node in new_list:
325 node.ext_attrs['ImplementedBy'] = new_interface.id
326
327 changed = self._merge_nodes(old_list, new_list, import_options)
328
329 # Delete list items with zero remaining annotations.
330 if changed and import_options.obsolete_old_declarations:
331
332 def has_annotations(idl_node):
333 return len(idl_node.annotations)
334
335 old_interface.__dict__[what] = filter(has_annotations, old_list)
336
337 return changed
338
339 # Smartly merge various declarations:
340 if merge_list('parents'):
341 changed = True
342 if merge_list('constants'):
343 changed = True
344 if merge_list('attributes'):
345 changed = True
346 if merge_list('operations'):
347 changed = True
348
349 if self._merge_ext_attrs(old_interface.ext_attrs, new_interface.ext_attrs):
350 changed = True
351
352 _logger.info('merged interface %s (changed=%s, supplemental=%s)' %
353 (old_interface.id, changed, new_interface.is_supplemental))
354
355 return changed
356
357 def _merge_impl_stmt(self, impl_stmt, import_options):
358 """Applies "X implements Y" statemetns on the proper places in the
359 database"""
360 implementor_name = impl_stmt.implementor.id
361 implemented_name = impl_stmt.implemented.id
362 _logger.info('merging impl stmt %s implements %s' %
363 (implementor_name, implemented_name))
364
365 source = import_options.source
366 if self._database.HasInterface(implementor_name):
367 interface = self._database.GetInterface(implementor_name)
368 if interface.parents is None:
369 interface.parents = []
370 for parent in interface.parents:
371 if parent.type.id == implemented_name:
372 if source and source not in parent.annotations:
373 parent.annotations[source] = IDLAnnotation(
374 import_options.source_attributes)
375 return
376 # not found, so add new one
377 parent = IDLParentInterface(None)
378 parent.type = IDLType(implemented_name)
379 if source:
380 parent.annotations[source] = IDLAnnotation(
381 import_options.source_attributes)
382 interface.parents.append(parent)
383
384 def merge_imported_interfaces(self):
385 """Merges all imported interfaces and loads them into the DB."""
386
387 # Step 1: Pre process imported interfaces
388 for interface, module_name, import_options in self._imported_interfaces:
389 self._annotate(interface, module_name, import_options)
390
391 # Step 2: Add all new interfaces and merge overlapping ones
392 for interface, module_name, import_options in self._imported_interfaces:
393 if not interface.is_supplemental:
394 if self._database.HasInterface(interface.id):
395 old_interface = self._database.GetInterface(interface.id)
396 self._merge_interfaces(old_interface, interface, import_options)
397 else:
398 if import_options.add_new_interfaces:
399 self._database.AddInterface(interface)
400
401 # Step 3: Merge in supplemental interfaces
402 for interface, module_name, import_options in self._imported_interfaces:
403 if interface.is_supplemental:
404 target_name = interface.ext_attrs['Supplemental']
405 if target_name:
406 # [Supplemental=DOMWindow] - merge into DOMWindow.
407 target = target_name
408 else:
409 # [Supplemental] - merge into existing inteface with same name.
410 target = interface.id
411 if self._database.HasInterface(target):
412 old_interface = self._database.GetInterface(target)
413 self._merge_interfaces(old_interface, interface, import_options)
414 else:
415 raise Exception("Supplemental target '%s' not found", target)
416
417 # Step 4: Resolve 'implements' statements
418 for impl_stmt, import_options in self._impl_stmts:
419 self._merge_impl_stmt(impl_stmt, import_options)
420
421 self._impl_stmts = []
422 self._imported_interfaces = []
423
424 def import_idl_file(self, file_path,
425 import_options=DatabaseBuilderOptions()):
426 """Parses, loads into memory and cleans up and IDL file"""
427 idl_file = self._load_idl_file(file_path, import_options)
428
429 self._strip_ext_attributes(idl_file)
430 self._resolve_type_defs(idl_file)
431 self._rename_types(idl_file, import_options)
432
433 def enabled(idl_node):
434 return self._is_node_enabled(idl_node, import_options.idl_defines)
435
436 for module in idl_file.modules:
437 for interface in module.interfaces:
438 if not self._is_node_enabled(interface, import_options.idl_defines):
439 _logger.info('skipping interface %s/%s (source=%s file=%s)'
440 % (module.id, interface.id, import_options.source,
441 file_path))
442 continue
443
444 _logger.info('importing interface %s/%s (source=%s file=%s)'
445 % (module.id, interface.id, import_options.source,
446 file_path))
447 interface.attributes = filter(enabled, interface.attributes)
448 interface.operations = filter(enabled, interface.operations)
449 self._imported_interfaces.append((interface, module.id, import_options))
450
451 for implStmt in module.implementsStatements:
452 self._impl_stmts.append((implStmt, import_options))
453
454 def _is_node_enabled(self, node, idl_defines):
455 if not 'Conditional' in node.ext_attrs:
456 return True
457
458 def enabled(condition):
459 return 'ENABLE_%s' % condition in idl_defines
460
461 conditional = node.ext_attrs['Conditional']
462 if conditional.find('&') != -1:
463 for condition in conditional.split('&'):
464 if not enabled(condition):
465 return False
466 return True
467
468 for condition in conditional.split('|'):
469 if enabled(condition):
470 return True
471 return False
472
473 def fix_displacements(self, source):
474 """E.g. In W3C, something is declared on HTMLDocument but in WebKit
475 its on Document, so we need to mark that something in HTMLDocument
476 with @WebKit(via=Document). The 'via' attribute specifies the
477 parent interface that has the declaration."""
478
479 for interface in self._database.GetInterfaces():
480 changed = False
481
482 _logger.info('fixing displacements in %s' % interface.id)
483
484 for parent_interface in self._get_parent_interfaces(interface):
485 _logger.info('scanning parent %s of %s' %
486 (parent_interface.id, interface.id))
487
488 def fix_nodes(local_list, parent_list):
489 changed = False
490 parent_signatures_map = self._build_signatures_map(
491 parent_list)
492 for idl_node in local_list:
493 sig = self._sign(idl_node)
494 if sig in parent_signatures_map:
495 parent_member = parent_signatures_map[sig]
496 if (source in parent_member.annotations
497 and source not in idl_node.annotations
498 and _VIA_ANNOTATION_ATTR_NAME
499 not in parent_member.annotations[source]):
500 idl_node.annotations[source] = IDLAnnotation(
501 {_VIA_ANNOTATION_ATTR_NAME: parent_interface.id})
502 changed = True
503 return changed
504
505 changed = fix_nodes(interface.constants,
506 parent_interface.constants) or changed
507 changed = fix_nodes(interface.attributes,
508 parent_interface.attributes) or changed
509 changed = fix_nodes(interface.operations,
510 parent_interface.operations) or changed
511 if changed:
512 _logger.info('fixed displaced declarations in %s' %
513 interface.id)
514
515 def normalize_annotations(self, sources):
516 """Makes the IDLs less verbose by removing annotation attributes
517 that are identical to the ones defined at the interface level.
518
519 Args:
520 sources -- list of source names to normalize."""
521 for interface in self._database.GetInterfaces():
522 _logger.debug('normalizing annotations for %s' % interface.id)
523 for source in sources:
524 if (source not in interface.annotations or
525 not interface.annotations[source]):
526 continue
527 top_level_annotation = interface.annotations[source]
528
529 def normalize(idl_node):
530 if (source in idl_node.annotations
531 and idl_node.annotations[source]):
532 annotation = idl_node.annotations[source]
533 for name, value in annotation.items():
534 if (name in top_level_annotation
535 and value == top_level_annotation[name]):
536 del annotation[name]
537
538 map(normalize, interface.parents)
539 map(normalize, interface.constants)
540 map(normalize, interface.attributes)
541 map(normalize, interface.operations)
542
543 def fetch_constructor_data(self, options):
544 window_interface = self._database.GetInterface('Window')
545 for attr in window_interface.attributes:
546 type = attr.type.id
547 if not type.endswith('Constructor'):
548 continue
549 type = re.sub('(Constructor)+$', '', type)
550 # TODO(antonm): Ideally we'd like to have pristine copy of WebKit IDLs and fetch
551 # this information directly from it. Unfortunately right now database is massaged
552 # a lot so it's difficult to maintain necessary information on DOMWindow i tself.
553 interface = self._database.GetInterface(options.type_rename_map.get(type, type))
554 if 'V8EnabledPerContext' in attr.ext_attrs:
555 interface.ext_attrs['synthesizedV8EnabledPerContext'] = \
556 attr.ext_attrs['V8EnabledPerContext']
557 if 'V8EnabledAtRuntime' in attr.ext_attrs:
558 interface.ext_attrs['synthesizedV8EnabledAtRuntime'] = \
559 attr.ext_attrs['V8EnabledAtRuntime'] or attr.id
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698