compiler: don't require exceptions to inherit ARTIQException.

This commit is contained in:
whitequark 2015-12-31 21:54:54 +08:00
parent f9e90d90fa
commit 8aa34ee952
8 changed files with 81 additions and 86 deletions

View File

@ -85,7 +85,6 @@ class TException(types.TMono):
# * Message, which can contain substitutions {0}, {1} and {2} (str). # * Message, which can contain substitutions {0}, {1} and {2} (str).
# * Three 64-bit integers, parameterizing the message (int(width=64)). # * Three 64-bit integers, parameterizing the message (int(width=64)).
# Keep this in sync with the function ARTIQIRGenerator.alloc_exn. # Keep this in sync with the function ARTIQIRGenerator.alloc_exn.
attributes = OrderedDict([ attributes = OrderedDict([
("__name__", TStr()), ("__name__", TStr()),
@ -99,8 +98,9 @@ class TException(types.TMono):
("__param2__", TInt(types.TValue(64))), ("__param2__", TInt(types.TValue(64))),
]) ])
def __init__(self, name="Exception"): def __init__(self, name="Exception", id=0):
super().__init__(name) super().__init__(name)
self.id = id
def fn_bool(): def fn_bool():
return types.TConstructor(TBool()) return types.TConstructor(TBool())

View File

@ -42,10 +42,10 @@ class ObjectMap:
self.forward_map.values())) self.forward_map.values()))
class ASTSynthesizer: 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 = ""
self.source_buffer = source.Buffer(self.source, "<synthesized>") self.source_buffer = source.Buffer(self.source, "<synthesized>")
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.quote_function = quote_function
self.expanded_from = expanded_from self.expanded_from = expanded_from
@ -120,13 +120,15 @@ class ASTSynthesizer:
if typ in self.type_map: if typ in self.type_map:
instance_type, constructor_type = self.type_map[typ] instance_type, constructor_type = self.type_map[typ]
else: else:
instance_type = types.TInstance("{}.{}".format(typ.__module__, typ.__qualname__),
OrderedDict())
instance_type.attributes['__objectid__'] = builtins.TInt32()
if issubclass(typ, BaseException): 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) constructor_type = types.TExceptionConstructor(instance_type)
else: 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 = types.TConstructor(instance_type)
constructor_type.attributes['__objectid__'] = builtins.TInt32() constructor_type.attributes['__objectid__'] = builtins.TInt32()
instance_type.constructor = constructor_type instance_type.constructor = constructor_type
@ -522,6 +524,7 @@ class Stitcher:
def _synthesizer(self, expanded_from=None): def _synthesizer(self, expanded_from=None):
return ASTSynthesizer(expanded_from=expanded_from, return ASTSynthesizer(expanded_from=expanded_from,
object_map=self.object_map,
type_map=self.type_map, type_map=self.type_map,
value_map=self.value_map, value_map=self.value_map,
quote_function=self._quote_function) quote_function=self._quote_function)

View File

@ -999,7 +999,7 @@ class Inferencer(algorithm.Visitor):
{"typeb": printer.name(typeb)}, {"typeb": printer.name(typeb)},
locb) 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) node.name_loc, node.filter.loc, makenotes)
def _type_from_arguments(self, node, ret): def _type_from_arguments(self, node, ret):

View File

@ -1155,7 +1155,8 @@ class LLVMIRGenerator:
self.llty_of_type(ir.TExceptionTypeInfo()), None) self.llty_of_type(ir.TExceptionTypeInfo()), None)
else: else:
llclauseexnname = self.llconst_of_const( 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)) lllandingpad.add_clause(ll.CatchClause(llclauseexnname))
if typ is None: if typ is None:

View File

@ -5,6 +5,7 @@ from enum import Enum
from fractions import Fraction from fractions import Fraction
from artiq.language import core as core_language from artiq.language import core as core_language
from artiq.coredevice import exceptions
from artiq import __version__ as software_version from artiq import __version__ as software_version
@ -446,40 +447,45 @@ class CommGeneric:
self._write_header(_H2DMsgType.RPC_REPLY) self._write_header(_H2DMsgType.RPC_REPLY)
self._write_bytes(return_tags) self._write_bytes(return_tags)
self._send_rpc_value(bytearray(return_tags), result, result, service) 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() self._write_flush()
except Exception as exn: except Exception as exn:
logger.debug("rpc service: %d %r ! %r", service_id, arguments, exn) logger.debug("rpc service: %d %r ! %r", service_id, arguments, exn)
self._write_header(_H2DMsgType.RPC_EXCEPTION) 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) if hasattr(exn, 'artiq_exception'):
self._write_string(filename) exn = exn.artiq_exception
self._write_int32(line) self._write_string(exn.name)
self._write_int32(-1) # column not known self._write_string(exn.message)
self._write_string(function) 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() self._write_flush()
def _serve_exception(self, symbolizer): def _serve_exception(self, object_map, symbolizer):
name = self._read_string() name = self._read_string()
message = self._read_string() message = self._read_string()
params = [self._read_int64() for _ in range(3)] params = [self._read_int64() for _ in range(3)]
@ -493,7 +499,17 @@ class CommGeneric:
traceback = list(reversed(symbolizer(backtrace))) + \ traceback = list(reversed(symbolizer(backtrace))) + \
[(filename, line, column, function, None)] [(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): def serve(self, object_map, symbolizer):
while True: while True:
@ -501,7 +517,7 @@ class CommGeneric:
if self._read_type == _D2HMsgType.RPC_REQUEST: if self._read_type == _D2HMsgType.RPC_REQUEST:
self._serve_rpc(object_map) self._serve_rpc(object_map)
elif self._read_type == _D2HMsgType.KERNEL_EXCEPTION: elif self._read_type == _D2HMsgType.KERNEL_EXCEPTION:
self._serve_exception(symbolizer) self._serve_exception(object_map, symbolizer)
else: else:
self._read_expect(_D2HMsgType.KERNEL_FINISHED) self._read_expect(_D2HMsgType.KERNEL_FINISHED)
return return

View File

@ -1,39 +1,31 @@
import builtins
from artiq.language.core import ARTIQException from artiq.language.core import ARTIQException
class ZeroDivisionError(ARTIQException): ZeroDivisionError = builtins.ZeroDivisionError
"""Python's :class:`ZeroDivisionError`, mirrored in ARTIQ.""" ValueError = builtins.ValueError
IndexError = builtins.IndexError
class ValueError(ARTIQException): class InternalError(Exception):
"""Python's :class:`ValueError`, mirrored in ARTIQ."""
class IndexError(ARTIQException):
"""Python's :class:`IndexError`, mirrored in ARTIQ."""
class InternalError(ARTIQException):
"""Raised when the runtime encounters an internal error condition.""" """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 """Raised when the CPU fails to submit a RTIO event early enough
(with respect to the event's timestamp). (with respect to the event's timestamp).
The offending event is discarded and the RTIO core keeps operating. The offending event is discarded and the RTIO core keeps operating.
""" """
class RTIOSequenceError(Exception):
class RTIOSequenceError(ARTIQException):
"""Raised when an event is submitted on a given channel with a timestamp """Raised when an event is submitted on a given channel with a timestamp
not larger than the previous one. not larger than the previous one.
The offending event is discarded and the RTIO core keeps operating. The offending event is discarded and the RTIO core keeps operating.
""" """
class RTIOCollisionError(Exception):
class RTIOCollisionError(ARTIQException):
"""Raised when an event is submitted on a given channel with the same """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. 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. The offending event is discarded and the RTIO core keeps operating.
""" """
class RTIOOverflow(Exception):
class RTIOOverflow(ARTIQException):
"""Raised when at least one event could not be registered into the RTIO """Raised when at least one event could not be registered into the RTIO
input FIFO because it was full (CPU not reading fast enough). 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. the exception is caught, and events will be partially retrieved.
""" """
class DDSBatchError(Exception):
class DDSBatchError(ARTIQException):
"""Raised when attempting to start a DDS batch while already in a batch, """Raised when attempting to start a DDS batch while already in a batch,
or when too many commands are batched. or when too many commands are batched.
""" """

View File

@ -131,6 +131,10 @@ def run(with_file=False):
except CompileError as error: except CompileError as error:
print(error.render_string(colored=True), file=sys.stderr) print(error.render_string(colored=True), file=sys.stderr)
return return
except Exception as exn:
if hasattr(exn, 'artiq_exception'):
print(exn.artiq_exception, file=sys.stderr)
raise exn
finally: finally:
device_mgr.close_devices() device_mgr.close_devices()

View File

@ -365,41 +365,20 @@ class TerminationRequested(Exception):
pass pass
class ARTIQException(Exception): class ARTIQException:
"""Base class for exceptions raised or passed through the core device.""" """Information about an exception 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
def __init__(self, name, message, params, traceback): def __init__(self, name, message, params, traceback):
Exception.__init__(self, name, message, *params) if ':' in name:
self.name, self.message, self.params = name, message, params 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) self.traceback = list(traceback)
def __str__(self): def __str__(self):
lines = [] 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):") lines.append("Core Device Traceback (most recent call last):")
for (filename, line, column, function, address) in self.traceback: for (filename, line, column, function, address) in self.traceback:
stub_globals = {"__name__": filename, "__loader__": source_loader} stub_globals = {"__name__": filename, "__loader__": source_loader}
@ -426,4 +405,6 @@ class ARTIQException(Exception):
lines.append(" {}".format(source_line.strip() if source_line else "<unknown>")) lines.append(" {}".format(source_line.strip() if source_line else "<unknown>"))
lines.append(" {}^".format(" " * (column - indentation))) lines.append(" {}^".format(" " * (column - indentation)))
lines.append("{}({}): {}".format(self.name, self.id,
self.message.format(*self.params)))
return "\n".join(lines) return "\n".join(lines)