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	if (EG(prev_exception)) {
186
187		OBJ_RELEASE(EG(prev_exception));
188		EG(prev_exception) = NULL;
189	}
190	if (!EG(exception)) {
191		return;
192	}
193	OBJ_RELEASE(EG(exception));
194	EG(exception) = NULL;
195	if (EG(current_execute_data)) {
196		EG(current_execute_data)->opline = EG(opline_before_exception);
197	}
198#if ZEND_DEBUG
199	EG(opline_before_exception) = NULL;
200#endif
201}
202/* }}} */
203
204static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, int skip_top_traces) /* {{{ */
205{
206	zval obj, tmp;
207	zend_object *object;
208	zval trace;
209	zend_class_entry *base_ce;
210	zend_string *filename;
211
212	Z_OBJ(obj) = object = zend_objects_new(class_type);
213	Z_OBJ_HT(obj) = &default_exception_handlers;
214
215	object_properties_init(object, class_type);
216
217	if (EG(current_execute_data)) {
218		zend_fetch_debug_backtrace(&trace,
219			skip_top_traces,
220			EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0);
221	} else {
222		array_init(&trace);
223	}
224	Z_SET_REFCOUNT(trace, 0);
225
226	base_ce = i_get_exception_base(&obj);
227
228	if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error)
229			|| !(filename = zend_get_compiled_filename()))) {
230		ZVAL_STRING(&tmp, zend_get_executed_filename());
231		zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
232		zval_ptr_dtor(&tmp);
233		ZVAL_LONG(&tmp, zend_get_executed_lineno());
234		zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
235	} else {
236		ZVAL_STR(&tmp, filename);
237		zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
238		ZVAL_LONG(&tmp, zend_get_compiled_lineno());
239		zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
240	}
241	zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
242
243	return object;
244}
245/* }}} */
246
247static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */
248{
249	return zend_default_exception_new_ex(class_type, 0);
250}
251/* }}} */
252
253static zend_object *zend_error_exception_new(zend_class_entry *class_type) /* {{{ */
254{
255	return zend_default_exception_new_ex(class_type, 2);
256}
257/* }}} */
258
259/* {{{ proto Exception|Error Exception|Error::__clone()
260   Clone the exception object */
261ZEND_COLD ZEND_METHOD(exception, __clone)
262{
263	/* Should never be executable */
264	zend_throw_exception(NULL, "Cannot clone object using __clone()", 0);
265}
266/* }}} */
267
268/* {{{ proto Exception|Error::__construct(string message, int code [, Throwable previous])
269   Exception constructor */
270ZEND_METHOD(exception, __construct)
271{
272	zend_string *message = NULL;
273	zend_long   code = 0;
274	zval  tmp, *object, *previous = NULL;
275	zend_class_entry *base_ce;
276	int    argc = ZEND_NUM_ARGS();
277
278	object = ZEND_THIS;
279	base_ce = i_get_exception_base(object);
280
281	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) {
282		zend_class_entry *ce;
283
284		if (Z_TYPE(EX(This)) == IS_OBJECT) {
285			ce = Z_OBJCE(EX(This));
286		} else if (Z_CE(EX(This))) {
287			ce = Z_CE(EX(This));
288		} else {
289			ce = base_ce;
290		}
291		zend_throw_error(NULL, "Wrong parameters for %s([string $message [, long $code [, Throwable $previous = NULL]]])", ZSTR_VAL(ce->name));
292		RETURN_THROWS();
293	}
294
295	if (message) {
296		ZVAL_STR(&tmp, message);
297		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
298	}
299
300	if (code) {
301		ZVAL_LONG(&tmp, code);
302		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
303	}
304
305	if (previous) {
306		zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
307	}
308}
309/* }}} */
310
311/* {{{ proto Exception::__wakeup()
312   Exception unserialize checks */
313#define CHECK_EXC_TYPE(id, type) \
314	pvalue = zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 1, &value); \
315	if (Z_TYPE_P(pvalue) != IS_NULL && Z_TYPE_P(pvalue) != type) { \
316		zend_unset_property(i_get_exception_base(object), object, ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \
317	}
318
319ZEND_METHOD(exception, __wakeup)
320{
321	zval value, *pvalue;
322	zval *object = ZEND_THIS;
323	CHECK_EXC_TYPE(ZEND_STR_MESSAGE,  IS_STRING);
324	CHECK_EXC_TYPE(ZEND_STR_STRING,   IS_STRING);
325	CHECK_EXC_TYPE(ZEND_STR_CODE,     IS_LONG);
326	CHECK_EXC_TYPE(ZEND_STR_FILE,     IS_STRING);
327	CHECK_EXC_TYPE(ZEND_STR_LINE,     IS_LONG);
328	CHECK_EXC_TYPE(ZEND_STR_TRACE,    IS_ARRAY);
329	pvalue = zend_read_property(i_get_exception_base(object), object, "previous", sizeof("previous")-1, 1, &value);
330	if (pvalue && Z_TYPE_P(pvalue) != IS_NULL && (Z_TYPE_P(pvalue) != IS_OBJECT ||
331			!instanceof_function(Z_OBJCE_P(pvalue), zend_ce_throwable) ||
332			pvalue == object)) {
333		zend_unset_property(i_get_exception_base(object), object, "previous", sizeof("previous")-1);
334	}
335}
336/* }}} */
337
338/* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Throwable previous]]])
339   ErrorException constructor */
340ZEND_METHOD(error_exception, __construct)
341{
342	zend_string *message = NULL, *filename = NULL;
343	zend_long   code = 0, severity = E_ERROR, lineno;
344	zval   tmp, *object, *previous = NULL;
345	int    argc = ZEND_NUM_ARGS();
346
347	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SllSlO!", &message, &code, &severity, &filename, &lineno, &previous, zend_ce_throwable) == FAILURE) {
348		zend_class_entry *ce;
349
350		if (Z_TYPE(EX(This)) == IS_OBJECT) {
351			ce = Z_OBJCE(EX(This));
352		} else if (Z_CE(EX(This))) {
353			ce = Z_CE(EX(This));
354		} else {
355			ce = zend_ce_error_exception;
356		}
357		zend_throw_error(NULL, "Wrong parameters for %s([string $message [, long $code, [ long $severity, [ string $filename, [ long $lineno  [, Throwable $previous = NULL]]]]]])", ZSTR_VAL(ce->name));
358		RETURN_THROWS();
359	}
360
361	object = ZEND_THIS;
362
363	if (message) {
364		ZVAL_STR_COPY(&tmp, message);
365		zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
366		zval_ptr_dtor(&tmp);
367	}
368
369	if (code) {
370		ZVAL_LONG(&tmp, code);
371		zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
372	}
373
374	if (previous) {
375		zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
376	}
377
378	ZVAL_LONG(&tmp, severity);
379	zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
380
381	if (argc >= 4) {
382		ZVAL_STR_COPY(&tmp, filename);
383		zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
384		zval_ptr_dtor(&tmp);
385    	if (argc < 5) {
386    	    lineno = 0; /* invalidate lineno */
387    	}
388		ZVAL_LONG(&tmp, lineno);
389		zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
390	}
391}
392/* }}} */
393
394#define GET_PROPERTY(object, id) \
395	zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 0, &rv)
396#define GET_PROPERTY_SILENT(object, id) \
397	zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 1, &rv)
398
399/* {{{ proto string Exception|Error::getFile()
400   Get the file in which the exception occurred */
401ZEND_METHOD(exception, getFile)
402{
403	zval *prop, rv;
404
405	ZEND_PARSE_PARAMETERS_NONE();
406
407	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_FILE);
408	ZVAL_DEREF(prop);
409	ZVAL_COPY(return_value, prop);
410}
411/* }}} */
412
413/* {{{ proto int Exception|Error::getLine()
414   Get the line in which the exception occurred */
415ZEND_METHOD(exception, getLine)
416{
417	zval *prop, rv;
418
419	ZEND_PARSE_PARAMETERS_NONE();
420
421	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_LINE);
422	ZVAL_DEREF(prop);
423	ZVAL_COPY(return_value, prop);
424}
425/* }}} */
426
427/* {{{ proto string Exception|Error::getMessage()
428   Get the exception message */
429ZEND_METHOD(exception, getMessage)
430{
431	zval *prop, rv;
432
433	ZEND_PARSE_PARAMETERS_NONE();
434
435	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_MESSAGE);
436	ZVAL_DEREF(prop);
437	ZVAL_COPY(return_value, prop);
438}
439/* }}} */
440
441/* {{{ proto int Exception|Error::getCode()
442   Get the exception code */
443ZEND_METHOD(exception, getCode)
444{
445	zval *prop, rv;
446
447	ZEND_PARSE_PARAMETERS_NONE();
448
449	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_CODE);
450	ZVAL_DEREF(prop);
451	ZVAL_COPY(return_value, prop);
452}
453/* }}} */
454
455/* {{{ proto array Exception|Error::getTrace()
456   Get the stack trace for the location in which the exception occurred */
457ZEND_METHOD(exception, getTrace)
458{
459	zval *prop, rv;
460
461	ZEND_PARSE_PARAMETERS_NONE();
462
463	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_TRACE);
464	ZVAL_DEREF(prop);
465	ZVAL_COPY(return_value, prop);
466}
467/* }}} */
468
469/* {{{ proto int ErrorException::getSeverity()
470   Get the exception severity */
471ZEND_METHOD(error_exception, getSeverity)
472{
473	zval *prop, rv;
474
475	ZEND_PARSE_PARAMETERS_NONE();
476
477	prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_SEVERITY);
478	ZVAL_DEREF(prop);
479	ZVAL_COPY(return_value, prop);
480}
481/* }}} */
482
483#define TRACE_APPEND_KEY(key) do {                                          \
484		tmp = zend_hash_find(ht, key);                                      \
485		if (tmp) {                                                          \
486			if (Z_TYPE_P(tmp) != IS_STRING) {                               \
487				zend_error(E_WARNING, "Value for %s is no string",          \
488					ZSTR_VAL(key));                                         \
489				smart_str_appends(str, "[unknown]");                        \
490			} else {                                                        \
491				smart_str_appends(str, Z_STRVAL_P(tmp));                    \
492			}                                                               \
493		} \
494	} while (0)
495
496static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
497{
498	/* the trivial way would be to do
499	 * convert_to_string_ex(arg);
500	 * append it and kill the now tmp arg.
501	 * but that could cause some E_NOTICE and also damn long lines.
502	 */
503
504	ZVAL_DEREF(arg);
505	switch (Z_TYPE_P(arg)) {
506		case IS_NULL:
507			smart_str_appends(str, "NULL, ");
508			break;
509		case IS_STRING:
510			smart_str_appendc(str, '\'');
511			smart_str_append_escaped(str, Z_STRVAL_P(arg), MIN(Z_STRLEN_P(arg), 15));
512			if (Z_STRLEN_P(arg) > 15) {
513				smart_str_appends(str, "...', ");
514			} else {
515				smart_str_appends(str, "', ");
516			}
517			break;
518		case IS_FALSE:
519			smart_str_appends(str, "false, ");
520			break;
521		case IS_TRUE:
522			smart_str_appends(str, "true, ");
523			break;
524		case IS_RESOURCE:
525			smart_str_appends(str, "Resource id #");
526			smart_str_append_long(str, Z_RES_HANDLE_P(arg));
527			smart_str_appends(str, ", ");
528			break;
529		case IS_LONG:
530			smart_str_append_long(str, Z_LVAL_P(arg));
531			smart_str_appends(str, ", ");
532			break;
533		case IS_DOUBLE: {
534			smart_str_append_printf(str, "%.*G", (int) EG(precision), Z_DVAL_P(arg));
535			smart_str_appends(str, ", ");
536			break;
537		}
538		case IS_ARRAY:
539			smart_str_appends(str, "Array, ");
540			break;
541		case IS_OBJECT: {
542			zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg));
543			smart_str_appends(str, "Object(");
544			smart_str_appends(str, ZSTR_VAL(class_name));
545			smart_str_appends(str, "), ");
546			zend_string_release_ex(class_name, 0);
547			break;
548		}
549	}
550}
551/* }}} */
552
553static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /* {{{ */
554{
555	zval *file, *tmp;
556
557	smart_str_appendc(str, '#');
558	smart_str_append_long(str, num);
559	smart_str_appendc(str, ' ');
560
561	file = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_FILE), 1);
562	if (file) {
563		if (Z_TYPE_P(file) != IS_STRING) {
564			zend_error(E_WARNING, "Function name is no string");
565			smart_str_appends(str, "[unknown function]");
566		} else{
567			zend_long line;
568			tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_LINE), 1);
569			if (tmp) {
570				if (Z_TYPE_P(tmp) == IS_LONG) {
571					line = Z_LVAL_P(tmp);
572				} else {
573					zend_error(E_WARNING, "Line is no long");
574					line = 0;
575				}
576			} else {
577				line = 0;
578			}
579			smart_str_append(str, Z_STR_P(file));
580			smart_str_appendc(str, '(');
581			smart_str_append_long(str, line);
582			smart_str_appends(str, "): ");
583		}
584	} else {
585		smart_str_appends(str, "[internal function]: ");
586	}
587	TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_CLASS));
588	TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_TYPE));
589	TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_FUNCTION));
590	smart_str_appendc(str, '(');
591	tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_ARGS), 1);
592	if (tmp) {
593		if (Z_TYPE_P(tmp) == IS_ARRAY) {
594			size_t last_len = ZSTR_LEN(str->s);
595			zval *arg;
596
597			ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmp), arg) {
598				_build_trace_args(arg, str);
599			} ZEND_HASH_FOREACH_END();
600
601			if (last_len != ZSTR_LEN(str->s)) {
602				ZSTR_LEN(str->s) -= 2; /* remove last ', ' */
603			}
604		} else {
605			zend_error(E_WARNING, "args element is no array");
606		}
607	}
608	smart_str_appends(str, ")\n");
609}
610/* }}} */
611
612/* {{{ proto string Exception|Error::getTraceAsString()
613   Obtain the backtrace for the exception as a string (instead of an array) */
614ZEND_METHOD(exception, getTraceAsString)
615{
616	zval *trace, *frame, rv;
617	zend_ulong index;
618	zval *object;
619	zend_class_entry *base_ce;
620	smart_str str = {0};
621	uint32_t num = 0;
622
623	ZEND_PARSE_PARAMETERS_NONE();
624
625	object = ZEND_THIS;
626	base_ce = i_get_exception_base(object);
627
628	trace = zend_read_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
629	if (Z_TYPE_P(trace) != IS_ARRAY) {
630		zend_type_error("Trace is not an array");
631		return;
632	}
633	ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
634		if (Z_TYPE_P(frame) != IS_ARRAY) {
635			zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
636			continue;
637		}
638
639		_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
640	} ZEND_HASH_FOREACH_END();
641
642	smart_str_appendc(&str, '#');
643	smart_str_append_long(&str, num);
644	smart_str_appends(&str, " {main}");
645	smart_str_0(&str);
646
647	RETURN_NEW_STR(str.s);
648}
649/* }}} */
650
651/* {{{ proto Throwable Exception|Error::getPrevious()
652   Return previous Throwable or NULL. */
653ZEND_METHOD(exception, getPrevious)
654{
655	zval rv;
656
657	ZEND_PARSE_PARAMETERS_NONE();
658
659	ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_PREVIOUS));
660} /* }}} */
661
662/* {{{ proto string Exception|Error::__toString()
663   Obtain the string representation of the Exception object */
664ZEND_METHOD(exception, __toString)
665{
666	zval trace, *exception;
667	zend_class_entry *base_ce;
668	zend_string *str;
669	zend_fcall_info fci;
670	zval rv, tmp;
671	zend_string *fname;
672
673	ZEND_PARSE_PARAMETERS_NONE();
674
675	str = ZSTR_EMPTY_ALLOC();
676
677	exception = ZEND_THIS;
678	fname = zend_string_init("gettraceasstring", sizeof("gettraceasstring")-1, 0);
679
680	while (exception && Z_TYPE_P(exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(exception), zend_ce_throwable)) {
681		zend_string *prev_str = str;
682		zend_string *message = zval_get_string(GET_PROPERTY(exception, ZEND_STR_MESSAGE));
683		zend_string *file = zval_get_string(GET_PROPERTY(exception, ZEND_STR_FILE));
684		zend_long line = zval_get_long(GET_PROPERTY(exception, ZEND_STR_LINE));
685
686		fci.size = sizeof(fci);
687		ZVAL_STR(&fci.function_name, fname);
688		fci.object = Z_OBJ_P(exception);
689		fci.retval = &trace;
690		fci.param_count = 0;
691		fci.params = NULL;
692		fci.no_separation = 1;
693
694		zend_call_function(&fci, NULL);
695
696		if (Z_TYPE(trace) != IS_STRING) {
697			zval_ptr_dtor(&trace);
698			ZVAL_UNDEF(&trace);
699		}
700
701		if ((Z_OBJCE_P(exception) == zend_ce_type_error || Z_OBJCE_P(exception) == zend_ce_argument_count_error) && strstr(ZSTR_VAL(message), ", called in ")) {
702			zend_string *real_message = zend_strpprintf(0, "%s and defined", ZSTR_VAL(message));
703			zend_string_release_ex(message, 0);
704			message = real_message;
705		}
706
707		if (ZSTR_LEN(message) > 0) {
708			str = zend_strpprintf(0, "%s: %s in %s:" ZEND_LONG_FMT
709					"\nStack trace:\n%s%s%s",
710					ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(message), ZSTR_VAL(file), line,
711					(Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n",
712					ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str));
713		} else {
714			str = zend_strpprintf(0, "%s in %s:" ZEND_LONG_FMT
715					"\nStack trace:\n%s%s%s",
716					ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(file), line,
717					(Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n",
718					ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str));
719		}
720
721		zend_string_release_ex(prev_str, 0);
722		zend_string_release_ex(message, 0);
723		zend_string_release_ex(file, 0);
724		zval_ptr_dtor(&trace);
725
726		Z_PROTECT_RECURSION_P(exception);
727		exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
728		if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) {
729			break;
730		}
731	}
732	zend_string_release_ex(fname, 0);
733
734	exception = ZEND_THIS;
735	/* Reset apply counts */
736	while (exception && Z_TYPE_P(exception) == IS_OBJECT && (base_ce = i_get_exception_base(exception)) && instanceof_function(Z_OBJCE_P(exception), base_ce)) {
737		if (Z_IS_RECURSIVE_P(exception)) {
738			Z_UNPROTECT_RECURSION_P(exception);
739		} else {
740			break;
741		}
742		exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
743	}
744
745	exception = ZEND_THIS;
746	base_ce = i_get_exception_base(exception);
747
748	/* We store the result in the private property string so we can access
749	 * the result in uncaught exception handlers without memleaks. */
750	ZVAL_STR(&tmp, str);
751	zend_update_property_ex(base_ce, exception, ZSTR_KNOWN(ZEND_STR_STRING), &tmp);
752
753	RETURN_STR(str);
754}
755/* }}} */
756
757/** {{{ Throwable method definition */
758static const zend_function_entry zend_funcs_throwable[] = {
759	ZEND_ABSTRACT_ME(throwable, getMessage,       arginfo_class_Throwable_getMessage)
760	ZEND_ABSTRACT_ME(throwable, getCode,          arginfo_class_Throwable_getCode)
761	ZEND_ABSTRACT_ME(throwable, getFile,          arginfo_class_Throwable_getFile)
762	ZEND_ABSTRACT_ME(throwable, getLine,          arginfo_class_Throwable_getLine)
763	ZEND_ABSTRACT_ME(throwable, getTrace,         arginfo_class_Throwable_getTrace)
764	ZEND_ABSTRACT_ME(throwable, getPrevious,      arginfo_class_Throwable_getPrevious)
765	ZEND_ABSTRACT_ME(throwable, getTraceAsString, arginfo_class_Throwable_getTraceAsString)
766	ZEND_ABSTRACT_ME(throwable, __toString,       arginfo_class_Throwable___toString)
767	ZEND_FE_END
768};
769/* }}} */
770
771/* {{{ internal structs */
772/* All functions that may be used in uncaught exception handlers must be final
773 * and must not throw exceptions. Otherwise we would need a facility to handle
774 * such exceptions in that handler.
775 * Also all getXY() methods are final because thy serve as read only access to
776 * their corresponding properties, no more, no less. If after all you need to
777 * override something then it is method __toString().
778 * And never try to change the state of exceptions and never implement anything
779 * that gives the user anything to accomplish this.
780 */
781static const zend_function_entry default_exception_functions[] = {
782	ZEND_ME(exception, __clone, arginfo_class_Exception___clone, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
783	ZEND_ME(exception, __construct, arginfo_class_Exception___construct, ZEND_ACC_PUBLIC)
784	ZEND_ME(exception, __wakeup, arginfo_class_Exception___wakeup, ZEND_ACC_PUBLIC)
785	ZEND_ME(exception, getMessage, arginfo_class_Exception_getMessage, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
786	ZEND_ME(exception, getCode, arginfo_class_Exception_getCode, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
787	ZEND_ME(exception, getFile, arginfo_class_Exception_getFile, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
788	ZEND_ME(exception, getLine, arginfo_class_Exception_getLine, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
789	ZEND_ME(exception, getTrace, arginfo_class_Exception_getTrace, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
790	ZEND_ME(exception, getPrevious, arginfo_class_Exception_getPrevious, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
791	ZEND_ME(exception, getTraceAsString, arginfo_class_Exception_getTraceAsString, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
792	ZEND_ME(exception, __toString, arginfo_class_Exception___toString, 0)
793	ZEND_FE_END
794};
795
796static const zend_function_entry error_exception_functions[] = {
797	ZEND_ME(error_exception, __construct, arginfo_class_ErrorException___construct, ZEND_ACC_PUBLIC)
798	ZEND_ME(error_exception, getSeverity, arginfo_class_ErrorException_getSeverity, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
799	ZEND_FE_END
800};
801/* }}} */
802
803void zend_register_default_exception(void) /* {{{ */
804{
805	zend_class_entry ce;
806
807	REGISTER_MAGIC_INTERFACE(throwable, Throwable);
808
809	memcpy(&default_exception_handlers, &std_object_handlers, sizeof(zend_object_handlers));
810	default_exception_handlers.clone_obj = NULL;
811
812	INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions);
813	zend_ce_exception = zend_register_internal_class_ex(&ce, NULL);
814	zend_ce_exception->create_object = zend_default_exception_new;
815	zend_class_implements(zend_ce_exception, 1, zend_ce_throwable);
816
817	zend_declare_property_string(zend_ce_exception, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
818	zend_declare_property_string(zend_ce_exception, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
819	zend_declare_property_long(zend_ce_exception, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
820	zend_declare_property_null(zend_ce_exception, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
821	zend_declare_property_null(zend_ce_exception, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
822	zend_declare_property_null(zend_ce_exception, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE);
823	zend_declare_property_null(zend_ce_exception, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE);
824
825	INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
826	zend_ce_error_exception = zend_register_internal_class_ex(&ce, zend_ce_exception);
827	zend_ce_error_exception->create_object = zend_error_exception_new;
828	zend_declare_property_long(zend_ce_error_exception, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED);
829
830	INIT_CLASS_ENTRY(ce, "Error", default_exception_functions);
831	zend_ce_error = zend_register_internal_class_ex(&ce, NULL);
832	zend_ce_error->create_object = zend_default_exception_new;
833	zend_class_implements(zend_ce_error, 1, zend_ce_throwable);
834
835	zend_declare_property_string(zend_ce_error, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED);
836	zend_declare_property_string(zend_ce_error, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE);
837	zend_declare_property_long(zend_ce_error, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
838	zend_declare_property_null(zend_ce_error, "file", sizeof("file")-1, ZEND_ACC_PROTECTED);
839	zend_declare_property_null(zend_ce_error, "line", sizeof("line")-1, ZEND_ACC_PROTECTED);
840	zend_declare_property_null(zend_ce_error, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE);
841	zend_declare_property_null(zend_ce_error, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE);
842
843	INIT_CLASS_ENTRY(ce, "CompileError", NULL);
844	zend_ce_compile_error = zend_register_internal_class_ex(&ce, zend_ce_error);
845	zend_ce_compile_error->create_object = zend_default_exception_new;
846
847	INIT_CLASS_ENTRY(ce, "ParseError", NULL);
848	zend_ce_parse_error = zend_register_internal_class_ex(&ce, zend_ce_compile_error);
849	zend_ce_parse_error->create_object = zend_default_exception_new;
850
851	INIT_CLASS_ENTRY(ce, "TypeError", NULL);
852	zend_ce_type_error = zend_register_internal_class_ex(&ce, zend_ce_error);
853	zend_ce_type_error->create_object = zend_default_exception_new;
854
855	INIT_CLASS_ENTRY(ce, "ArgumentCountError", NULL);
856	zend_ce_argument_count_error = zend_register_internal_class_ex(&ce, zend_ce_type_error);
857	zend_ce_argument_count_error->create_object = zend_default_exception_new;
858
859	INIT_CLASS_ENTRY(ce, "ValueError", NULL);
860	zend_ce_value_error = zend_register_internal_class_ex(&ce, zend_ce_error);
861	zend_ce_value_error->create_object = zend_default_exception_new;
862
863	INIT_CLASS_ENTRY(ce, "ArithmeticError", NULL);
864	zend_ce_arithmetic_error = zend_register_internal_class_ex(&ce, zend_ce_error);
865	zend_ce_arithmetic_error->create_object = zend_default_exception_new;
866
867	INIT_CLASS_ENTRY(ce, "DivisionByZeroError", NULL);
868	zend_ce_division_by_zero_error = zend_register_internal_class_ex(&ce, zend_ce_arithmetic_error);
869	zend_ce_division_by_zero_error->create_object = zend_default_exception_new;
870}
871/* }}} */
872
873/* {{{ Deprecated - Use zend_ce_exception directly instead */
874ZEND_API zend_class_entry *zend_exception_get_default(void)
875{
876	return zend_ce_exception;
877}
878/* }}} */
879
880/* {{{ Deprecated - Use zend_ce_error_exception directly instead */
881ZEND_API zend_class_entry *zend_get_error_exception(void)
882{
883	return zend_ce_error_exception;
884}
885/* }}} */
886
887ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) /* {{{ */
888{
889	zval ex, tmp;
890
891	if (exception_ce) {
892		if (!instanceof_function(exception_ce, zend_ce_throwable)) {
893			zend_error(E_NOTICE, "Exceptions must implement Throwable");
894			exception_ce = zend_ce_exception;
895		}
896	} else {
897		exception_ce = zend_ce_exception;
898	}
899	object_init_ex(&ex, exception_ce);
900
901
902	if (message) {
903		ZVAL_STRING(&tmp, message);
904		zend_update_property_ex(exception_ce, &ex, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
905		zval_ptr_dtor(&tmp);
906	}
907	if (code) {
908		ZVAL_LONG(&tmp, code);
909		zend_update_property_ex(exception_ce, &ex, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
910	}
911
912	zend_throw_exception_internal(&ex);
913	return Z_OBJ(ex);
914}
915/* }}} */
916
917ZEND_API ZEND_COLD zend_object *zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format, ...) /* {{{ */
918{
919	va_list arg;
920	char *message;
921	zend_object *obj;
922
923	va_start(arg, format);
924	zend_vspprintf(&message, 0, format, arg);
925	va_end(arg);
926	obj = zend_throw_exception(exception_ce, message, code);
927	efree(message);
928	return obj;
929}
930/* }}} */
931
932ZEND_API ZEND_COLD zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, const char *message, zend_long code, int severity) /* {{{ */
933{
934	zval ex, tmp;
935	zend_object *obj = zend_throw_exception(exception_ce, message, code);
936	ZVAL_OBJ(&ex, obj);
937	ZVAL_LONG(&tmp, severity);
938	zend_update_property_ex(zend_ce_error_exception, &ex, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
939	return obj;
940}
941/* }}} */
942
943static void zend_error_va(int type, const char *file, uint32_t lineno, const char *format, ...) /* {{{ */
944{
945	va_list args;
946
947	va_start(args, format);
948	zend_error_cb(type, file, lineno, format, args);
949	va_end(args);
950}
951/* }}} */
952
953static void zend_error_helper(int type, const char *filename, const uint32_t lineno, const char *format, ...) /* {{{ */
954{
955	va_list va;
956
957	va_start(va, format);
958	zend_error_cb(type, filename, lineno, format, va);
959	va_end(va);
960}
961/* }}} */
962
963/* This function doesn't return if it uses E_ERROR */
964ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {{{ */
965{
966	zval exception, rv;
967	zend_class_entry *ce_exception;
968
969	ZVAL_OBJ(&exception, ex);
970	ce_exception = ex->ce;
971	EG(exception) = NULL;
972	if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) {
973		zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE));
974		zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
975		zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE));
976
977		zend_error_helper(
978			(ce_exception == zend_ce_parse_error ? E_PARSE : E_COMPILE_ERROR) | E_DONT_BAIL,
979			ZSTR_VAL(file), line, "%s", ZSTR_VAL(message));
980
981		zend_string_release_ex(file, 0);
982		zend_string_release_ex(message, 0);
983	} else if (instanceof_function(ce_exception, zend_ce_throwable)) {
984		zval tmp;
985		zend_string *str, *file = NULL;
986		zend_long line = 0;
987
988		zend_call_method_with_0_params(Z_OBJ(exception), ce_exception, &ex->ce->__tostring, "__tostring", &tmp);
989		if (!EG(exception)) {
990			if (Z_TYPE(tmp) != IS_STRING) {
991				zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name));
992			} else {
993				zend_update_property_ex(i_get_exception_base(&exception), &exception, ZSTR_KNOWN(ZEND_STR_STRING), &tmp);
994			}
995		}
996		zval_ptr_dtor(&tmp);
997
998		if (EG(exception)) {
999			zval zv;
1000
1001			ZVAL_OBJ(&zv, EG(exception));
1002			/* do the best we can to inform about the inner exception */
1003			if (instanceof_function(ce_exception, zend_ce_exception) || instanceof_function(ce_exception, zend_ce_error)) {
1004				file = zval_get_string(GET_PROPERTY_SILENT(&zv, ZEND_STR_FILE));
1005				line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE));
1006			}
1007
1008			zend_error_va(E_WARNING, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
1009				"Uncaught %s in exception handling during call to %s::__tostring()",
1010				ZSTR_VAL(Z_OBJCE(zv)->name), ZSTR_VAL(ce_exception->name));
1011
1012			if (file) {
1013				zend_string_release_ex(file, 0);
1014			}
1015		}
1016
1017		str = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_STRING));
1018		file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
1019		line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE));
1020
1021		zend_error_va(severity | E_DONT_BAIL,
1022			(file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
1023			"Uncaught %s\n  thrown", ZSTR_VAL(str));
1024
1025		zend_string_release_ex(str, 0);
1026		zend_string_release_ex(file, 0);
1027	} else {
1028		zend_error(severity, "Uncaught exception '%s'", ZSTR_VAL(ce_exception->name));
1029	}
1030
1031	OBJ_RELEASE(ex);
1032}
1033/* }}} */
1034
1035ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */
1036{
1037	zend_class_entry *exception_ce;
1038
1039	if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) {
1040		zend_error_noreturn(E_CORE_ERROR, "Need to supply an object when throwing an exception");
1041	}
1042
1043	exception_ce = Z_OBJCE_P(exception);
1044
1045	if (!exception_ce || !instanceof_function(exception_ce, zend_ce_throwable)) {
1046		zend_throw_error(NULL, "Cannot throw objects that do not implement Throwable");
1047		zval_ptr_dtor(exception);
1048		return;
1049	}
1050	zend_throw_exception_internal(exception);
1051}
1052/* }}} */
1053