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