From 669c569fc81cd8cb1c1f78827b64b59aade40655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Brzuchalski?= Date: Tue, 7 Jul 2020 22:17:48 +0200 Subject: [PATCH 1/3] Added StackFrame class with tests --- Zend/tests/stack_frame_001.phpt | 38 +++++++ Zend/tests/stack_frame_002.phpt | 38 +++++++ Zend/tests/stack_frame_003.phpt | 38 +++++++ Zend/zend_builtin_functions.c | 93 ++++++++++++++--- Zend/zend_builtin_functions.h | 2 +- Zend/zend_exceptions.c | 177 +++++++++++++++++++++++++++++++- Zend/zend_exceptions.h | 3 + Zend/zend_exceptions.stub.php | 19 ++++ Zend/zend_exceptions_arginfo.h | 41 +++++++- ext/reflection/php_reflection.c | 2 +- sapi/phpdbg/phpdbg_frame.c | 2 +- 11 files changed, 431 insertions(+), 22 deletions(-) create mode 100644 Zend/tests/stack_frame_001.phpt create mode 100644 Zend/tests/stack_frame_002.phpt create mode 100644 Zend/tests/stack_frame_003.phpt diff --git a/Zend/tests/stack_frame_001.phpt b/Zend/tests/stack_frame_001.phpt new file mode 100644 index 0000000000000..13096b6833fa6 --- /dev/null +++ b/Zend/tests/stack_frame_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +StackFrame read properties +--FILE-- + $frame->file, + 'line' => $frame->line, + 'function' => $frame->function, + 'class' => $frame->class, + 'type' => $frame->type, + 'object' => $frame->object, + 'args' => $frame->args, +]); + +--EXPECTF-- +array(7) { + ["file"]=> + string(%d) "%s.php" + ["line"]=> + int(%d) + ["function"]=> + string(6) "frames" + ["class"]=> + NULL + ["type"]=> + NULL + ["object"]=> + NULL + ["args"]=> + array(0) { + } +} diff --git a/Zend/tests/stack_frame_002.phpt b/Zend/tests/stack_frame_002.phpt new file mode 100644 index 0000000000000..65fad695a0f87 --- /dev/null +++ b/Zend/tests/stack_frame_002.phpt @@ -0,0 +1,38 @@ +--TEST-- +StackFrame read dimensions +--FILE-- + $frame['file'], + 'line' => $frame['line'], + 'function' => $frame['function'], + 'class' => $frame['class'], + 'type' => $frame['type'], + 'object' => $frame['object'], + 'args' => $frame['args'], +]); + +--EXPECTF-- +array(7) { + ["file"]=> + string(%d) "%s.php" + ["line"]=> + int(%d) + ["function"]=> + string(6) "frames" + ["class"]=> + NULL + ["type"]=> + NULL + ["object"]=> + NULL + ["args"]=> + array(0) { + } +} diff --git a/Zend/tests/stack_frame_003.phpt b/Zend/tests/stack_frame_003.phpt new file mode 100644 index 0000000000000..622f3d9fdc201 --- /dev/null +++ b/Zend/tests/stack_frame_003.phpt @@ -0,0 +1,38 @@ +--TEST-- +StackFrame getters +--FILE-- + $frame->getFile(), + 'line' => $frame->getLine(), + 'function' => $frame->getFunction(), + 'class' => $frame->getClass(), + 'type' => $frame->getType(), + 'object' => $frame->getObject(), + 'args' => $frame->getArgs(), +]); + +--EXPECTF-- +array(7) { + ["file"]=> + string(%d) "%s.php" + ["line"]=> + int(%d) + ["function"]=> + string(6) "frames" + ["class"]=> + NULL + ["type"]=> + NULL + ["object"]=> + NULL + ["args"]=> + array(0) { + } +} diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 47c44c8fd6137..5936eafceea77 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1927,10 +1927,10 @@ ZEND_FUNCTION(debug_print_backtrace) /* }}} */ -ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit) /* {{{ */ +ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit, int as_objects) /* {{{ */ { zend_execute_data *ptr, *skip, *call = NULL; - zend_object *object; + zend_object *object, *frame_object; int lineno, frameno = 0; zend_function *func; zend_string *function_name; @@ -1969,7 +1969,14 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int while (ptr && (limit == 0 || frameno < limit)) { frameno++; - array_init(&stack_frame); + if (as_objects == 0) { + array_init(&stack_frame); + } else { + frame_object = zend_objects_new(zend_ce_stack_frame); + ZVAL_OBJ(&stack_frame, frame_object); + object_properties_init(frame_object, zend_ce_stack_frame); + frame_object->handlers = &zend_stack_frame_handlers; + } ptr = zend_generator_check_placeholder_frame(ptr); @@ -1991,9 +1998,17 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int lineno = skip->opline->lineno; } ZVAL_STR_COPY(&tmp, filename); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + } ZVAL_LONG(&tmp, lineno); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + } /* try to fetch args only if an FCALL was just made - elsewise we're in the middle of a function * and debug_baktrace() might have been called by the error_handler. in this case we don't @@ -2011,9 +2026,17 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int } if (prev->func && ZEND_USER_CODE(prev->func->common.type)) { ZVAL_STR_COPY(&tmp, prev->func->op_array.filename); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + } ZVAL_LONG(&tmp, prev->opline->lineno); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + } break; } prev_call = prev; @@ -2035,7 +2058,11 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int if (function_name) { ZVAL_STR_COPY(&tmp, function_name); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + } if (object) { if (func->common.scope) { @@ -2045,26 +2072,50 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int } else { ZVAL_STR(&tmp, object->handlers->get_class_name(object)); } - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); + } if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) { ZVAL_OBJ_COPY(&tmp, object); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp); + } } ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_OBJECT_OPERATOR)); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + } } else if (func->common.scope) { ZVAL_STR_COPY(&tmp, func->common.scope->name); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); + } ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM)); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + } } if ((options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0 && func->type != ZEND_EVAL_CODE) { debug_backtrace_get_args(call, &tmp); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_ARGS), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_ARGS), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_ARGS), &tmp); + } } } else { /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ @@ -2112,11 +2163,19 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int ZVAL_STR_COPY(&tmp, include_filename); zend_hash_next_index_insert_new(Z_ARRVAL(arg_array), &tmp); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_ARGS), &arg_array); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_ARGS), &arg_array); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_ARGS), &tmp); + } } ZVAL_INTERNED_STR(&tmp, pseudo_function_name); - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + if (as_objects == 0) { + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + } else { + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + } } zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &stack_frame); @@ -2140,7 +2199,7 @@ ZEND_FUNCTION(debug_backtrace) RETURN_THROWS(); } - zend_fetch_debug_backtrace(return_value, 1, options, limit); + zend_fetch_debug_backtrace(return_value, 1, options, limit, 0); } /* }}} */ diff --git a/Zend/zend_builtin_functions.h b/Zend/zend_builtin_functions.h index cfc347ed41ffc..c59f47cd94d42 100644 --- a/Zend/zend_builtin_functions.h +++ b/Zend/zend_builtin_functions.h @@ -23,7 +23,7 @@ int zend_startup_builtin_functions(void); BEGIN_EXTERN_C() -ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit); +ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit, int as_objects); END_EXTERN_C() #endif /* ZEND_BUILTIN_FUNCTIONS_H */ diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index c66a91899379e..68de62aaf35b9 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -40,6 +40,7 @@ ZEND_API zend_class_entry *zend_ce_argument_count_error; ZEND_API zend_class_entry *zend_ce_value_error; ZEND_API zend_class_entry *zend_ce_arithmetic_error; ZEND_API zend_class_entry *zend_ce_division_by_zero_error; +ZEND_API zend_class_entry *zend_ce_stack_frame; /* Internal pseudo-exception that is not exposed to userland. */ static zend_class_entry zend_ce_unwind_exit; @@ -47,6 +48,7 @@ static zend_class_entry zend_ce_unwind_exit; ZEND_API void (*zend_throw_exception_hook)(zval *ex); static zend_object_handlers default_exception_handlers; +zend_object_handlers zend_stack_frame_handlers; /* {{{ zend_implement_throwable */ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type) @@ -228,7 +230,7 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, if (EG(current_execute_data)) { zend_fetch_debug_backtrace(&trace, skip_top_traces, - EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0); + EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0, 0); } else { array_init(&trace); } @@ -758,6 +760,164 @@ static void declare_exception_properties(zend_class_entry *ce) (zend_type) ZEND_TYPE_INIT_CE(zend_ce_throwable, /* allow_null */ 1, 0)); } +static void declare_stack_frame_properties(zend_class_entry *ce) +{ + zval val; + + ZVAL_NULL(&val); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_FILE), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_LINE), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_FUNCTION), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_CLASS), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_TYPE), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_OBJECT), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_OBJECT)); + + ZVAL_EMPTY_ARRAY(&val); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_ARGS), &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); +} + +static ZEND_COLD zend_function *zend_stack_frame_get_constructor(zend_object *object) +{ + zend_throw_error(NULL, "Instantiation of '%s' is not allowed", ZSTR_VAL(object->ce->name)); + return NULL; +} + +static ZEND_COLD zval *zend_stack_frame_object_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) +{ + zend_throw_error(NULL, "'%s' objects are immutable and do not support writing properties", ZSTR_VAL(object->ce->name)); + + return &EG(uninitialized_zval); +} + +static ZEND_COLD zval *zend_stack_frame_read_dimension(zend_object *object, zval *offset, int type, zval *rv) +{ + zval *retval = rv; + if (offset == NULL) { + zend_throw_error(NULL, "Cannot append to '%s'", ZSTR_VAL(object->ce->name)); + return NULL; + } + + if (Z_TYPE_P(offset) != IS_STRING) { + zend_type_error("'%s' key must be a string", ZSTR_VAL(object->ce->name)); + return NULL; + } + + retval = zend_std_read_property(object, Z_STR_P(offset), type, NULL, rv); + + return retval; +} + +static ZEND_COLD void zend_stack_frame_write_dimension(zend_object *object, zval *offset, zval *value) +{ + zend_throw_error(NULL, "Cannot modify a '%s'", ZSTR_VAL(object->ce->name)); +} + +static ZEND_COLD int zend_stack_frame_has_dimension(zend_object *object, zval *offset, int check_empty) +{ + if (Z_TYPE_P(offset) != IS_STRING) { + zend_type_error("'%s' key must be a string", ZSTR_VAL(object->ce->name)); + return 0; + } + + return zend_std_has_property(object, Z_STR_P(offset), 0, NULL); +} + +static ZEND_COLD void zend_stack_frame_unset_dimension(zend_object *object, zval *offset) +{ + zend_throw_error(NULL, "Cannot modify a '%s'", ZSTR_VAL(object->ce->name)); +} + +ZEND_METHOD(StackFrame, getTrace) +{ + zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; + zend_long limit = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &options, &limit) == FAILURE) { + RETURN_THROWS(); + } + + zend_fetch_debug_backtrace(return_value, 1, options, limit, 1); +} + +ZEND_METHOD(StackFrame, getFile) +{ + zval *prop, rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_FILE); + RETURN_STR(zval_get_string(prop)); +} + +ZEND_METHOD(StackFrame, getLine) +{ + zval *prop, rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_LINE); + RETURN_LONG(zval_get_long(prop)); +} + +ZEND_METHOD(StackFrame, getFunction) +{ + zval rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_FUNCTION)); +} + +ZEND_METHOD(StackFrame, getClass) +{ + zval rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_CLASS)); +} + +ZEND_METHOD(StackFrame, getObject) +{ + zval rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_OBJECT)); +} + +ZEND_METHOD(StackFrame, getType) +{ + zval rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_TYPE)); +} + +ZEND_METHOD(StackFrame, getArgs) +{ + zval rv; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_ARGS)); +} + void zend_register_default_exception(void) /* {{{ */ { zend_class_entry ce; @@ -814,6 +974,21 @@ void zend_register_default_exception(void) /* {{{ */ zend_ce_division_by_zero_error->create_object = zend_default_exception_new; INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); + + INIT_CLASS_ENTRY(ce, "StackFrame", class_StackFrame_methods); + zend_ce_stack_frame = zend_register_internal_class_ex(&ce, NULL); + zend_ce_stack_frame->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_class_implements(zend_ce_stack_frame, 1, zend_ce_arrayaccess); + declare_stack_frame_properties(zend_ce_stack_frame); + + memcpy(&zend_stack_frame_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + // this needs different approach as it blocks property updates + // zend_stack_frame_handlers.write_property = zend_stack_frame_object_write_property; + zend_stack_frame_handlers.read_dimension = zend_stack_frame_read_dimension; + zend_stack_frame_handlers.write_dimension = zend_stack_frame_write_dimension; + zend_stack_frame_handlers.has_dimension = zend_stack_frame_has_dimension; + zend_stack_frame_handlers.unset_dimension = zend_stack_frame_unset_dimension; + zend_stack_frame_handlers.get_constructor = zend_stack_frame_get_constructor; } /* }}} */ diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index fdae31a0137c9..ed669eaedd092 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -35,6 +35,9 @@ extern ZEND_API zend_class_entry *zend_ce_argument_count_error; extern ZEND_API zend_class_entry *zend_ce_value_error; extern ZEND_API zend_class_entry *zend_ce_arithmetic_error; extern ZEND_API zend_class_entry *zend_ce_division_by_zero_error; +extern ZEND_API zend_class_entry *zend_ce_stack_frame; + +extern zend_object_handlers zend_stack_frame_handlers; ZEND_API void zend_exception_set_previous(zend_object *exception, zend_object *add_previous); ZEND_API void zend_exception_save(void); diff --git a/Zend/zend_exceptions.stub.php b/Zend/zend_exceptions.stub.php index 9cf55fd38404e..376a69c5207cd 100644 --- a/Zend/zend_exceptions.stub.php +++ b/Zend/zend_exceptions.stub.php @@ -119,3 +119,22 @@ class ArithmeticError extends Error class DivisionByZeroError extends ArithmeticError { } + +final class StackFrame +{ + public static function getTrace(): array {} + + public function getFile(): string {} + + public function getLine(): int {} + + public function getFunction(): ?string {} + + public function getClass(): ?string {} + + public function getObject(): ?object {} + + public function getType(): ?string {} + + public function getArgs(): array {} +} diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h index 6a226954a0c8c..ff6c5698ab77b 100644 --- a/Zend/zend_exceptions_arginfo.h +++ b/Zend/zend_exceptions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7eb20393f4ca314324d9813983124f724189ce8a */ + * Stub hash: 686db33b530c54804faf1fe5d4f8b1d6a6b06d6e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Throwable_getMessage, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -79,6 +79,24 @@ ZEND_END_ARG_INFO() #define arginfo_class_Error___toString arginfo_class_Throwable_getMessage +#define arginfo_class_StackFrame_getTrace arginfo_class_Throwable_getTrace + +#define arginfo_class_StackFrame_getFile arginfo_class_Throwable_getMessage + +#define arginfo_class_StackFrame_getLine arginfo_class_Throwable_getLine + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StackFrame_getFunction, 0, 0, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_StackFrame_getClass arginfo_class_StackFrame_getFunction + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StackFrame_getObject, 0, 0, IS_OBJECT, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_StackFrame_getType arginfo_class_StackFrame_getFunction + +#define arginfo_class_StackFrame_getArgs arginfo_class_Throwable_getTrace + ZEND_METHOD(Exception, __clone); ZEND_METHOD(Exception, __construct); @@ -93,6 +111,14 @@ ZEND_METHOD(Exception, getTraceAsString); ZEND_METHOD(Exception, __toString); ZEND_METHOD(ErrorException, __construct); ZEND_METHOD(ErrorException, getSeverity); +ZEND_METHOD(StackFrame, getTrace); +ZEND_METHOD(StackFrame, getFile); +ZEND_METHOD(StackFrame, getLine); +ZEND_METHOD(StackFrame, getFunction); +ZEND_METHOD(StackFrame, getClass); +ZEND_METHOD(StackFrame, getObject); +ZEND_METHOD(StackFrame, getType); +ZEND_METHOD(StackFrame, getArgs); static const zend_function_entry class_Throwable_methods[] = { @@ -179,3 +205,16 @@ static const zend_function_entry class_ArithmeticError_methods[] = { static const zend_function_entry class_DivisionByZeroError_methods[] = { ZEND_FE_END }; + + +static const zend_function_entry class_StackFrame_methods[] = { + ZEND_ME(StackFrame, getTrace, arginfo_class_StackFrame_getTrace, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(StackFrame, getFile, arginfo_class_StackFrame_getFile, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getLine, arginfo_class_StackFrame_getLine, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getFunction, arginfo_class_StackFrame_getFunction, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getClass, arginfo_class_StackFrame_getClass, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getObject, arginfo_class_StackFrame_getObject, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getType, arginfo_class_StackFrame_getType, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getArgs, arginfo_class_StackFrame_getArgs, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e5ce3cf88d00e..abff7c60b9da2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2145,7 +2145,7 @@ ZEND_METHOD(ReflectionGenerator, getTrace) } EG(current_execute_data) = root_generator->execute_data; - zend_fetch_debug_backtrace(return_value, 0, options, 0); + zend_fetch_debug_backtrace(return_value, 0, options, 0, 0); EG(current_execute_data) = ex_backup; root_generator->execute_data->prev_execute_data = root_prev; diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c index 453a0d74ba374..25b8847cc98fe 100644 --- a/sapi/phpdbg/phpdbg_frame.c +++ b/sapi/phpdbg/phpdbg_frame.c @@ -277,7 +277,7 @@ void phpdbg_dump_backtrace(size_t num) /* {{{ */ } phpdbg_try_access { - zend_fetch_debug_backtrace(&zbacktrace, 0, 0, limit); + zend_fetch_debug_backtrace(&zbacktrace, 0, 0, limit, 0); } phpdbg_catch_access { phpdbg_error("signalsegv", "", "Couldn't fetch backtrace, invalid data source"); return; From 771702f2b8efb7be679909c380b502e07d0de650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Brzuchalski?= Date: Tue, 7 Jul 2020 22:59:32 +0200 Subject: [PATCH 2/3] Removed unused function for write_property --- Zend/zend_exceptions.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 68de62aaf35b9..355e8252e12a8 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -796,13 +796,6 @@ static ZEND_COLD zend_function *zend_stack_frame_get_constructor(zend_object *ob return NULL; } -static ZEND_COLD zval *zend_stack_frame_object_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) -{ - zend_throw_error(NULL, "'%s' objects are immutable and do not support writing properties", ZSTR_VAL(object->ce->name)); - - return &EG(uninitialized_zval); -} - static ZEND_COLD zval *zend_stack_frame_read_dimension(zend_object *object, zval *offset, int type, zval *rv) { zval *retval = rv; @@ -982,8 +975,7 @@ void zend_register_default_exception(void) /* {{{ */ declare_stack_frame_properties(zend_ce_stack_frame); memcpy(&zend_stack_frame_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - // this needs different approach as it blocks property updates - // zend_stack_frame_handlers.write_property = zend_stack_frame_object_write_property; + // this needs different approach than adding fake write_property as it blocks property updates zend_stack_frame_handlers.read_dimension = zend_stack_frame_read_dimension; zend_stack_frame_handlers.write_dimension = zend_stack_frame_write_dimension; zend_stack_frame_handlers.has_dimension = zend_stack_frame_has_dimension; From 1a733c51e61c337716e77cca7da944a35ea1c0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Brzuchalski?= Date: Thu, 9 Jul 2020 08:39:16 +0200 Subject: [PATCH 3/3] StackFrame prop write&read optimisation, added closure and object_class --- Zend/tests/stack_frame_001.phpt | 8 ++- Zend/tests/stack_frame_002.phpt | 8 ++- Zend/tests/stack_frame_003.phpt | 8 ++- Zend/zend_builtin_functions.c | 120 +++++++++++++++++--------------- Zend/zend_exceptions.c | 39 ++++++++--- Zend/zend_exceptions.stub.php | 6 +- Zend/zend_exceptions_arginfo.h | 16 ++++- 7 files changed, 135 insertions(+), 70 deletions(-) diff --git a/Zend/tests/stack_frame_001.phpt b/Zend/tests/stack_frame_001.phpt index 13096b6833fa6..fe3d2edc70421 100644 --- a/Zend/tests/stack_frame_001.phpt +++ b/Zend/tests/stack_frame_001.phpt @@ -16,10 +16,12 @@ var_dump([ 'type' => $frame->type, 'object' => $frame->object, 'args' => $frame->args, + 'object_class' => $frame->object_class, + 'closure' => $frame->closure, ]); --EXPECTF-- -array(7) { +array(%d) { ["file"]=> string(%d) "%s.php" ["line"]=> @@ -35,4 +37,8 @@ array(7) { ["args"]=> array(0) { } + ["object_class"]=> + NULL + ["closure"]=> + NULL } diff --git a/Zend/tests/stack_frame_002.phpt b/Zend/tests/stack_frame_002.phpt index 65fad695a0f87..731e4cb0b42f1 100644 --- a/Zend/tests/stack_frame_002.phpt +++ b/Zend/tests/stack_frame_002.phpt @@ -16,10 +16,12 @@ var_dump([ 'type' => $frame['type'], 'object' => $frame['object'], 'args' => $frame['args'], + 'object_class' => $frame['object_class'], + 'closure' => $frame['closure'], ]); --EXPECTF-- -array(7) { +array(%d) { ["file"]=> string(%d) "%s.php" ["line"]=> @@ -35,4 +37,8 @@ array(7) { ["args"]=> array(0) { } + ["object_class"]=> + NULL + ["closure"]=> + NULL } diff --git a/Zend/tests/stack_frame_003.phpt b/Zend/tests/stack_frame_003.phpt index 622f3d9fdc201..02be80fabcdf3 100644 --- a/Zend/tests/stack_frame_003.phpt +++ b/Zend/tests/stack_frame_003.phpt @@ -16,10 +16,12 @@ var_dump([ 'type' => $frame->getType(), 'object' => $frame->getObject(), 'args' => $frame->getArgs(), + 'object_class' => $frame->getObjectClass(), + 'closure' => $frame->getClosure(), ]); --EXPECTF-- -array(7) { +array(%d) { ["file"]=> string(%d) "%s.php" ["line"]=> @@ -35,4 +37,8 @@ array(7) { ["args"]=> array(0) { } + ["object_class"]=> + NULL + ["closure"]=> + NULL } diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 5936eafceea77..8d0468410628b 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1930,13 +1930,13 @@ ZEND_FUNCTION(debug_print_backtrace) ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit, int as_objects) /* {{{ */ { zend_execute_data *ptr, *skip, *call = NULL; - zend_object *object, *frame_object; + zend_object *object, *frame_object = NULL; int lineno, frameno = 0; zend_function *func; zend_string *function_name; zend_string *filename; zend_string *include_filename = NULL; - zval stack_frame, tmp; + zval stack_frame, tmp, tmp2; array_init(return_value); @@ -1969,13 +1969,13 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int while (ptr && (limit == 0 || frameno < limit)) { frameno++; - if (as_objects == 0) { - array_init(&stack_frame); - } else { + if (as_objects == 1) { frame_object = zend_objects_new(zend_ce_stack_frame); ZVAL_OBJ(&stack_frame, frame_object); object_properties_init(frame_object, zend_ce_stack_frame); frame_object->handlers = &zend_stack_frame_handlers; + } else { + array_init(&stack_frame); } ptr = zend_generator_check_placeholder_frame(ptr); @@ -1997,17 +1997,14 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int } else { lineno = skip->opline->lineno; } - ZVAL_STR_COPY(&tmp, filename); - if (as_objects == 0) { - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + if (as_objects == 1) { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 0), filename); + ZVAL_LONG(OBJ_PROP_NUM(frame_object, 1), lineno); } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); - } - ZVAL_LONG(&tmp, lineno); - if (as_objects == 0) { + ZVAL_STR_COPY(&tmp, filename); + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + ZVAL_LONG(&tmp, lineno); zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); - } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); } /* try to fetch args only if an FCALL was just made - elsewise we're in the middle of a function @@ -2025,17 +2022,14 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int break; } if (prev->func && ZEND_USER_CODE(prev->func->common.type)) { - ZVAL_STR_COPY(&tmp, prev->func->op_array.filename); - if (as_objects == 0) { - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + if (as_objects == 1) { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 0), prev->func->op_array.filename); + ZVAL_LONG(OBJ_PROP_NUM(frame_object, 1), prev->opline->lineno); } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); - } - ZVAL_LONG(&tmp, prev->opline->lineno); - if (as_objects == 0) { + ZVAL_STR_COPY(&tmp, prev->func->op_array.filename); + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); + ZVAL_LONG(&tmp, prev->opline->lineno); zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); - } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); } break; } @@ -2057,53 +2051,69 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int } if (function_name) { - ZVAL_STR_COPY(&tmp, function_name); - if (as_objects == 0) { - zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + if (as_objects == 1) { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 2), function_name); } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); + ZVAL_STR_COPY(&tmp, function_name); + zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp); } if (object) { - if (func->common.scope) { - ZVAL_STR_COPY(&tmp, func->common.scope->name); - } else if (object->handlers->get_class_name == zend_std_get_class_name) { - ZVAL_STR_COPY(&tmp, object->ce->name); + if (as_objects == 1) { + if (func->common.scope) { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 3), func->common.scope->name); + } else if (object->handlers->get_class_name == zend_std_get_class_name) { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 3), object->ce->name); + } else { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 6), object->handlers->get_class_name(object)); + } + if (object->handlers->get_class_name == zend_std_get_class_name) { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 6), object->ce->name); + } else { + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 6), object->handlers->get_class_name(object)); + } + if (func->common.fn_flags & ZEND_ACC_CLOSURE) { + ZVAL_OBJ_COPY(&tmp2, object); + zend_create_closure(&tmp, func, func->common.scope, object->ce, &tmp2); + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_CALLABLE), &tmp); + } + if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) { + ZVAL_OBJ_COPY(&tmp, object); + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp); + } + zval_ptr_dtor(OBJ_PROP_NUM(frame_object, 4)); + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 4), ZSTR_KNOWN(ZEND_STR_OBJECT_OPERATOR)); } else { - ZVAL_STR(&tmp, object->handlers->get_class_name(object)); - } - if (as_objects == 0) { + if (func->common.scope) { + ZVAL_STR_COPY(&tmp, func->common.scope->name); + } else if (object->handlers->get_class_name == zend_std_get_class_name) { + ZVAL_STR_COPY(&tmp, object->ce->name); + } else { + ZVAL_STR(&tmp, object->handlers->get_class_name(object)); + } zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); - } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); - } - if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) { - ZVAL_OBJ_COPY(&tmp, object); - if (as_objects == 0) { + if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) { + ZVAL_OBJ_COPY(&tmp, object); zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp); - } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp); } - } - - ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_OBJECT_OPERATOR)); - if (as_objects == 0) { + ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_OBJECT_OPERATOR)); zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); - } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); } } else if (func->common.scope) { - ZVAL_STR_COPY(&tmp, func->common.scope->name); if (as_objects == 0) { + ZVAL_STR_COPY(&tmp, func->common.scope->name); zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); - } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_CLASS), &tmp); - } - ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM)); - if (as_objects == 0) { + ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM)); zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); } else { - zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_TYPE), &tmp); + zval_ptr_dtor(OBJ_PROP_NUM(frame_object, 3)); + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 3), func->common.scope->name); + zval_ptr_dtor(OBJ_PROP_NUM(frame_object, 4)); + ZVAL_STR_COPY(OBJ_PROP_NUM(frame_object, 4), ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM)); + if (func->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_create_closure(&tmp, func, func->common.scope, NULL, NULL); + zend_update_property_ex(zend_ce_stack_frame, &stack_frame, ZSTR_KNOWN(ZEND_STR_CALLABLE), &tmp); + } } } diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 355e8252e12a8..3c780d1fbe6c0 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -763,29 +763,37 @@ static void declare_exception_properties(zend_class_entry *ce) static void declare_stack_frame_properties(zend_class_entry *ce) { zval val; + zend_string *prop; ZVAL_NULL(&val); - zend_declare_typed_property( + zend_declare_typed_property( // 0 ce, ZSTR_KNOWN(ZEND_STR_FILE), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_declare_typed_property( + zend_declare_typed_property( // 1 ce, ZSTR_KNOWN(ZEND_STR_LINE), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_declare_typed_property( + zend_declare_typed_property( // 2 ce, ZSTR_KNOWN(ZEND_STR_FUNCTION), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_declare_typed_property( + zend_declare_typed_property( // 3 ce, ZSTR_KNOWN(ZEND_STR_CLASS), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_declare_typed_property( + zend_declare_typed_property( // 4 ce, ZSTR_KNOWN(ZEND_STR_TYPE), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_declare_typed_property( + zend_declare_typed_property( // 5 ce, ZSTR_KNOWN(ZEND_STR_OBJECT), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_OBJECT)); + prop = zend_string_init("object_class", sizeof("object_class")-1, 1); + zend_declare_typed_property( // 6 + ce, prop, &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(prop); // 7 + zend_declare_property_null(ce, "closure", sizeof("closure")-1, ZEND_ACC_PUBLIC); + ZVAL_EMPTY_ARRAY(&val); - zend_declare_typed_property( + zend_declare_typed_property( // 8 ce, ZSTR_KNOWN(ZEND_STR_ARGS), &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); } @@ -893,6 +901,20 @@ ZEND_METHOD(StackFrame, getObject) ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_OBJECT)); } +ZEND_METHOD(StackFrame, getObjectClass) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 6)); +} + +ZEND_METHOD(StackFrame, getClosure) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + ZVAL_COPY(return_value, OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 7)); +} + ZEND_METHOD(StackFrame, getType) { zval rv; @@ -970,12 +992,11 @@ void zend_register_default_exception(void) /* {{{ */ INIT_CLASS_ENTRY(ce, "StackFrame", class_StackFrame_methods); zend_ce_stack_frame = zend_register_internal_class_ex(&ce, NULL); - zend_ce_stack_frame->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_ce_stack_frame->ce_flags |= ZEND_ACC_FINAL; zend_class_implements(zend_ce_stack_frame, 1, zend_ce_arrayaccess); declare_stack_frame_properties(zend_ce_stack_frame); memcpy(&zend_stack_frame_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - // this needs different approach than adding fake write_property as it blocks property updates zend_stack_frame_handlers.read_dimension = zend_stack_frame_read_dimension; zend_stack_frame_handlers.write_dimension = zend_stack_frame_write_dimension; zend_stack_frame_handlers.has_dimension = zend_stack_frame_has_dimension; diff --git a/Zend/zend_exceptions.stub.php b/Zend/zend_exceptions.stub.php index 376a69c5207cd..417385c4ea1f1 100644 --- a/Zend/zend_exceptions.stub.php +++ b/Zend/zend_exceptions.stub.php @@ -122,7 +122,7 @@ class DivisionByZeroError extends ArithmeticError final class StackFrame { - public static function getTrace(): array {} + public static function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array {} public function getFile(): string {} @@ -134,6 +134,10 @@ public function getClass(): ?string {} public function getObject(): ?object {} + public function getObjectClass(): ?string {} + + public function getClosure(): ?\Closure {} + public function getType(): ?string {} public function getArgs(): array {} diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h index ff6c5698ab77b..44de7fd0131f9 100644 --- a/Zend/zend_exceptions_arginfo.h +++ b/Zend/zend_exceptions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 686db33b530c54804faf1fe5d4f8b1d6a6b06d6e */ + * Stub hash: ef999915ca870d847b00554a51ae420e44a8602c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Throwable_getMessage, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -79,7 +79,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_Error___toString arginfo_class_Throwable_getMessage -#define arginfo_class_StackFrame_getTrace arginfo_class_Throwable_getTrace +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StackFrame_getTrace, 0, 0, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "DEBUG_BACKTRACE_PROVIDE_OBJECT") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() #define arginfo_class_StackFrame_getFile arginfo_class_Throwable_getMessage @@ -93,6 +96,11 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StackFrame_getObject, 0, 0, IS_OBJECT, 1) ZEND_END_ARG_INFO() +#define arginfo_class_StackFrame_getObjectClass arginfo_class_StackFrame_getFunction + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_StackFrame_getClosure, 0, 0, Closure, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_StackFrame_getType arginfo_class_StackFrame_getFunction #define arginfo_class_StackFrame_getArgs arginfo_class_Throwable_getTrace @@ -117,6 +125,8 @@ ZEND_METHOD(StackFrame, getLine); ZEND_METHOD(StackFrame, getFunction); ZEND_METHOD(StackFrame, getClass); ZEND_METHOD(StackFrame, getObject); +ZEND_METHOD(StackFrame, getObjectClass); +ZEND_METHOD(StackFrame, getClosure); ZEND_METHOD(StackFrame, getType); ZEND_METHOD(StackFrame, getArgs); @@ -214,6 +224,8 @@ static const zend_function_entry class_StackFrame_methods[] = { ZEND_ME(StackFrame, getFunction, arginfo_class_StackFrame_getFunction, ZEND_ACC_PUBLIC) ZEND_ME(StackFrame, getClass, arginfo_class_StackFrame_getClass, ZEND_ACC_PUBLIC) ZEND_ME(StackFrame, getObject, arginfo_class_StackFrame_getObject, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getObjectClass, arginfo_class_StackFrame_getObjectClass, ZEND_ACC_PUBLIC) + ZEND_ME(StackFrame, getClosure, arginfo_class_StackFrame_getClosure, ZEND_ACC_PUBLIC) ZEND_ME(StackFrame, getType, arginfo_class_StackFrame_getType, ZEND_ACC_PUBLIC) ZEND_ME(StackFrame, getArgs, arginfo_class_StackFrame_getArgs, ZEND_ACC_PUBLIC) ZEND_FE_END