summaryrefslogtreecommitdiff
path: root/Zend/zend_exceptions.c
diff options
context:
space:
mode:
authorAaron Piotrowski <aaron@trowski.com>2015-05-16 15:30:59 -0500
committerAaron Piotrowski <aaron@trowski.com>2015-05-16 15:30:59 -0500
commitd042d0880796cfe99262bb6fa44225e984c63ace (patch)
treeeb428c9c3d43c4ab9c230b86eeaceccd01e42120 /Zend/zend_exceptions.c
parentc9f27ee4227268bc74fc54e0e06102317e614804 (diff)
downloadphp-git-d042d0880796cfe99262bb6fa44225e984c63ace.tar.gz
Remodel exceptions based on Throwable interface
Added Throwable interface that exceptions must implement in order to be thrown. BaseException was removed, EngineException renamed to Error, and TypeException and ParseException renamed to TypeError and ParseError. Exception and Error no longer extend a common base class, rather they both implement the Throwable interface.
Diffstat (limited to 'Zend/zend_exceptions.c')
-rw-r--r--Zend/zend_exceptions.c216
1 files changed, 113 insertions, 103 deletions
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index 671c09cd60..484fcdca60 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -30,33 +30,43 @@
#include "zend_dtrace.h"
#include "zend_smart_str.h"
-static zend_class_entry *base_exception_ce;
static zend_class_entry *default_exception_ce;
static zend_class_entry *error_exception_ce;
-static zend_class_entry *engine_exception_ce;
-static zend_class_entry *parse_exception_ce;
-static zend_class_entry *type_exception_ce;
+static zend_class_entry *error_ce;
+static zend_class_entry *parse_error_ce;
+static zend_class_entry *type_error_ce;
static zend_object_handlers default_exception_handlers;
ZEND_API void (*zend_throw_exception_hook)(zval *ex);
+ZEND_API zend_class_entry *zend_get_exception_base(zval *object)
+{
+ if (instanceof_function(Z_OBJCE_P(object), error_ce)) {
+ return error_ce;
+ }
+
+ return default_exception_ce;
+}
+
void zend_exception_set_previous(zend_object *exception, zend_object *add_previous)
{
zval tmp, *previous, zv, *pzv, rv;
+ zend_class_entry *base_ce;
if (exception == add_previous || !add_previous || !exception) {
return;
}
ZVAL_OBJ(&tmp, add_previous);
- if (!instanceof_function(Z_OBJCE(tmp), base_exception_ce)) {
+ if (!instanceof_function(Z_OBJCE(tmp), zend_ce_throwable)) {
zend_error_noreturn(E_CORE_ERROR, "Cannot set non exception as previous exception");
return;
}
ZVAL_OBJ(&zv, exception);
pzv = &zv;
do {
- previous = zend_read_property(base_exception_ce, pzv, "previous", sizeof("previous")-1, 1, &rv);
+ base_ce = zend_get_exception_base(pzv);
+ previous = zend_read_property(base_ce, pzv, "previous", sizeof("previous")-1, 1, &rv);
if (Z_TYPE_P(previous) == IS_NULL) {
- zend_update_property(base_exception_ce, pzv, "previous", sizeof("previous")-1, &tmp);
+ zend_update_property(base_ce, pzv, "previous", sizeof("previous")-1, &tmp);
GC_REFCOUNT(add_previous)--;
return;
}
@@ -110,7 +120,7 @@ ZEND_API void zend_throw_exception_internal(zval *exception) /* {{{ */
}
}
if (!EG(current_execute_data)) {
- if (exception && Z_OBJCE_P(exception) == parse_exception_ce) {
+ if (exception && Z_OBJCE_P(exception) == parse_error_ce) {
return;
}
if(EG(exception)) {
@@ -158,6 +168,7 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type,
zval obj;
zend_object *object;
zval trace;
+ zend_class_entry *base_ce;
Z_OBJ(obj) = object = zend_objects_new(class_type);
Z_OBJ_HT(obj) = &default_exception_handlers;
@@ -170,15 +181,17 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type,
array_init(&trace);
}
Z_SET_REFCOUNT(trace, 0);
+
+ base_ce = zend_get_exception_base(&obj);
- if (EXPECTED(class_type != parse_exception_ce)) {
- zend_update_property_string(base_exception_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename());
- zend_update_property_long(base_exception_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno());
+ if (EXPECTED(class_type != parse_error_ce)) {
+ zend_update_property_string(base_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename());
+ zend_update_property_long(base_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno());
} else {
- zend_update_property_string(base_exception_ce, &obj, "file", sizeof("file")-1, zend_get_compiled_filename()->val);
- zend_update_property_long(base_exception_ce, &obj, "line", sizeof("line")-1, zend_get_compiled_lineno());
+ zend_update_property_string(base_ce, &obj, "file", sizeof("file")-1, zend_get_compiled_filename()->val);
+ zend_update_property_long(base_ce, &obj, "line", sizeof("line")-1, zend_get_compiled_lineno());
}
- zend_update_property(base_exception_ce, &obj, "trace", sizeof("trace")-1, &trace);
+ zend_update_property(base_ce, &obj, "trace", sizeof("trace")-1, &trace);
return object;
}
@@ -196,6 +209,12 @@ static zend_object *zend_error_exception_new(zend_class_entry *class_type) /* {{
}
/* }}} */
+static zval *zend_get_exception_property(zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv) /* {{{ */
+{
+ return zend_read_property(zend_get_exception_base(object), object, name, name_length, silent, rv);
+}
+/* }}} */
+
/* {{{ proto Exception Exception::__clone()
Clone the exception object */
ZEND_METHOD(exception, __clone)
@@ -212,25 +231,27 @@ ZEND_METHOD(exception, __construct)
zend_string *message = NULL;
zend_long code = 0;
zval *object, *previous = NULL;
+ zend_class_entry *base_ce;
int argc = ZEND_NUM_ARGS();
- if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SlO!", &message, &code, &previous, base_exception_ce) == FAILURE) {
+ object = getThis();
+ base_ce = zend_get_exception_base(object);
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SlO!", &message, &code, &previous, base_ce) == FAILURE) {
zend_error(E_EXCEPTION | E_ERROR, "Wrong parameters for Exception([string $exception [, long $code [, Exception $previous = NULL]]])");
return;
}
- object = getThis();
-
if (message) {
- zend_update_property_str(base_exception_ce, object, "message", sizeof("message")-1, message);
+ zend_update_property_str(base_ce, object, "message", sizeof("message")-1, message);
}
if (code) {
- zend_update_property_long(base_exception_ce, object, "code", sizeof("code")-1, code);
+ zend_update_property_long(base_ce, object, "code", sizeof("code")-1, code);
}
if (previous) {
- zend_update_property(base_exception_ce, object, "previous", sizeof("previous")-1, previous);
+ zend_update_property(base_ce, object, "previous", sizeof("previous")-1, previous);
}
}
/* }}} */
@@ -244,8 +265,8 @@ ZEND_METHOD(error_exception, __construct)
zval *object, *previous = NULL;
int argc = ZEND_NUM_ARGS();
size_t message_len, filename_len;
-
- if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|sllslO!", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, base_exception_ce) == FAILURE) {
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|sllslO!", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, default_exception_ce) == FAILURE) {
zend_error(E_EXCEPTION | E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno [, Exception $previous = NULL]]]]]])");
return;
}
@@ -253,25 +274,25 @@ ZEND_METHOD(error_exception, __construct)
object = getThis();
if (message) {
- zend_update_property_string(base_exception_ce, object, "message", sizeof("message")-1, message);
+ zend_update_property_string(default_exception_ce, object, "message", sizeof("message")-1, message);
}
if (code) {
- zend_update_property_long(base_exception_ce, object, "code", sizeof("code")-1, code);
+ zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code);
}
if (previous) {
- zend_update_property(base_exception_ce, object, "previous", sizeof("previous")-1, previous);
+ zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous);
}
- zend_update_property_long(base_exception_ce, object, "severity", sizeof("severity")-1, severity);
+ zend_update_property_long(error_exception_ce, object, "severity", sizeof("severity")-1, severity);
if (argc >= 4) {
- zend_update_property_string(base_exception_ce, object, "file", sizeof("file")-1, filename);
+ zend_update_property_string(default_exception_ce, object, "file", sizeof("file")-1, filename);
if (argc < 5) {
lineno = 0; /* invalidate lineno */
}
- zend_update_property_long(base_exception_ce, object, "line", sizeof("line")-1, lineno);
+ zend_update_property_long(default_exception_ce, object, "line", sizeof("line")-1, lineno);
}
}
/* }}} */
@@ -282,9 +303,9 @@ ZEND_METHOD(error_exception, __construct)
}
#define GET_PROPERTY(object, name) \
- zend_read_property(base_exception_ce, (object), name, sizeof(name) - 1, 0, &rv)
+ zend_get_exception_property((object), name, sizeof(name) - 1, 0, &rv)
#define GET_PROPERTY_SILENT(object, name) \
- zend_read_property(base_exception_ce, (object), name, sizeof(name) - 1, 1, &rv)
+ zend_get_exception_property((object), name, sizeof(name) - 1, 1, &rv)
/* {{{ proto string Exception::getFile()
Get the file in which the exception occurred */
@@ -551,12 +572,17 @@ ZEND_METHOD(exception, getTraceAsString)
{
zval *trace, *frame, rv;
zend_ulong index;
+ zval *object;
+ zend_class_entry *base_ce;
smart_str str = {0};
uint32_t num = 0;
DEFAULT_0_PARAMS;
+
+ object = getThis();
+ base_ce = zend_get_exception_base(object);
- trace = zend_read_property(base_exception_ce, getThis(), "trace", sizeof("trace")-1, 1, &rv);
+ trace = zend_read_property(base_ce, getThis(), "trace", sizeof("trace")-1, 1, &rv);
if (Z_TYPE_P(trace) != IS_ARRAY) {
RETURN_FALSE;
}
@@ -618,6 +644,7 @@ zend_string *zend_strpprintf(size_t max_len, const char *format, ...) /* {{{ */
ZEND_METHOD(exception, __toString)
{
zval trace, *exception;
+ zend_class_entry *base_ce;
zend_string *str;
zend_fcall_info fci;
zval fname, rv;
@@ -675,9 +702,12 @@ ZEND_METHOD(exception, __toString)
}
zval_dtor(&fname);
+ exception = getThis();
+ base_ce = zend_get_exception_base(exception);
+
/* We store the result in the private property string so we can access
* the result in uncaught exception handlers without memleaks. */
- zend_update_property_str(base_exception_ce, getThis(), "string", sizeof("string")-1, str);
+ zend_update_property_str(base_ce, exception, "string", sizeof("string")-1, str);
RETURN_STR(str);
}
@@ -734,66 +764,48 @@ void zend_register_default_exception(void) /* {{{ */
zend_class_entry ce;
zend_property_info *prop;
- INIT_CLASS_ENTRY(ce, "BaseException", default_exception_functions);
- base_exception_ce = zend_register_internal_class(&ce);
- base_exception_ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
- base_exception_ce->create_object = NULL;
+ INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions);
+ default_exception_ce = zend_register_internal_class_ex(&ce, NULL);
+ default_exception_ce->create_object = zend_default_exception_new;
memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
default_exception_handlers.clone_obj = NULL;
-
- zend_declare_property_string(base_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
- zend_declare_property_string(base_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
- zend_declare_property_long(base_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
- zend_declare_property_null(base_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
- zend_declare_property_null(base_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
- zend_declare_property_null(base_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE);
- zend_declare_property_null(base_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE);
-
- INIT_CLASS_ENTRY(ce, "Exception", NULL);
- default_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce);
- default_exception_ce->create_object = zend_default_exception_new;
-
- /* A trick, to make visible private properties of BaseException */
- ZEND_HASH_FOREACH_PTR(&default_exception_ce->properties_info, prop) {
- if (prop->flags & ZEND_ACC_SHADOW) {
- if (prop->name->len == sizeof("\0BaseException\0string")-1) {
- prop->flags &= ~ZEND_ACC_SHADOW;
- prop->flags |= ZEND_ACC_PRIVATE;
- prop->ce = default_exception_ce;
- } else if (prop->name->len == sizeof("\0BaseException\0trace")-1) {
- prop->flags &= ~ZEND_ACC_SHADOW;
- prop->flags |= ZEND_ACC_PRIVATE;
- prop->ce = default_exception_ce;
- } else if (prop->name->len == sizeof("\0BaseException\0previous")-1) {
- prop->flags &= ~ZEND_ACC_SHADOW;
- prop->flags |= ZEND_ACC_PRIVATE;
- prop->ce = default_exception_ce;
- }
- }
- } ZEND_HASH_FOREACH_END();
+ zend_class_implements(default_exception_ce, 1, zend_ce_throwable);
+
+ zend_declare_property_string(default_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
+ zend_declare_property_string(default_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
+ zend_declare_property_long(default_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
+ zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
+ zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
+ zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE);
+ zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE);
INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce);
error_exception_ce->create_object = zend_error_exception_new;
zend_declare_property_long(error_exception_ce, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED);
- INIT_CLASS_ENTRY(ce, "EngineException", NULL);
- engine_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce);
- engine_exception_ce->create_object = zend_default_exception_new;
-
- INIT_CLASS_ENTRY(ce, "ParseException", NULL);
- parse_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce);
- parse_exception_ce->create_object = zend_default_exception_new;
+ INIT_CLASS_ENTRY(ce, "Error", default_exception_functions);
+ error_ce = zend_register_internal_class_ex(&ce, NULL);
+ error_ce->create_object = zend_default_exception_new;
+ memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ default_exception_handlers.clone_obj = NULL;
+ zend_class_implements(error_ce, 1, zend_ce_throwable);
+
+ zend_declare_property_string(error_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
+ zend_declare_property_string(error_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
+ zend_declare_property_long(error_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
+ zend_declare_property_null(error_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
+ zend_declare_property_null(error_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
+ zend_declare_property_null(error_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE);
+ zend_declare_property_null(error_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE);
- INIT_CLASS_ENTRY(ce, "TypeException", NULL);
- type_exception_ce = zend_register_internal_class_ex(&ce, engine_exception_ce);
- type_exception_ce->create_object = zend_default_exception_new;
-}
-/* }}} */
+ INIT_CLASS_ENTRY(ce, "ParseError", NULL);
+ parse_error_ce = zend_register_internal_class_ex(&ce, error_ce);
+ parse_error_ce->create_object = zend_default_exception_new;
-ZEND_API zend_class_entry *zend_exception_get_base(void) /* {{{ */
-{
- return base_exception_ce;
+ INIT_CLASS_ENTRY(ce, "TypeError", NULL);
+ type_error_ce = zend_register_internal_class_ex(&ce, error_ce);
+ type_error_ce->create_object = zend_default_exception_new;
}
/* }}} */
@@ -809,21 +821,21 @@ ZEND_API zend_class_entry *zend_get_error_exception(void) /* {{{ */
}
/* }}} */
-ZEND_API zend_class_entry *zend_get_engine_exception(void) /* {{{ */
+ZEND_API zend_class_entry *zend_get_error(void) /* {{{ */
{
- return engine_exception_ce;
+ return error_ce;
}
/* }}} */
-ZEND_API zend_class_entry *zend_get_parse_exception(void) /* {{{ */
+ZEND_API zend_class_entry *zend_get_parse_error(void) /* {{{ */
{
- return parse_exception_ce;
+ return parse_error_ce;
}
/* }}} */
-ZEND_API zend_class_entry *zend_get_type_exception(void) /* {{{ */
+ZEND_API zend_class_entry *zend_get_type_error(void) /* {{{ */
{
- return type_exception_ce;
+ return type_error_ce;
}
/* }}} */
@@ -833,8 +845,8 @@ ZEND_API zend_object *zend_throw_exception(zend_class_entry *exception_ce, const
zval ex;
if (exception_ce) {
- if (!instanceof_function(exception_ce, base_exception_ce)) {
- zend_error(E_NOTICE, "Exceptions must be derived from the Exception base class");
+ if (!instanceof_function(exception_ce, zend_ce_throwable)) {
+ zend_error(E_NOTICE, "Exceptions must implement Throwable");
exception_ce = default_exception_ce;
}
} else {
@@ -844,10 +856,10 @@ ZEND_API zend_object *zend_throw_exception(zend_class_entry *exception_ce, const
if (message) {
- zend_update_property_string(base_exception_ce, &ex, "message", sizeof("message")-1, message);
+ zend_update_property_string(exception_ce, &ex, "message", sizeof("message")-1, message);
}
if (code) {
- zend_update_property_long(base_exception_ce, &ex, "code", sizeof("code")-1, code);
+ zend_update_property_long(exception_ce, &ex, "code", sizeof("code")-1, code);
}
zend_throw_exception_internal(&ex);
@@ -875,7 +887,7 @@ ZEND_API zend_object *zend_throw_error_exception(zend_class_entry *exception_ce,
zval ex;
zend_object *obj = zend_throw_exception(exception_ce, message, code);
ZVAL_OBJ(&ex, obj);
- zend_update_property_long(base_exception_ce, &ex, "severity", sizeof("severity")-1, severity);
+ zend_update_property_long(error_exception_ce, &ex, "severity", sizeof("severity")-1, severity);
return obj;
}
/* }}} */
@@ -908,13 +920,13 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */
ZVAL_OBJ(&exception, ex);
ce_exception = Z_OBJCE(exception);
EG(exception) = NULL;
- if (ce_exception == parse_exception_ce || ce_exception == type_exception_ce) {
+ if (ce_exception == parse_error_ce || ce_exception == type_error_ce) {
zend_string *message = zval_get_string(GET_PROPERTY(&exception, "message"));
zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, "file"));
zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, "line"));
zend_long code = zval_get_long(GET_PROPERTY_SILENT(&exception, "code"));
- if (ce_exception == type_exception_ce && strstr(message->val, ", called in ")) {
+ if (ce_exception == type_error_ce && strstr(message->val, ", called in ")) {
zend_error_helper(code, file->val, line, "%s and defined", message->val);
} else {
zend_error_helper(code, file->val, line, "%s", message->val);
@@ -922,7 +934,7 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */
zend_string_release(file);
zend_string_release(message);
- } else if (instanceof_function(ce_exception, base_exception_ce)) {
+ } else if (instanceof_function(ce_exception, default_exception_ce) || instanceof_function(ce_exception, error_ce)) {
zval tmp, rv;
zend_string *str, *file = NULL;
zend_long line = 0;
@@ -932,7 +944,7 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */
if (Z_TYPE(tmp) != IS_STRING) {
zend_error(E_WARNING, "%s::__toString() must return a string", ce_exception->name->val);
} else {
- zend_update_property_string(base_exception_ce, &exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name->val : Z_STRVAL(tmp));
+ zend_update_property_string(zend_get_exception_base(&exception), &exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name->val : Z_STRVAL(tmp));
}
}
zval_ptr_dtor(&tmp);
@@ -942,10 +954,8 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */
ZVAL_OBJ(&zv, EG(exception));
/* do the best we can to inform about the inner exception */
- if (instanceof_function(ce_exception, base_exception_ce)) {
- file = zval_get_string(GET_PROPERTY_SILENT(&zv, "file"));
- line = zval_get_long(GET_PROPERTY_SILENT(&zv, "line"));
- }
+ file = zval_get_string(GET_PROPERTY_SILENT(&zv, "file"));
+ line = zval_get_long(GET_PROPERTY_SILENT(&zv, "line"));
zend_error_va(E_WARNING, (file && file->len > 0) ? file->val : NULL, line,
"Uncaught %s in exception handling during call to %s::__tostring()",
@@ -983,8 +993,8 @@ ZEND_API void zend_throw_exception_object(zval *exception) /* {{{ */
exception_ce = Z_OBJCE_P(exception);
- if (!exception_ce || !instanceof_function(exception_ce, base_exception_ce)) {
- zend_error(E_EXCEPTION | E_ERROR, "Exceptions must be valid objects derived from the Exception base class");
+ if (!exception_ce || !instanceof_function(exception_ce, zend_ce_throwable)) {
+ zend_error(E_EXCEPTION | E_ERROR, "Exceptions must be valid objects implementing Throwable");
return;
}
zend_throw_exception_internal(exception);