Synopsis - Cross-Reference

File: Synopsis/Processors/Linker.py
  1#
  2# Copyright (C) 2000 Stefan Seefeld
  3# Copyright (C) 2000 Stephen Davies
  4# All rights reserved.
  5# Licensed to the public under the terms of the GNU LGPL (>= 2),
  6# see the file COPYING for details.
  7#
  8
  9from Synopsis.Processor import Composite, Parameter
 10from Synopsis import ASG
 11from Synopsis.QualifiedName import *
 12from Synopsis.Processors import TemplateLinker
 13
 14class Linker(Composite, ASG.Visitor):
 15   """Visitor that removes duplicate declarations"""
 16
 17   remove_empty_modules = Parameter(True, 'Remove empty modules.')
 18   sort_modules = Parameter(True, 'Sort module content alphabetically.')
 19   sxr_prefix = Parameter('', 'Compile sxr data, if defined.')
 20
 21   def process(self, ir, **kwds):
 22
 23      self.set_parameters(kwds)
 24      self.ir = self.merge_input(ir)
 25
 26      root = ASG.MetaModule("", QualifiedName())
 27      self.__scopes = [root]
 28      global_dict = {}
 29      self.__dict_map = {id(root) : global_dict}
 30      self.__dicts = [global_dict]
 31
 32      self.types = self.ir.asg.types
 33
 34      try:
 35         for d in self.ir.asg.declarations:
 36            d.accept(self)
 37         self.ir.asg.declarations = root.declarations
 38      except TypeError, e:
 39         import traceback
 40         traceback.print_exc()
 41         print 'linker error :', e
 42
 43      for file in self.ir.files.values():
 44         self.visit_source_file(file)
 45
 46      self.ir = TemplateLinker.TemplateLinker().process(self.ir)
 47
 48      if self.remove_empty_modules:
 49         import ModuleFilter
 50         self.ir = ModuleFilter.ModuleFilter().process(self.ir)
 51
 52      if self.sort_modules:
 53         import ModuleSorter
 54         self.ir = ModuleSorter.ModuleSorter().process(self.ir)
 55
 56      if self.sxr_prefix:
 57         import SXRCompiler
 58         self.ir = SXRCompiler.SXRCompiler(prefix=self.sxr_prefix).process(self.ir)
 59
 60      # now deal with the sub-processors, if any
 61      output = self.output
 62      self.ir = Composite.process(self, self.ir, input=[], output='')
 63      self.output = output
 64
 65      return self.output_and_return_ir()
 66
 67   def lookup(self, name):
 68      """look whether the current scope already contains
 69      a declaration with the given name"""
 70
 71      return self.__dicts[-1].get(name)
 72
 73   def append(self, declaration):
 74      """append declaration to the current scope"""
 75
 76      self.__scopes[-1].declarations.append(declaration)
 77      self.__dicts[-1][declaration.name] = declaration
 78
 79   def push(self, scope):
 80      """push new scope on the stack"""
 81
 82      self.__scopes.append(scope)
 83      dict = self.__dict_map.setdefault(id(scope), {})
 84      self.__dicts.append(dict)
 85
 86   def pop(self):
 87      """restore the previous scope"""
 88
 89      del self.__scopes[-1]
 90      del self.__dicts[-1]
 91
 92   def top(self):
 93
 94      return self.__scopes[-1]
 95
 96   def top_dict(self):
 97
 98      return self.__dicts[-1]
 99
100   def link_type(self, type):
101      "Returns the same or new proxy type"
102
103      self.__type = type
104      if type is not None: type.accept(self)
105      return self.__type
106
107   #################### Type Visitor ###########################################
108
109   def visit_builtin_type_id(self, type):
110
111      if self.types.has_key(type.name):
112         self.__type = self.types[type.name]
113
114   def visit_unknown_type_id(self, type):
115
116      if self.types.has_key(type.name):
117         self.__type = self.types[type.name]
118
119   def visit_declared_type_id(self, type):
120
121      if self.types.has_key(type.name):
122         self.__type = self.types[type.name]
123      else:
124         print "Couldn't find declared type-id:",type.name
125
126   def visit_template_id(self, type):
127
128      # Should be a Declared with the same name
129      if not self.types.has_key(type.name):
130         return
131      declared = self.types[type.name]
132      if isinstance(declared, ASG.UnknownTypeId):
133         #the type was declared in a file for which no ASG is retained
134         return
135      elif not isinstance(declared, ASG.DeclaredTypeId):
136         print "Warning: template declaration was not a declaration:",type.name,declared.__class__.__name__
137         return
138      decl = declared.declaration
139      if not hasattr(decl, 'template'):
140         #print "Warning: non-class/function template",type.name, decl.__class__.__name__
141         return
142      if decl.template:
143         self.__type = decl.template
144      else:
145         print "Warning: template type disappeared:",type.name
146
147   def visit_modifier_type_id(self, type):
148
149      alias = self.link_type(type.alias)
150      if alias is not type.alias:
151         type.alias = alias
152      self.__type = type
153
154   def visit_array_type_id(self, type):
155
156      alias = self.link_type(type.alias)
157      if alias is not type.alias:
158         type.alias = alias
159      self.__type = type
160
161   def visit_parametrized_type_id(self, type):
162
163      templ = self.link_type(type.template)
164      if templ is not type.template:
165         type.template = templ
166      type.parameters = [self.link_type(p) for p in type.parameters]
167      self.__type = type
168
169   def visit_function_type_id(self, type):
170
171      ret = self.link_type(type.return_type)
172      if ret is not type.return_type:
173         type.return_type = ret
174      type.parameters = [self.link_type(p) for p in type.parameters]
175      self.__type = type
176
177   #################### ASG Visitor ############################################
178
179   def visit_source_file(self, file):
180      """Resolves any duplicates in the list of declarations from this
181      file"""
182
183      types = self.types
184
185      # Clear the list and refill it
186      declarations = file.declarations
187      file.declarations = []
188
189      for d in declarations:
190         # If this is a forward declaration try to
191         # replace it by the definition...
192         if types.has_key(d.name):
193            declared = types[d.name]
194            if isinstance(declared, ASG.DeclaredTypeId):
195               d = declared.declaration
196         # ...and only declare it once.
197         if d not in file.declarations:
198            file.declarations.append(d)
199
200      # TODO: includes.
201
202   def visit_module(self, module):
203
204      #hmm, we assume that the parent node is a MetaModule. Can that ever fail ?
205      metamodule = self.lookup(module.name)
206      if metamodule is None:
207         metamodule = ASG.MetaModule(module.type,module.name)
208         self.append(metamodule)
209      elif not isinstance(metamodule, ASG.MetaModule):
210         raise TypeError, 'symbol type mismatch: Synopsis.ASG.Module and %s both match "%s"'%(metamodule.__class__, str(module.name))
211
212      metamodule.module_declarations.append(module)
213
214      # Merge comments.
215      self.merge_comments(metamodule, module)
216
217      self.push(metamodule)
218      for d in module.declarations:
219         d.accept(self)
220      module.declarations = []
221      self.pop()
222
223
224   def visit_group(self, group):
225
226      previous = self.lookup(group.name)
227      if not previous:
228         self.append(group)
229      elif isinstance(previous, ASG.Group):
230         previous.declarations.append(group.declarations)
231         self.merge_comments(previous, group)
232         group = previous
233      else:
234         raise TypeError, 'symbol type mismatch: Synopsis.ASG.Group and %s both match "%s"'%(previous.__class__, str(previous.name))
235
236      self.push(group)
237      for d in group.declarations:
238         d.accept(self)
239      self.pop()
240
241
242   def merge_comments(self, metamodule, module):
243      """Append the module comments into the metamodule."""
244
245      if module.annotations.has_key('comments'):
246         new_comments = module.annotations['comments']
247         metamodule.annotations.setdefault('comments', [])
248         comments = metamodule.annotations['comments']
249         if comments[-len(new_comments):] != new_comments:
250            comments.extend(new_comments)
251
252
253   def visit_meta_module(self, module):
254
255      #hmm, we assume that the parent node is a MetaModule. Can that ever fail ?
256      metamodule = self.lookup(module.name)
257      if metamodule is None:
258         metamodule = ASG.MetaModule(module.type,module.name)
259         self.append(metamodule)
260      elif not isinstance(metamodule, ASG.MetaModule):
261         raise TypeError, 'symbol type mismatch: Synopsis.ASG.MetaModule and %s both match "%s"'%(metamodule.__class__, '::'.join(module.name))
262
263      metamodule.module_declarations.extend(module.module_declarations)
264      self.merge_comments(metamodule, module)
265      self.push(metamodule)
266      for d in module.declarations:
267         d.accept(self)
268      module.declarations = []
269      self.pop()
270
271
272   def add_declaration(self, decl):
273      """Adds a declaration to the current (top) scope.
274      If there is already a Forward declaration, then this replaces it
275      unless this is also a Forward.
276      """
277
278      name = decl.name
279      dict = self.__dicts[-1]
280      decls = self.top().declarations
281      if dict.has_key(name):
282         prev = dict[name]
283         if not isinstance(prev, ASG.Forward):
284            return
285         if not isinstance(decl, ASG.Forward):
286            decls.remove(prev)
287            decls.append(decl)
288            dict[name] = decl # overwrite prev
289         return
290      decls.append(decl)
291      dict[name] = decl
292
293   def visit_builtin(self, builtin):
294      """preserve builtins unconditionally"""
295
296      self.top().declarations.append(builtin)
297
298   def visit_named_type(self, decl):
299
300      name = decl.name
301      if self.lookup(decl.name): return
302      self.add_declaration(decl)
303
304   visit_declaration = add_declaration
305   visit_forward = add_declaration
306   visit_enum = add_declaration
307
308   def visit_function(self, func):
309      if not isinstance(self.top(), (ASG.Class, ASG.ClassTemplate)):
310         for d in self.top().declarations:
311            if not isinstance(d, ASG.Function): continue
312            if func.name == d.name:
313               return
314      ret = self.link_type(func.return_type)
315      if ret is not func.return_type:
316         func.return_type = ret
317      for param in func.parameters:
318         self.visit_parameter(param)
319      self.top().declarations.append(func)
320
321
322   visit_operation = visit_function
323
324   def visit_variable(self, var):
325
326      #if not scopedNameOkay(var.name): return
327      vt = self.link_type(var.vtype)
328      if vt is not var.vtype:
329         var.vtype = vt
330      self.add_declaration(var)
331
332   def visit_typedef(self, tdef):
333
334      alias = self.link_type(tdef.alias)
335      if alias is not tdef.alias:
336         tdef.alias = alias
337      self.add_declaration(tdef)
338
339   def visit_class(self, class_):
340
341      prev = self.lookup(class_.name)
342      if prev:
343         if isinstance(prev, ASG.Forward):
344            # Forward declaration, replace it
345            self.top().declarations.remove(prev)
346            del self.top_dict()[class_.name]
347         elif isinstance(prev, (ASG.Class, ASG.ClassTemplate)):
348            # Previous class. Would ignore duplicate but class_ may have
349            # class declarations that prev doesn't. (forward declared
350            # nested -- see ThreadData.hh for example)
351            self.push(prev)
352            for d in class_.declarations:
353               if isinstance(d, (ASG.Class, ASG.ClassTemplate)):
354                  d.accept(self)
355            self.pop()
356            return
357         else:
358            raise TypeError, 'symbol type mismatch: Synopsis.ASG.Class and %s both match "%s"'%(prev.__class__, '::'.join(class_.name))
359      self.add_declaration(class_)
360      for p in class_.parents:
361         p.accept(self)
362      declarations = class_.declarations
363      class_.declarations = []
364      self.push(class_)
365      for d in declarations:
366         d.accept(self)
367      self.pop()
368
369   def visit_inheritance(self, parent):
370
371      type = parent.parent
372      if isinstance(type, (ASG.DeclaredTypeId, ASG.UnknownTypeId)):
373         ltype = self.link_type(type)
374         if ltype is not type:
375            parent.parent = ltype
376      elif isinstance(type, ASG.ParametrizedTypeId):
377         ltype = self.link_type(type.template)
378         if ltype is not type.template:
379            # Must find a ASG.TemplateId from it
380            if not isinstance(ltype, ASG.DeclaredTypeId):
381               # Error
382               return
383            decl = ltype.declaration
384            if isinstance(decl, ASG.ClassTemplate):
385               type.template = decl.template
386      else:
387         # Unknown type in class inheritance
388         pass
389
390   def visit_parameter(self, param):
391
392      type = self.link_type(param.type)
393      if type is not param.type:
394         param.type = type
395
396   def visit_const(self, const):
397
398      ct = self.link_type(const.ctype)
399      if ct is not const.ctype:
400         const.ctype = ct
401      self.add_declaration(const)
402