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