1/*
2   +----------------------------------------------------------------------+
3   | Zend Engine                                                          |
4   +----------------------------------------------------------------------+
5   | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 2.00 of the Zend license,     |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.zend.com/license/2_00.txt.                                |
11   | If you did not receive a copy of the Zend license and are unable to  |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@zend.com so we can mail you a copy immediately.              |
14   +----------------------------------------------------------------------+
15   | Authors: Andi Gutmans <andi@php.net>                                 |
16   |          Marcus Boerger <helly@php.net>                              |
17   |          Sterling Hughes <sterling@php.net>                          |
18   |          Zeev Suraski <zeev@php.net>                                 |
19   +----------------------------------------------------------------------+
20*/
21
22#include "zend.h"
23#include "zend_API.h"
24#include "zend_builtin_functions.h"
25#include "zend_interfaces.h"
26#include "zend_exceptions.h"
27#include "zend_vm.h"
28#include "zend_dtrace.h"
29#include "zend_smart_str.h"
30#include "zend_exceptions_arginfo.h"
31#include "zend_observer.h"
32
33ZEND_API zend_class_entry *zend_ce_throwable;
34ZEND_API zend_class_entry *zend_ce_exception;
35ZEND_API zend_class_entry *zend_ce_error_exception;
36ZEND_API zend_class_entry *zend_ce_error;
37ZEND_API zend_class_entry *zend_ce_compile_error;
38ZEND_API zend_class_entry *zend_ce_parse_error;
39ZEND_API zend_class_entry *zend_ce_type_error;
40ZEND_API zend_class_entry *zend_ce_argument_count_error;
41ZEND_API zend_class_entry *zend_ce_value_error;
42ZEND_API zend_class_entry *zend_ce_arithmetic_error;
43ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
44ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
45
46/* Internal pseudo-exception that is not exposed to userland. */
47static zend_class_entry zend_ce_unwind_exit;
48
49ZEND_API void (*zend_throw_exception_hook)(zend_object *ex);
50
51static zend_object_handlers default_exception_handlers;
52
53/* {{{ zend_implement_throwable */
54static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type)
55{
56	if (instanceof_function(class_type, zend_ce_exception) || instanceof_function(class_type, zend_ce_error)) {
57		return SUCCESS;
58	}
59	zend_error_noreturn(E_ERROR, "Class %s cannot implement interface %s, extend %s or %s instead",
60		ZSTR_VAL(class_type->name),
61		ZSTR_VAL(interface->name),
62		ZSTR_VAL(zend_ce_exception->name),
63		ZSTR_VAL(zend_ce_error->name));
64	return FAILURE;
65}
66/* }}} */
67
68static inline zend_class_entry *i_get_exception_base(zend_object *object) /* {{{ */
69{
70	return instanceof_function(object->ce, zend_ce_exception) ? zend_ce_exception : zend_ce_error;
71}
72/* }}} */
73
74ZEND_API zend_class_entry *zend_get_exception_base(zend_object *object) /* {{{ */
75{
76	return i_get_exception_base(object);
77}
78/* }}} */
79
80void zend_exception_set_previous(zend_object *exception, zend_object *add_previous) /* {{{ */
81{
82    zval *previous, *ancestor, *ex;
83	zval  pv, zv, rv;
84	zend_class_entry *base_ce;
85
86	if (!exception || !add_previous) {
87		return;
88	}
89
90	if (exception == add_previous || zend_is_unwind_exit(add_previous)) {
91		OBJ_RELEASE(add_previous);
92		return;
93	}
94
95	ZEND_ASSERT(instanceof_function(add_previous->ce, zend_ce_throwable)
96		&& "Previous execption must implement Throwable");
97
98	ZVAL_OBJ(&pv, add_previous);
99	ZVAL_OBJ(&zv, exception);
100	ex = &zv;
101	do {
102		ancestor = zend_read_property_ex(i_get_exception_base(add_previous), add_previous, ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv);
103		while (Z_TYPE_P(ancestor) == IS_OBJECT) {
104			if (Z_OBJ_P(ancestor) == Z_OBJ_P(ex)) {
105				OBJ_RELEASE(add_previous);
106				return;
107			}
108			ancestor = zend_read_property_ex(i_get_exception_base(Z_OBJ_P(ancestor)), Z_OBJ_P(ancestor), ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv);
109		}
110		base_ce = i_get_exception_base(Z_OBJ_P(ex));
111		previous = zend_read_property_ex(base_ce, Z_OBJ_P(ex), ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv);
112		if (Z_TYPE_P(previous) == IS_NULL) {
113			zend_update_property_ex(base_ce, Z_OBJ_P(ex), ZSTR_KNOWN(ZEND_STR_PREVIOUS), &pv);
114			GC_DELREF(add_previous);
115			return;
116		}
117		ex = previous;
118	} while (Z_OBJ_P(ex) != add_previous);
119}
120/* }}} */
121
122void zend_exception_save(void) /* {{{ */
123{
124	if (EG(prev_exception)) {
125		zend_exception_set_previous(EG(exception), EG(prev_exception));
126	}
127	if (EG(exception)) {
128		EG(prev_exception) = EG(exception);
129	}
130	EG(exception) = NULL;
131}
132/* }}} */
133
134void zend_exception_restore(void) /* {{{ */
135{
136	if (EG(prev_exception)) {
137		if (EG(exception)) {
138			zend_exception_set_previous(EG(exception), EG(prev_exception));
139		} else {
140			EG(exception) = EG(prev_exception);
141		}
142		EG(prev_exception) = NULL;
143	}
144}
145/* }}} */
146
147static zend_always_inline zend_bool is_handle_exception_set() {
148	zend_execute_data *execute_data = EG(current_execute_data);
149	return !execute_data->func
150		|| !ZEND_USER_CODE(execute_data->func->common.type)
151		|| execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION;
152}
153
154ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* {{{ */
155{
156#ifdef HAVE_DTRACE
157	if (DTRACE_EXCEPTION_THROWN_ENABLED()) {
158		if (exception != NULL) {
159			DTRACE_EXCEPTION_THROWN(ZSTR_VAL(exception->ce->name));
160		} else {
161			DTRACE_EXCEPTION_THROWN(NULL);
162		}
163	}
164#endif /* HAVE_DTRACE */
165
166	if (exception != NULL) {
167		zend_object *previous = EG(exception);
168		zend_exception_set_previous(exception, EG(exception));
169		EG(exception) = exception;
170		if (previous) {
171			ZEND_ASSERT(is_handle_exception_set() && "HANDLE_EXCEPTION not set?");
172			return;
173		}
174	}
175	if (!EG(current_execute_data)) {
176		if (exception && (exception->ce == zend_ce_parse_error || exception->ce == zend_ce_compile_error)) {
177			return;
178		}
179		if (EG(exception)) {
180			zend_exception_error(EG(exception), E_ERROR);
181			zend_bailout();
182		}
183		zend_error_noreturn(E_CORE_ERROR, "Exception thrown without a stack frame");
184	}
185
186	if (zend_throw_exception_hook) {
187		zend_throw_exception_hook(exception);
188	}
189
190	if (is_handle_exception_set()) {
191		/* no need to rethrow the exception */
192		return;
193	}
194	EG(opline_before_exception) = EG(current_execute_data)->opline;
195	EG(current_execute_data)->opline = EG(exception_op);
196}
197/* }}} */
198
199ZEND_API void zend_clear_exception(void) /* {{{ */
200{
201	zend_object *exception;
202	if (EG(prev_exception)) {
203		OBJ_RELEASE(EG(prev_exception));
204		EG(prev_exception) = NULL;
205	}
206	if (!EG(exception)) {
207		return;
208	}
209	/* exception may have destructor */
210	exception = EG(exception);
211	EG(exception) = NULL;
212	OBJ_RELEASE(exception);
213	if (EG(current_execute_data)) {
214		EG(current_execute_data)->opline = EG(opline_before_exception);
215	}
216#if ZEND_DEBUG
217	EG(opline_before_exception) = NULL;
218#endif
219}
220/* }}} */
221
222static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, bool skip_top_traces) /* {{{ */
223{
224	zval tmp;
225	zval trace;
226	zend_class_entry *base_ce;
227	zend_string *filename;
228
229	zend_object *object = zend_objects_new(class_type);
230	object->handlers = &default_exception_handlers;
231
232	object_properties_init(object, class_type);
233
234	if (EG(current_execute_data)) {
235		zend_fetch_debug_backtrace(&trace,
236			skip_top_traces,
237			EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0);
238	} else {
239		array_init(&trace);
240	}
241	Z_SET_REFCOUNT(trace, 0);
242
243	base_ce = i_get_exception_base(object);
244
245	if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error)
246			|| !(filename = zend_get_compiled_filename()))) {
247		ZVAL_STRING(&tmp, zend_get_executed_filename());
248		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
249		zval_ptr_dtor(&tmp);
250		ZVAL_LONG(&tmp, zend_get_executed_lineno());
251		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
252	} else {
253		ZVAL_STR(&tmp, filename);
254		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
255		ZVAL_LONG(&tmp, zend_get_compiled_lineno());
256		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
257	}
258	zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
259
260	return object;
261}
262/* }}} */
263
264static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */
265{
266	return zend_default_exception_new_ex(class_type, 0);
267}
268/* }}} */
269
270static zend_object *zend_error_exception_new(zend_class_entry *class_type) /* {{{ */
271{
272	return zend_default_exception_new_ex(class_type, 2);
273}
274/* }}} */
275
276/* {{{ Clone the exception object */
277ZEND_COLD ZEND_METHOD(Exception, __clone)
278{
279	/* Should never be executable */
280	zend_throw_exception(NULL, "Cannot clone object using __clone()", 0);
281}
282/* }}} */
283
284/* {{{ Exception constructor */
285ZEND_METHOD(Exception, __construct)
286{
287	zend_string *message = NULL;
288	zend_long   code = 0;
289	zval  tmp, *object, *previous = NULL;
290	zend_class_entry *base_ce;
291
292	object = ZEND_THIS;
293	base_ce = i_get_exception_base(Z_OBJ_P(object));
294
295	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) {
296		RETURN_THROWS();
297	}
298
299	if (message) {
300		ZVAL_STR(&tmp, message);
301		zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
302	}
303
304	if (code) {
305		ZVAL_LONG(&tmp, code);
306		zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
307	}
308
309	if (previous) {
310		zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
311	}
312}
313/* }}} */
314
315/* {{{ Exception unserialize checks */
316#define CHECK_EXC_TYPE(id, type) \
317	pvalue = zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 1, &value); \
318	if (Z_TYPE_P(pvalue) != IS_NULL && Z_TYPE_P(pvalue) != type) { \
319		zend_unset_property(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \
320	}
321
322ZEND_METHOD(Exception, __wakeup)
323{
324	ZEND_PARSE_PARAMETERS_NONE();
325
326	zval value, *pvalue;
327	zval *object = ZEND_THIS;
328	CHECK_EXC_TYPE(ZEND_STR_MESSAGE,  IS_STRING);
329	CHECK_EXC_TYPE(ZEND_STR_STRING,   IS_STRING);
330	CHECK_EXC_TYPE(ZEND_STR_CODE,     IS_LONG);
331	CHECK_EXC_TYPE(ZEND_STR_FILE,     IS_STRING);
332	CHECK_EXC_TYPE(ZEND_STR_LINE,     IS_LONG);
333	/* The type of $trace and $previous is enforced through typed properties. */
334}
335/* }}} */
336
337/* {{{ ErrorException constructor */
338ZEND_METHOD(ErrorException, __construct)
339{
340	zend_string *message = NULL, *filename = NULL;
341	zend_long   code = 0, severity = E_ERROR, lineno;
342	zend_bool lineno_is_null = 1;
343	zval   tmp, *object, *previous = NULL;
344
345	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SllS!l!O!", &message, &code, &severity, &filename, &lineno, &lineno_is_null, &previous, zend_ce_throwable) == FAILURE) {
346		RETURN_THROWS();
347	}
348
349	object = ZEND_THIS;
350
351	if (message) {
352		ZVAL_STR_COPY(&tmp, message);
353		zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
354		zval_ptr_dtor(&tmp);
355	}
356
357	if (code) {
358		ZVAL_LONG(&tmp, code);
359		zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
360	}
361
362	if (previous) {
363		zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
364	}
365
366	ZVAL_LONG(&tmp, severity);
367	zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
368
369	if (filename) {
370		ZVAL_STR_COPY(&tmp, filename);
371		zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
372		zval_ptr_dtor(&tmp);
373	}
374
375	if (!lineno_is_null) {
376		ZVAL_LONG(&tmp, lineno);
377		zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
378	} else if (filename) {
379		ZVAL_LONG(&tmp, 0);
380		zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
381	}
382}
383/* }}} */
384
385#define GET_PROPERTY(object, id) \
386	zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 0, &rv)
387#define GET_PROPERTY_SILENT(object, id) \
388	zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 1, &rv)
389
390/* {{{ Get the file in which the exception occurred */
391ZEND_METHOD(Exception, getFile)
392{
393	zval *prop, rv;
394
395	ZEND_PARSE_PARAMETERS_NONE();
396
397	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_FILE);
398	RETURN_STR(zval_get_string(prop));
399}
400/* }}} */
401
402/* {{{ Get the line in which the exception occurred */
403ZEND_METHOD(Exception, getLine)
404{
405	zval *prop, rv;
406
407	ZEND_PARSE_PARAMETERS_NONE();
408
409	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_LINE);
410	RETURN_LONG(zval_get_long(prop));
411}
412/* }}} */
413
414/* {{{ Get the exception message */
415ZEND_METHOD(Exception, getMessage)
416{
417	zval *prop, rv;
418
419	ZEND_PARSE_PARAMETERS_NONE();
420
421	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_MESSAGE);
422	RETURN_STR(zval_get_string(prop));
423}
424/* }}} */
425
426/* {{{ Get the exception code */
427ZEND_METHOD(Exception, getCode)
428{
429	zval *prop, rv;
430
431	ZEND_PARSE_PARAMETERS_NONE();
432
433	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_CODE);
434	ZVAL_DEREF(prop);
435	ZVAL_COPY(return_value, prop);
436}
437/* }}} */
438
439/* {{{ Get the stack trace for the location in which the exception occurred */
440ZEND_METHOD(Exception, getTrace)
441{
442	zval *prop, rv;
443
444	ZEND_PARSE_PARAMETERS_NONE();
445
446	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_TRACE);
447	ZVAL_DEREF(prop);
448	ZVAL_COPY(return_value, prop);
449}
450/* }}} */
451
452/* {{{ Get the exception severity */
453ZEND_METHOD(ErrorException, getSeverity)
454{
455	zval *prop, rv;
456
457	ZEND_PARSE_PARAMETERS_NONE();
458
459	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_SEVERITY);
460	ZVAL_DEREF(prop);
461	ZVAL_COPY(return_value, prop);
462}
463/* }}} */
464
465#define TRACE_APPEND_KEY(key) do {                                          \
466		tmp = zend_hash_find(ht, key);                                      \
467		if (tmp) {                                                          \
468			if (Z_TYPE_P(tmp) != IS_STRING) {                               \
469				zend_error(E_WARNING, "Value for %s is not a string",       \
470					ZSTR_VAL(key));                                         \
471				smart_str_appends(str, "[unknown]");                        \
472			} else {                                                        \
473				smart_str_appends(str, Z_STRVAL_P(tmp));                    \
474			}                                                               \
475		} \
476	} while (0)
477
478static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
479{
480	/* the trivial way would be to do
481	 * convert_to_string_ex(arg);
482	 * append it and kill the now tmp arg.
483	 * but that could cause some E_NOTICE and also damn long lines.
484	 */
485
486	ZVAL_DEREF(arg);
487	switch (Z_TYPE_P(arg)) {
488		case IS_NULL:
489			smart_str_appends(str, "NULL, ");
490			break;
491		case IS_STRING:
492			smart_str_appendc(str, '\'');
493			smart_str_append_escaped(str, Z_STRVAL_P(arg), MIN(Z_STRLEN_P(arg), EG(exception_string_param_max_len)));
494			if (Z_STRLEN_P(arg) > EG(exception_string_param_max_len)) {
495				smart_str_appends(str, "...', ");
496			} else {
497				smart_str_appends(str, "', ");
498			}
499			break;
500		case IS_FALSE:
501			smart_str_appends(str, "false, ");
502			break;
503		case IS_TRUE:
504			smart_str_appends(str, "true, ");
505			break;
506		case IS_RESOURCE:
507			smart_str_appends(str, "Resource id #");
508			smart_str_append_long(str, Z_RES_HANDLE_P(arg));
509			smart_str_appends(str, ", ");
510			break;
511		case IS_LONG:
512			smart_str_append_long(str, Z_LVAL_P(arg));
513			smart_str_appends(str, ", ");
514			break;
515		case IS_DOUBLE: {
516			smart_str_append_printf(str, "%.*G", (int) EG(precision), Z_DVAL_P(arg));
517			smart_str_appends(str, ", ");
518			break;
519		}
520		case IS_ARRAY:
521			smart_str_appends(str, "Array, ");
522			break;
523		case IS_OBJECT: {
524			zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg));
525			smart_str_appends(str, "Object(");
526			smart_str_appends(str, ZSTR_VAL(class_name));
527			smart_str_appends(str, "), ");
528			zend_string_release_ex(class_name, 0);
529			break;
530		}
531	}
532}
533/* }}} */
534
535static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /* {{{ */
536{
537	zval *file, *tmp;
538
539	smart_str_appendc(str, '#');
540	smart_str_append_long(str, num);
541	smart_str_appendc(str, ' ');
542
543	file = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_FILE), 1);
544	if (file) {
545		if (Z_TYPE_P(file) != IS_STRING) {
546			zend_error(E_WARNING, "Function name is not a string");
547			smart_str_appends(str, "[unknown function]");
548		} else{
549			zend_long line;
550			tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_LINE), 1);
551			if (tmp) {
552				if (Z_TYPE_P(tmp) == IS_LONG) {
553					line = Z_LVAL_P(tmp);
554				} else {
555					zend_error(E_WARNING, "Line is not an int");
556					line = 0;
557				}
558			} else {
559				line = 0;
560			}
561			smart_str_append(str, Z_STR_P(file));
562			smart_str_appendc(str, '(');
563			smart_str_append_long(str, line);
564			smart_str_appends(str, "): ");
565		}
566	} else {
567		smart_str_appends(str, "[internal function]: ");
568	}
569	TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_CLASS));
570	TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_TYPE));
571	TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_FUNCTION));
572	smart_str_appendc(str, '(');
573	tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_ARGS), 1);
574	if (tmp) {
575		if (Z_TYPE_P(tmp) == IS_ARRAY) {
576			size_t last_len = ZSTR_LEN(str->s);
577			zend_string *name;
578			zval *arg;
579
580			ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), name, arg) {
581				if (name) {
582					smart_str_append(str, name);
583					smart_str_appends(str, ": ");
584				}
585				_build_trace_args(arg, str);
586			} ZEND_HASH_FOREACH_END();
587
588			if (last_len != ZSTR_LEN(str->s)) {
589				ZSTR_LEN(str->s) -= 2; /* remove last ', ' */
590			}
591		} else {
592			zend_error(E_WARNING, "args element is not an array");
593		}
594	}
595	smart_str_appends(str, ")\n");
596}
597/* }}} */
598
599/* {{{ Obtain the backtrace for the exception as a string (instead of an array) */
600ZEND_METHOD(Exception, getTraceAsString)
601{
602	zval *trace, *frame, rv;
603	zend_ulong index;
604	zval *object;
605	zend_class_entry *base_ce;
606	smart_str str = {0};
607	uint32_t num = 0;
608
609	ZEND_PARSE_PARAMETERS_NONE();
610
611	object = ZEND_THIS;
612	base_ce = i_get_exception_base(Z_OBJ_P(object));
613
614	trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
615	if (EG(exception)) {
616		RETURN_THROWS();
617	}
618
619	/* Type should be guaranteed by property type. */
620	ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY);
621	ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
622		if (Z_TYPE_P(frame) != IS_ARRAY) {
623			zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
624			continue;
625		}
626
627		_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
628	} ZEND_HASH_FOREACH_END();
629
630	smart_str_appendc(&str, '#');
631	smart_str_append_long(&str, num);
632	smart_str_appends(&str, " {main}");
633	smart_str_0(&str);
634
635	RETURN_NEW_STR(str.s);
636}
637/* }}} */
638
639/* {{{ Return previous Throwable or NULL. */
640ZEND_METHOD(Exception, getPrevious)
641{
642	zval rv;
643
644	ZEND_PARSE_PARAMETERS_NONE();
645
646	ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_PREVIOUS));
647} /* }}} */
648
649/* {{{ Obtain the string representation of the Exception object */
650ZEND_METHOD(Exception, __toString)
651{
652	zval trace, *exception;
653	zend_class_entry *base_ce;
654	zend_string *str;
655	zend_fcall_info fci;
656	zval rv, tmp;
657	zend_string *fname;
658
659	ZEND_PARSE_PARAMETERS_NONE();
660
661	str = ZSTR_EMPTY_ALLOC();
662
663	exception = ZEND_THIS;
664	fname = zend_string_init("gettraceasstring", sizeof("gettraceasstring")-1, 0);
665
666	while (exception && Z_TYPE_P(exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(exception), zend_ce_throwable)) {
667		zend_string *prev_str = str;
668		zend_string *message = zval_get_string(GET_PROPERTY(exception, ZEND_STR_MESSAGE));
669		zend_string *file = zval_get_string(GET_PROPERTY(exception, ZEND_STR_FILE));
670		zend_long line = zval_get_long(GET_PROPERTY(exception, ZEND_STR_LINE));
671
672		fci.size = sizeof(fci);
673		ZVAL_STR(&fci.function_name, fname);
674		fci.object = Z_OBJ_P(exception);
675		fci.retval = &trace;
676		fci.param_count = 0;
677		fci.params = NULL;
678		fci.named_params = NULL;
679
680		zend_call_function(&fci, NULL);
681
682		if (Z_TYPE(trace) != IS_STRING) {
683			zval_ptr_dtor(&trace);
684			ZVAL_UNDEF(&trace);
685		}
686
687		if ((Z_OBJCE_P(exception) == zend_ce_type_error || Z_OBJCE_P(exception) == zend_ce_argument_count_error) && strstr(ZSTR_VAL(message), ", called in ")) {
688			zend_string *real_message = zend_strpprintf(0, "%s and defined", ZSTR_VAL(message));
689			zend_string_release_ex(message, 0);
690			message = real_message;
691		}
692
693		if (ZSTR_LEN(message) > 0) {
694			str = zend_strpprintf(0, "%s: %s in %s:" ZEND_LONG_FMT
695					"\nStack trace:\n%s%s%s",
696					ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(message), ZSTR_VAL(file), line,
697					(Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n",
698					ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str));
699		} else {
700			str = zend_strpprintf(0, "%s in %s:" ZEND_LONG_FMT
701					"\nStack trace:\n%s%s%s",
702					ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(file), line,
703					(Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n",
704					ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str));
705		}
706
707		zend_string_release_ex(prev_str, 0);
708		zend_string_release_ex(message, 0);
709		zend_string_release_ex(file, 0);
710		zval_ptr_dtor(&trace);
711
712		Z_PROTECT_RECURSION_P(exception);
713		exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
714		if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) {
715			break;
716		}
717	}
718	zend_string_release_ex(fname, 0);
719
720	exception = ZEND_THIS;
721	/* Reset apply counts */
722	while (exception && Z_TYPE_P(exception) == IS_OBJECT && (base_ce = i_get_exception_base(Z_OBJ_P(exception))) && instanceof_function(Z_OBJCE_P(exception), base_ce)) {
723		if (Z_IS_RECURSIVE_P(exception)) {
724			Z_UNPROTECT_RECURSION_P(exception);
725		} else {
726			break;
727		}
728		exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
729	}
730
731	exception = ZEND_THIS;
732	base_ce = i_get_exception_base(Z_OBJ_P(exception));
733
734	/* We store the result in the private property string so we can access
735	 * the result in uncaught exception handlers without memleaks. */
736	ZVAL_STR(&tmp, str);
737	zend_update_property_ex(base_ce, Z_OBJ_P(exception), ZSTR_KNOWN(ZEND_STR_STRING), &tmp);
738
739	RETURN_STR(str);
740}
741/* }}} */
742
743static void declare_exception_properties(zend_class_entry *ce)
744{
745	zval val;
746
747	zend_declare_property_string(ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
748	zend_declare_property_string(ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
749	zend_declare_property_long(ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
750	zend_declare_property_null(ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
751	zend_declare_property_null(ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
752
753	ZVAL_EMPTY_ARRAY(&val);
754	zend_declare_typed_property(
755		ce, ZSTR_KNOWN(ZEND_STR_TRACE), &val, ZEND_ACC_PRIVATE, NULL,
756		(zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
757
758	ZVAL_NULL(&val);
759	zend_declare_typed_property(
760		ce, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &val, ZEND_ACC_PRIVATE, NULL,
761		(zend_type) ZEND_TYPE_INIT_CE(zend_ce_throwable, /* allow_null */ 1, 0));
762}
763
764void zend_register_default_exception(void) /* {{{ */
765{
766	zend_class_entry ce;
767
768	REGISTER_MAGIC_INTERFACE(throwable, Throwable);
769	zend_class_implements(zend_ce_throwable, 1, zend_ce_stringable);
770
771	memcpy(&default_exception_handlers, &std_object_handlers, sizeof(zend_object_handlers));
772	default_exception_handlers.clone_obj = NULL;
773
774	INIT_CLASS_ENTRY(ce, "Exception", class_Exception_methods);
775	zend_ce_exception = zend_register_internal_class_ex(&ce, NULL);
776	zend_ce_exception->create_object = zend_default_exception_new;
777	zend_class_implements(zend_ce_exception, 1, zend_ce_throwable);
778	declare_exception_properties(zend_ce_exception);
779
780	INIT_CLASS_ENTRY(ce, "ErrorException", class_ErrorException_methods);
781	zend_ce_error_exception = zend_register_internal_class_ex(&ce, zend_ce_exception);
782	zend_ce_error_exception->create_object = zend_error_exception_new;
783	zend_declare_property_long(zend_ce_error_exception, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED);
784
785	INIT_CLASS_ENTRY(ce, "Error", class_Error_methods);
786	zend_ce_error = zend_register_internal_class_ex(&ce, NULL);
787	zend_ce_error->create_object = zend_default_exception_new;
788	zend_class_implements(zend_ce_error, 1, zend_ce_throwable);
789	declare_exception_properties(zend_ce_error);
790
791	INIT_CLASS_ENTRY(ce, "CompileError", class_CompileError_methods);
792	zend_ce_compile_error = zend_register_internal_class_ex(&ce, zend_ce_error);
793	zend_ce_compile_error->create_object = zend_default_exception_new;
794
795	INIT_CLASS_ENTRY(ce, "ParseError", class_ParseError_methods);
796	zend_ce_parse_error = zend_register_internal_class_ex(&ce, zend_ce_compile_error);
797	zend_ce_parse_error->create_object = zend_default_exception_new;
798
799	INIT_CLASS_ENTRY(ce, "TypeError", class_TypeError_methods);
800	zend_ce_type_error = zend_register_internal_class_ex(&ce, zend_ce_error);
801	zend_ce_type_error->create_object = zend_default_exception_new;
802
803	INIT_CLASS_ENTRY(ce, "ArgumentCountError", class_ArgumentCountError_methods);
804	zend_ce_argument_count_error = zend_register_internal_class_ex(&ce, zend_ce_type_error);
805	zend_ce_argument_count_error->create_object = zend_default_exception_new;
806
807	INIT_CLASS_ENTRY(ce, "ValueError", class_ValueError_methods);
808	zend_ce_value_error = zend_register_internal_class_ex(&ce, zend_ce_error);
809	zend_ce_value_error->create_object = zend_default_exception_new;
810
811	INIT_CLASS_ENTRY(ce, "ArithmeticError", class_ArithmeticError_methods);
812	zend_ce_arithmetic_error = zend_register_internal_class_ex(&ce, zend_ce_error);
813	zend_ce_arithmetic_error->create_object = zend_default_exception_new;
814
815	INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods);
816	zend_ce_division_by_zero_error = zend_register_internal_class_ex(&ce, zend_ce_arithmetic_error);
817	zend_ce_division_by_zero_error->create_object = zend_default_exception_new;
818
819	INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
820
821	INIT_CLASS_ENTRY(ce, "UnhandledMatchError", NULL);
822	zend_ce_unhandled_match_error = zend_register_internal_class_ex(&ce, zend_ce_error);
823	zend_ce_unhandled_match_error->create_object = zend_default_exception_new;
824}
825/* }}} */
826
827/* {{{ Deprecated - Use zend_ce_exception directly instead */
828ZEND_API zend_class_entry *zend_exception_get_default(void)
829{
830	return zend_ce_exception;
831}
832/* }}} */
833
834/* {{{ Deprecated - Use zend_ce_error_exception directly instead */
835ZEND_API zend_class_entry *zend_get_error_exception(void)
836{
837	return zend_ce_error_exception;
838}
839/* }}} */
840
841static zend_object *zend_throw_exception_zstr(zend_class_entry *exception_ce, zend_string *message, zend_long code) /* {{{ */
842{
843	zval ex, tmp;
844
845	if (!exception_ce) {
846		exception_ce = zend_ce_exception;
847	}
848
849	ZEND_ASSERT(instanceof_function(exception_ce, zend_ce_throwable)
850		&& "Exceptions must implement Throwable");
851
852	object_init_ex(&ex, exception_ce);
853
854	if (message) {
855		ZVAL_STR(&tmp, message);
856		zend_update_property_ex(exception_ce, Z_OBJ(ex), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
857	}
858	if (code) {
859		ZVAL_LONG(&tmp, code);
860		zend_update_property_ex(exception_ce, Z_OBJ(ex), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
861	}
862
863	zend_throw_exception_internal(Z_OBJ(ex));
864
865	return Z_OBJ(ex);
866}
867/* }}} */
868
869ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) /* {{{ */
870{
871	zend_string *msg_str = message ? zend_string_init(message, strlen(message), 0) : NULL;
872	zend_object *ex = zend_throw_exception_zstr(exception_ce, msg_str, code);
873	if (msg_str) {
874		zend_string_release(msg_str);
875	}
876	return ex;
877}
878/* }}} */
879
880ZEND_API ZEND_COLD zend_object *zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format, ...) /* {{{ */
881{
882	va_list arg;
883	char *message;
884	zend_object *obj;
885
886	va_start(arg, format);
887	zend_vspprintf(&message, 0, format, arg);
888	va_end(arg);
889	obj = zend_throw_exception(exception_ce, message, code);
890	efree(message);
891	return obj;
892}
893/* }}} */
894
895ZEND_API ZEND_COLD zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, zend_string *message, zend_long code, int severity) /* {{{ */
896{
897	zval tmp;
898	zend_object *obj = zend_throw_exception_zstr(exception_ce, message, code);
899	ZVAL_LONG(&tmp, severity);
900	zend_update_property_ex(zend_ce_error_exception, obj, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
901	return obj;
902}
903/* }}} */
904
905static void zend_error_va(int type, const char *file, uint32_t lineno, const char *format, ...) /* {{{ */
906{
907	va_list args;
908	va_start(args, format);
909	zend_string *message = zend_vstrpprintf(0, format, args);
910	zend_observer_error_notify(type, file, lineno, message);
911	zend_error_cb(type, file, lineno, message);
912	zend_string_release(message);
913	va_end(args);
914}
915/* }}} */
916
917/* This function doesn't return if it uses E_ERROR */
918ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severity) /* {{{ */
919{
920	zval exception, rv;
921	zend_class_entry *ce_exception;
922	zend_result result = FAILURE;
923
924	ZVAL_OBJ(&exception, ex);
925	ce_exception = ex->ce;
926	EG(exception) = NULL;
927	if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) {
928		zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE));
929		zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
930		zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE));
931		int type = (ce_exception == zend_ce_parse_error ? E_PARSE : E_COMPILE_ERROR) | E_DONT_BAIL;
932
933		zend_observer_error_notify(type, ZSTR_VAL(file), line, message);
934		zend_error_cb(type, ZSTR_VAL(file), line, message);
935
936		zend_string_release_ex(file, 0);
937		zend_string_release_ex(message, 0);
938	} else if (instanceof_function(ce_exception, zend_ce_throwable)) {
939		zval tmp;
940		zend_string *str, *file = NULL;
941		zend_long line = 0;
942
943		zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp);
944		if (!EG(exception)) {
945			if (Z_TYPE(tmp) != IS_STRING) {
946				zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name));
947			} else {
948				zend_update_property_ex(i_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_STRING), &tmp);
949			}
950		}
951		zval_ptr_dtor(&tmp);
952
953		if (EG(exception)) {
954			zval zv;
955
956			ZVAL_OBJ(&zv, EG(exception));
957			/* do the best we can to inform about the inner exception */
958			if (instanceof_function(ce_exception, zend_ce_exception) || instanceof_function(ce_exception, zend_ce_error)) {
959				file = zval_get_string(GET_PROPERTY_SILENT(&zv, ZEND_STR_FILE));
960				line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE));
961			}
962
963			zend_error_va(E_WARNING, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
964				"Uncaught %s in exception handling during call to %s::__toString()",
965				ZSTR_VAL(Z_OBJCE(zv)->name), ZSTR_VAL(ce_exception->name));
966
967			if (file) {
968				zend_string_release_ex(file, 0);
969			}
970		}
971
972		str = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_STRING));
973		file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
974		line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE));
975
976		zend_error_va(severity | E_DONT_BAIL,
977			(file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
978			"Uncaught %s\n  thrown", ZSTR_VAL(str));
979
980		zend_string_release_ex(str, 0);
981		zend_string_release_ex(file, 0);
982	} else if (ce_exception == &zend_ce_unwind_exit) {
983		/* We successfully unwound, nothing more to do.
984		 * We still return FAILURE in this case, as further execution should still be aborted. */
985	} else {
986		zend_error(severity, "Uncaught exception %s", ZSTR_VAL(ce_exception->name));
987	}
988
989	OBJ_RELEASE(ex);
990	return result;
991}
992/* }}} */
993
994ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */
995{
996	if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) {
997		zend_error_noreturn(E_CORE_ERROR, "Need to supply an object when throwing an exception");
998	}
999
1000	zend_class_entry *exception_ce = Z_OBJCE_P(exception);
1001
1002	if (!exception_ce || !instanceof_function(exception_ce, zend_ce_throwable)) {
1003		zend_throw_error(NULL, "Cannot throw objects that do not implement Throwable");
1004		zval_ptr_dtor(exception);
1005		return;
1006	}
1007
1008	zend_throw_exception_internal(Z_OBJ_P(exception));
1009}
1010/* }}} */
1011
1012ZEND_API ZEND_COLD void zend_throw_unwind_exit(void)
1013{
1014	ZEND_ASSERT(!EG(exception));
1015	EG(exception) = zend_objects_new(&zend_ce_unwind_exit);
1016	EG(opline_before_exception) = EG(current_execute_data)->opline;
1017	EG(current_execute_data)->opline = EG(exception_op);
1018}
1019
1020ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex)
1021{
1022	return ex->ce == &zend_ce_unwind_exit;
1023}
1024