|
@@ -1,4 +1,6 @@
|
|
|
+import sys
|
|
|
import modelverse_kernel.primitives as primitive_functions
|
|
|
+import modelverse_jit.runtime as jit_runtime
|
|
|
|
|
|
class KnownRequestHandled(Exception):
|
|
|
"""An exception that signifies that a known request was handled."""
|
|
@@ -10,7 +12,7 @@ class GeneratorStackEntry(object):
|
|
|
self.generator = generator
|
|
|
self.function_name = None
|
|
|
self.source_map = None
|
|
|
- self.function_source = None
|
|
|
+ self.function_origin = None
|
|
|
self.pending_requests = None
|
|
|
self.finished_requests = True
|
|
|
self.replies = []
|
|
@@ -38,6 +40,22 @@ class GeneratorStackEntry(object):
|
|
|
self.replies = []
|
|
|
self.has_reply = False
|
|
|
|
|
|
+def format_stack_trace(stack_trace):
|
|
|
+ """Formats a list of (function name, debug info, origin) triples."""
|
|
|
+ return '\n'.join([jit_runtime.format_stack_frame(*triple) for triple in stack_trace])
|
|
|
+
|
|
|
+class UnhandledRequestHandlerException(Exception):
|
|
|
+ """The type of exception that is thrown when the request handler encounters an
|
|
|
+ unhandled exception."""
|
|
|
+ def __init__(self, inner_exception, stack_trace):
|
|
|
+ Exception.__init__(
|
|
|
+ self,
|
|
|
+ """The request handler encountered an unknown exception.\n
|
|
|
+ Inner exception: %s\n
|
|
|
+ Stack trace:\n%s\n""" % (inner_exception, format_stack_trace(stack_trace)))
|
|
|
+ self.inner_exception = inner_exception
|
|
|
+ self.stack_trace = stack_trace
|
|
|
+
|
|
|
class RequestHandler(object):
|
|
|
"""A type of object that intercepts logic-related Modelverse requests, and
|
|
|
forwards Modelverse state requests."""
|
|
@@ -109,8 +127,9 @@ class RequestHandler(object):
|
|
|
return None
|
|
|
except Exception as ex:
|
|
|
# Maybe get an exception handler to do this.
|
|
|
- if not self.handle_exception(ex):
|
|
|
- raise
|
|
|
+ stack_trace = self.handle_exception(ex)
|
|
|
+ if stack_trace is not None:
|
|
|
+ raise UnhandledRequestHandlerException(ex, stack_trace)
|
|
|
|
|
|
def set_finished_requests_flag(self):
|
|
|
"""Sets the finished_requests flag in the top-of-stack tuple."""
|
|
@@ -181,14 +200,50 @@ class RequestHandler(object):
|
|
|
('TAIL_CALL_ARGS', [applicable_handler, (exception,)])]
|
|
|
stack_entry.finished_requests = False
|
|
|
self.generator_stack.append(stack_entry)
|
|
|
- return True
|
|
|
+ return None
|
|
|
|
|
|
# We couldn't find an applicable exception handler, even after exhausting the
|
|
|
# entire exception handler stack. All is lost.
|
|
|
- # Also, clean up after ourselves.
|
|
|
- self.generator_stack = []
|
|
|
+ # Also, clean up after ourselves by unwinding the stack.
|
|
|
+ return self.unwind_stack()
|
|
|
+
|
|
|
+ def unwind_stack(self):
|
|
|
+ """Unwinds the entirety of the stack. All generators and exception handlers are
|
|
|
+ discarded. A list of (function name, debug information, source) statements is
|
|
|
+ returned."""
|
|
|
+ class UnwindStackException(Exception):
|
|
|
+ """A hard-to-catch exception that is used to make generators crash.
|
|
|
+ The exceptions they produce can then be analyzed for line numbers."""
|
|
|
+ pass
|
|
|
+
|
|
|
+ # First throw away all exception handlers. We won't be needing them any more.
|
|
|
self.exception_handlers = []
|
|
|
- return False
|
|
|
+
|
|
|
+ # Then pop every generator from the stack and make it crash.
|
|
|
+ stack_trace = []
|
|
|
+ while len(self.generator_stack) > 0:
|
|
|
+ top_entry = self.generator_stack.pop()
|
|
|
+ if top_entry.function_origin is None:
|
|
|
+ # Skip this function.
|
|
|
+ continue
|
|
|
+
|
|
|
+ try:
|
|
|
+ # Crash the generator.
|
|
|
+ top_entry.generator.throw(UnwindStackException())
|
|
|
+ except UnwindStackException:
|
|
|
+ # Find out where the exception was thrown.
|
|
|
+ _, _, exc_traceback = sys.exc_info()
|
|
|
+ line_number = exc_traceback.tb_lineno
|
|
|
+ source_map = top_entry.source_map
|
|
|
+ if source_map is not None:
|
|
|
+ debug_info = source_map.get_debug_info(line_number)
|
|
|
+ else:
|
|
|
+ debug_info = None
|
|
|
+
|
|
|
+ function_name = top_entry.function_name
|
|
|
+ stack_trace.append((function_name, debug_info, top_entry.function_origin))
|
|
|
+
|
|
|
+ return stack_trace[::-1]
|
|
|
|
|
|
def pop_requests(self):
|
|
|
"""Tries to pop a batch of Modelverse _state_ requests from the
|
|
@@ -353,6 +408,6 @@ class RequestHandler(object):
|
|
|
# encountered.
|
|
|
# Format: ("DEBUG_INFO", [function_name, source_map])
|
|
|
top_entry = self.generator_stack[-1]
|
|
|
- top_entry.function_name, top_entry.source_map, top_entry.function_source = request_args
|
|
|
+ top_entry.function_name, top_entry.source_map, top_entry.function_origin = request_args
|
|
|
top_entry.append_reply(None)
|
|
|
|