source_map.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. """Defines source maps: dictionaries that map lines in generated source to debug information."""
  2. class SourceMapBase(object):
  3. """Define a base class for source maps: objects that can convert line numbers
  4. to debug information."""
  5. def get_debug_info(self, line_number):
  6. """Gets the debug information for the given line number, or None if no debug info was
  7. found."""
  8. raise NotImplementedError()
  9. class SourceMap(SourceMapBase):
  10. """A source map, which converts generated source lines to debug information."""
  11. def __init__(self):
  12. SourceMapBase.__init__(self)
  13. self.lines = {}
  14. def map_line(self, line_number, debug_info):
  15. """Assigns the given debug information to the given line."""
  16. self.lines[line_number] = debug_info
  17. def get_debug_info(self, line_number):
  18. """Gets the debug information for the given line number, or None if no debug info was
  19. found."""
  20. if line_number in self.lines:
  21. return self.lines[line_number]
  22. else:
  23. return None
  24. def __str__(self):
  25. return '\n'.join(
  26. ['%d: %s' % pair
  27. for pair in sorted(self.lines.items(), key=lambda (key, _): key)])
  28. class SourceMapBuilder(object):
  29. """A type of object that makes it easy to build source maps for hierarchical instructions."""
  30. def __init__(self, initial_line_number=0):
  31. self.source_map = SourceMap()
  32. self.debug_info_stack = []
  33. self.line_number = initial_line_number - 1
  34. def push_debug_info(self, debug_info):
  35. """Informs the source map that subsequent lines of code will have the given debug
  36. information associated with them."""
  37. self.debug_info_stack.append(debug_info)
  38. def pop_debug_info(self):
  39. """Informs the source map that the debug information that was pushed last should
  40. be discarded in favor of the debug information that was pushed onto the stack
  41. just prior to it."""
  42. return self.debug_info_stack.pop()
  43. def append_line(self):
  44. """Has the source map builder increment its line number counter, and assigns the debug
  45. information that is at the top of the debug information stack to that line."""
  46. self.line_number += 1
  47. if len(self.debug_info_stack) > 0:
  48. self.source_map.map_line(self.line_number, self.debug_info_stack[-1])
  49. def __str__(self):
  50. return str(self.source_map)
  51. class ManualSourceMap(SourceMapBase):
  52. """A source map whose debug information can be set and reset. Line numbers are ignored."""
  53. def __init__(self):
  54. SourceMapBase.__init__(self)
  55. self.debug_information = None
  56. def get_debug_info(self, _):
  57. """Gets the debug information for the given line number, or None if no debug info was
  58. found."""
  59. return self.debug_information
  60. def __str__(self):
  61. return str(self.debug_information)
  62. class InterpreterSourceMap(SourceMapBase):
  63. """A source map that extracts debug information from the reference interpreter's debug info
  64. stack. Line numbers are ignored."""
  65. def __init__(self, kernel, task_name, stack_index):
  66. SourceMapBase.__init__(self)
  67. self.kernel = kernel
  68. self.task_name = task_name
  69. self.stack_index = stack_index
  70. def get_debug_info(self, _):
  71. """Gets the debug information for the given line number, or None if no debug info was
  72. found."""
  73. return self.kernel.debug_info[self.task_name][self.stack_index]
  74. def __str__(self):
  75. return str(self.get_debug_info(None))