From 8aa34ee9525e5e76b21130d27dfe1e42035653ea Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 31 Dec 2015 21:54:54 +0800 Subject: [PATCH] compiler: don't require exceptions to inherit ARTIQException. --- artiq/compiler/builtins.py | 4 +- artiq/compiler/embedding.py | 15 ++-- artiq/compiler/transforms/inferencer.py | 2 +- .../compiler/transforms/llvm_ir_generator.py | 3 +- artiq/coredevice/comm_generic.py | 70 ++++++++++++------- artiq/coredevice/exceptions.py | 30 +++----- artiq/frontend/artiq_run.py | 4 ++ artiq/language/core.py | 39 +++-------- 8 files changed, 81 insertions(+), 86 deletions(-) diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 9ae3e22b1..8f5526a1c 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -85,7 +85,6 @@ class TException(types.TMono): # * Message, which can contain substitutions {0}, {1} and {2} (str). # * Three 64-bit integers, parameterizing the message (int(width=64)). - # Keep this in sync with the function ARTIQIRGenerator.alloc_exn. attributes = OrderedDict([ ("__name__", TStr()), @@ -99,8 +98,9 @@ class TException(types.TMono): ("__param2__", TInt(types.TValue(64))), ]) - def __init__(self, name="Exception"): + def __init__(self, name="Exception", id=0): super().__init__(name) + self.id = id def fn_bool(): return types.TConstructor(TBool()) diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 87941b13c..4e256fd09 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -42,10 +42,10 @@ class ObjectMap: self.forward_map.values())) class ASTSynthesizer: - def __init__(self, type_map, value_map, quote_function=None, expanded_from=None): + def __init__(self, object_map, type_map, value_map, quote_function=None, expanded_from=None): self.source = "" self.source_buffer = source.Buffer(self.source, "") - self.type_map, self.value_map = type_map, value_map + self.object_map, self.type_map, self.value_map = object_map, type_map, value_map self.quote_function = quote_function self.expanded_from = expanded_from @@ -120,13 +120,15 @@ class ASTSynthesizer: if typ in self.type_map: instance_type, constructor_type = self.type_map[typ] else: - instance_type = types.TInstance("{}.{}".format(typ.__module__, typ.__qualname__), - OrderedDict()) - instance_type.attributes['__objectid__'] = builtins.TInt32() - if issubclass(typ, BaseException): + instance_type = builtins.TException("{}.{}".format(typ.__module__, typ.__qualname__), + id=self.object_map.store(typ)) constructor_type = types.TExceptionConstructor(instance_type) else: + instance_type = types.TInstance("{}.{}".format(typ.__module__, typ.__qualname__), + OrderedDict()) + instance_type.attributes['__objectid__'] = builtins.TInt32() + constructor_type = types.TConstructor(instance_type) constructor_type.attributes['__objectid__'] = builtins.TInt32() instance_type.constructor = constructor_type @@ -522,6 +524,7 @@ class Stitcher: def _synthesizer(self, expanded_from=None): return ASTSynthesizer(expanded_from=expanded_from, + object_map=self.object_map, type_map=self.type_map, value_map=self.value_map, quote_function=self._quote_function) diff --git a/artiq/compiler/transforms/inferencer.py b/artiq/compiler/transforms/inferencer.py index eab103d41..3169ed3dd 100644 --- a/artiq/compiler/transforms/inferencer.py +++ b/artiq/compiler/transforms/inferencer.py @@ -999,7 +999,7 @@ class Inferencer(algorithm.Visitor): {"typeb": printer.name(typeb)}, locb) ] - self._unify(node.name_type, builtins.TException(node.filter.type.name), + self._unify(node.name_type, node.filter.type.instance, node.name_loc, node.filter.loc, makenotes) def _type_from_arguments(self, node, ret): diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 68670ac62..4743d1649 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1155,7 +1155,8 @@ class LLVMIRGenerator: self.llty_of_type(ir.TExceptionTypeInfo()), None) else: llclauseexnname = self.llconst_of_const( - ir.Constant(typ.name, ir.TExceptionTypeInfo())) + ir.Constant("{}:{}".format(typ.id, typ.name), + ir.TExceptionTypeInfo())) lllandingpad.add_clause(ll.CatchClause(llclauseexnname)) if typ is None: diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 8c86b27b9..8e72f9720 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -5,6 +5,7 @@ from enum import Enum from fractions import Fraction from artiq.language import core as core_language +from artiq.coredevice import exceptions from artiq import __version__ as software_version @@ -446,40 +447,45 @@ class CommGeneric: self._write_header(_H2DMsgType.RPC_REPLY) self._write_bytes(return_tags) self._send_rpc_value(bytearray(return_tags), result, result, service) - self._write_flush() - except core_language.ARTIQException as exn: - logger.debug("rpc service: %d %r ! %r", service_id, arguments, exn) - - self._write_header(_H2DMsgType.RPC_EXCEPTION) - self._write_string(exn.name) - self._write_string(exn.message) - for index in range(3): - self._write_int64(exn.param[index]) - - self._write_string(exn.filename) - self._write_int32(exn.line) - self._write_int32(exn.column) - self._write_string(exn.function) - self._write_flush() except Exception as exn: logger.debug("rpc service: %d %r ! %r", service_id, arguments, exn) self._write_header(_H2DMsgType.RPC_EXCEPTION) - self._write_string(type(exn).__name__) - self._write_string(str(exn)) - for index in range(3): - self._write_int64(0) - (_, (filename, line, function, _), ) = traceback.extract_tb(exn.__traceback__, 2) - self._write_string(filename) - self._write_int32(line) - self._write_int32(-1) # column not known - self._write_string(function) + if hasattr(exn, 'artiq_exception'): + exn = exn.artiq_exception + self._write_string(exn.name) + self._write_string(exn.message) + for index in range(3): + self._write_int64(exn.param[index]) + + filename, line, column, function = exn.traceback[-1] + self._write_string(filename) + self._write_int32(line) + self._write_int32(column) + self._write_string(function) + else: + exn_type = type(exn) + if exn_type in (ZeroDivisionError, ValueError, IndexError): + self._write_string("0:{}".format(exn_type.__name__)) + else: + exn_id = object_map.store(exn_type) + self._write_string("{}:{}.{}".format(exn_id, + exn_type.__module__, exn_type.__qualname__)) + self._write_string(str(exn)) + for index in range(3): + self._write_int64(0) + + (_, (filename, line, function, _), ) = traceback.extract_tb(exn.__traceback__, 2) + self._write_string(filename) + self._write_int32(line) + self._write_int32(-1) # column not known + self._write_string(function) self._write_flush() - def _serve_exception(self, symbolizer): + def _serve_exception(self, object_map, symbolizer): name = self._read_string() message = self._read_string() params = [self._read_int64() for _ in range(3)] @@ -493,7 +499,17 @@ class CommGeneric: traceback = list(reversed(symbolizer(backtrace))) + \ [(filename, line, column, function, None)] - raise core_language.ARTIQException(name, message, params, traceback) + exception = core_language.ARTIQException(name, message, params, traceback) + + if hasattr(exceptions, exception.name): + python_exn_type = getattr(exceptions, exception.name) + else: + assert exception.id != 0 + python_exn_type = object_map.retrieve(exception.id) + + python_exn = python_exn_type(message) + python_exn.artiq_exception = exception + raise python_exn def serve(self, object_map, symbolizer): while True: @@ -501,7 +517,7 @@ class CommGeneric: if self._read_type == _D2HMsgType.RPC_REQUEST: self._serve_rpc(object_map) elif self._read_type == _D2HMsgType.KERNEL_EXCEPTION: - self._serve_exception(symbolizer) + self._serve_exception(object_map, symbolizer) else: self._read_expect(_D2HMsgType.KERNEL_FINISHED) return diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index d07473a17..c595d2fcc 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -1,39 +1,31 @@ +import builtins from artiq.language.core import ARTIQException -class ZeroDivisionError(ARTIQException): - """Python's :class:`ZeroDivisionError`, mirrored in ARTIQ.""" +ZeroDivisionError = builtins.ZeroDivisionError +ValueError = builtins.ValueError +IndexError = builtins.IndexError -class ValueError(ARTIQException): - """Python's :class:`ValueError`, mirrored in ARTIQ.""" - - -class IndexError(ARTIQException): - """Python's :class:`IndexError`, mirrored in ARTIQ.""" - - -class InternalError(ARTIQException): +class InternalError(Exception): """Raised when the runtime encounters an internal error condition.""" -class RTIOUnderflow(ARTIQException): +class RTIOUnderflow(Exception): """Raised when the CPU fails to submit a RTIO event early enough (with respect to the event's timestamp). The offending event is discarded and the RTIO core keeps operating. """ - -class RTIOSequenceError(ARTIQException): +class RTIOSequenceError(Exception): """Raised when an event is submitted on a given channel with a timestamp not larger than the previous one. The offending event is discarded and the RTIO core keeps operating. """ - -class RTIOCollisionError(ARTIQException): +class RTIOCollisionError(Exception): """Raised when an event is submitted on a given channel with the same coarse timestamp as the previous one but with a different fine timestamp. @@ -44,8 +36,7 @@ class RTIOCollisionError(ARTIQException): The offending event is discarded and the RTIO core keeps operating. """ - -class RTIOOverflow(ARTIQException): +class RTIOOverflow(Exception): """Raised when at least one event could not be registered into the RTIO input FIFO because it was full (CPU not reading fast enough). @@ -54,8 +45,7 @@ class RTIOOverflow(ARTIQException): the exception is caught, and events will be partially retrieved. """ - -class DDSBatchError(ARTIQException): +class DDSBatchError(Exception): """Raised when attempting to start a DDS batch while already in a batch, or when too many commands are batched. """ diff --git a/artiq/frontend/artiq_run.py b/artiq/frontend/artiq_run.py index b20e7de94..0aa03ca33 100755 --- a/artiq/frontend/artiq_run.py +++ b/artiq/frontend/artiq_run.py @@ -131,6 +131,10 @@ def run(with_file=False): except CompileError as error: print(error.render_string(colored=True), file=sys.stderr) return + except Exception as exn: + if hasattr(exn, 'artiq_exception'): + print(exn.artiq_exception, file=sys.stderr) + raise exn finally: device_mgr.close_devices() diff --git a/artiq/language/core.py b/artiq/language/core.py index 6c6985e42..f32b598ee 100644 --- a/artiq/language/core.py +++ b/artiq/language/core.py @@ -365,41 +365,20 @@ class TerminationRequested(Exception): pass -class ARTIQException(Exception): - """Base class for exceptions raised or passed through the core device.""" - - # Try and create an instance of the specific class, if one exists. - def __new__(cls, name, message, params, traceback): - def find_subclass(cls): - if cls.__name__ == name: - return cls - else: - for subclass in cls.__subclasses__(): - cls = find_subclass(subclass) - if cls is not None: - return cls - - more_specific_cls = find_subclass(cls) - if more_specific_cls is None: - more_specific_cls = cls - - exn = Exception.__new__(more_specific_cls) - exn.__init__(name, message, params, traceback) - return exn +class ARTIQException: + """Information about an exception raised or passed through the core device.""" def __init__(self, name, message, params, traceback): - Exception.__init__(self, name, message, *params) - self.name, self.message, self.params = name, message, params + if ':' in name: + exn_id, self.name = name.split(':', 2) + self.id = host_int(exn_id) + else: + self.id, self.name = 0, name + self.message, self.params = message, params self.traceback = list(traceback) def __str__(self): lines = [] - - if type(self).__name__ == self.name: - lines.append(self.message.format(*self.params)) - else: - lines.append("({}) {}".format(self.name, self.message.format(*self.params))) - lines.append("Core Device Traceback (most recent call last):") for (filename, line, column, function, address) in self.traceback: stub_globals = {"__name__": filename, "__loader__": source_loader} @@ -426,4 +405,6 @@ class ARTIQException(Exception): lines.append(" {}".format(source_line.strip() if source_line else "")) lines.append(" {}^".format(" " * (column - indentation))) + lines.append("{}({}): {}".format(self.name, self.id, + self.message.format(*self.params))) return "\n".join(lines)