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