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.remove_empty_modules:
 49         import ModuleFilter
 50         self.ir = ModuleFilter.ModuleFilter().process(self.ir, debug=self.debug)
 51
 52      if self.sort_modules:
 53         import ModuleSorter
 54         self.ir = ModuleSorter.ModuleSorter().process(self.ir, debug=self.debug)
 55
 56      if self.sxr_prefix:
 57         import SXRCompiler
 58         sxrcompiler = SXRCompiler.SXRCompiler(prefix=self.sxr_prefix)
 59         self.ir = sxrcompiler.process(self.ir, debug=self.debug)
 60
 61      # now deal with the sub-processors, if any
 62      output = self.output
 63      self.ir = Composite.process(self, self.ir, input=[], output='')
 64      self.output = output
 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_forward = add_declaration
307   visit_enum = add_declaration
308
309   def visit_function(self, func):
310      if not isinstance(self.top(), (ASG.Class, ASG.ClassTemplate)):
311         for d in self.top().declarations:
312            if not isinstance(d, ASG.Function): continue
313            if func.name == d.name:
314               return
315      ret = self.link_type(func.return_type)
316      if ret is not func.return_type:
317         func.return_type = ret
318      for param in func.parameters:
319         self.visit_parameter(param)
320      self.top().declarations.append(func)
321
322
323   visit_operation = visit_function
324
325   def visit_variable(self, var):
326
327      #if not scopedNameOkay(var.name): return
328      vt = self.link_type(var.vtype)
329      if vt is not var.vtype:
330         var.vtype = vt
331      self.add_declaration(var)
332
333   def visit_typedef(self, tdef):
334
335      alias = self.link_type(tdef.alias)
336      if alias is not tdef.alias:
337         tdef.alias = alias
338      self.add_declaration(tdef)
339
340   def visit_class(self, class_):
341
342      prev = self.lookup(class_.name)
343      if prev:
344         if isinstance(prev, ASG.Forward):
345            # Forward declaration, replace it
346            self.top().declarations.remove(prev)
347            del self.top_dict()[class_.name]
348         elif isinstance(prev, (ASG.Class, ASG.ClassTemplate)):
349            # Previous class. Would ignore duplicate but class_ may have
350            # class declarations that prev doesn't. (forward declared
351            # nested -- see ThreadData.hh for example)
352            self.push(prev)
353            for d in class_.declarations:
354               if isinstance(d, (ASG.Class, ASG.ClassTemplate)):
355                  d.accept(self)
356            self.pop()
357            return
358         else:
359            raise TypeError, 'symbol type mismatch: Synopsis.ASG.Class and %s both match "%s"'%(prev.__class__, '::'.join(class_.name))
360      self.add_declaration(class_)
361      for p in class_.parents:
362         p.accept(self)
363      declarations = class_.declarations
364      class_.declarations = []
365      self.push(class_)
366      for d in declarations:
367         d.accept(self)
368      self.pop()
369
370   def visit_inheritance(self, parent):
371
372      type = parent.parent
373      if isinstance(type, (ASG.DeclaredTypeId, ASG.UnknownTypeId)):
374         ltype = self.link_type(type)
375         if ltype is not type:
376            parent.parent = ltype
377      elif isinstance(type, ASG.ParametrizedTypeId):
378         ltype = self.link_type(type.template)
379         if ltype is not type.template:
380            # Must find a ASG.TemplateId from it
381            if not isinstance(ltype, ASG.DeclaredTypeId):
382               # Error
383               return
384            decl = ltype.declaration
385            if isinstance(decl, ASG.ClassTemplate):
386               type.template = decl.template
387      else:
388         # Unknown type in class inheritance
389         pass
390
391   def visit_parameter(self, param):
392
393      type = self.link_type(param.type)
394      if type is not param.type:
395         param.type = type
396
397   def visit_const(self, const):
398
399      ct = self.link_type(const.ctype)
400      if ct is not const.ctype:
401         const.ctype = ct
402      self.add_declaration(const)
403