diff --git a/Zend/zend.c b/Zend/zend.c index af0013220f20..2a5988273bcd 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -833,8 +833,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ #endif executor_globals->flags = EG_FLAGS_INITIAL; executor_globals->record_errors = false; - executor_globals->num_errors = 0; - executor_globals->errors = NULL; + memset(&executor_globals->errors, 0, sizeof(executor_globals->errors)); executor_globals->filename_override = NULL; executor_globals->lineno_override = -1; #ifdef ZEND_CHECK_STACK_LIMIT @@ -1446,8 +1445,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_stack delayed_oplines_stack; int type = orig_type & E_ALL; bool orig_record_errors; - uint32_t orig_num_errors; - zend_error_info **orig_errors; + zend_err_buf orig_errors_buf; zend_result res; /* If we're executing a function during SCCP, count any warnings that may be emitted, @@ -1459,11 +1457,9 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( } /* Emit any delayed error before handling fatal error */ - if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(num_errors)) { - uint32_t num_errors = EG(num_errors); - zend_error_info **errors = EG(errors); - EG(num_errors) = 0; - EG(errors) = NULL; + if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(errors).size) { + zend_err_buf errors_buf = EG(errors); + EG(errors).size = 0; bool orig_record_errors = EG(record_errors); EG(record_errors) = false; @@ -1473,12 +1469,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting); EG(user_error_handler_error_reporting) = 0; - zend_emit_recorded_errors_ex(num_errors, errors); + zend_emit_recorded_errors_ex(errors_buf.size, errors_buf.errors); EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting; EG(record_errors) = orig_record_errors; - EG(num_errors) = num_errors; - EG(errors) = errors; + EG(errors) = errors_buf; } if (EG(record_errors)) { @@ -1487,12 +1482,13 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( info->lineno = error_lineno; info->filename = zend_string_copy(error_filename); info->message = zend_string_copy(message); - - /* This is very inefficient for a large number of errors. - * Use pow2 realloc if it becomes a problem. */ - EG(num_errors)++; - EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors)); - EG(errors)[EG(num_errors)-1] = info; + EG(errors).size++; + if (EG(errors).size > EG(errors).capacity) { + uint32_t capacity = EG(errors).capacity ? EG(errors).capacity + (EG(errors).capacity >> 1) : 2; + EG(errors).errors = erealloc(EG(errors).errors, sizeof(zend_error_info *) * capacity); + EG(errors).capacity = capacity; + } + EG(errors).errors[EG(errors).size - 1] = info; /* Do not process non-fatal recorded error */ if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) { @@ -1575,17 +1571,15 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( } orig_record_errors = EG(record_errors); - orig_num_errors = EG(num_errors); - orig_errors = EG(errors); EG(record_errors) = false; - EG(num_errors) = 0; - EG(errors) = NULL; + + orig_errors_buf = EG(errors); + memset(&EG(errors), 0, sizeof(EG(errors))); res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); EG(record_errors) = orig_record_errors; - EG(num_errors) = orig_num_errors; - EG(errors) = orig_errors; + EG(errors) = orig_errors_buf; if (res == SUCCESS) { if (Z_TYPE(retval) != IS_UNDEF) { @@ -1780,8 +1774,7 @@ ZEND_API void zend_begin_record_errors(void) { ZEND_ASSERT(!EG(record_errors) && "Error recording already enabled"); EG(record_errors) = true; - EG(num_errors) = 0; - EG(errors) = NULL; + EG(errors).size = 0; } ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors) @@ -1795,24 +1788,23 @@ ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info ZEND_API void zend_emit_recorded_errors(void) { EG(record_errors) = false; - zend_emit_recorded_errors_ex(EG(num_errors), EG(errors)); + zend_emit_recorded_errors_ex(EG(errors).size, EG(errors).errors); } ZEND_API void zend_free_recorded_errors(void) { - if (!EG(num_errors)) { + if (!EG(errors).size) { return; } - for (uint32_t i = 0; i < EG(num_errors); i++) { - zend_error_info *info = EG(errors)[i]; + for (uint32_t i = 0; i < EG(errors).size; i++) { + zend_error_info *info = EG(errors).errors[i]; zend_string_release(info->filename); zend_string_release(info->message); - efree(info); + efree_size(info, sizeof(zend_error_info)); } - efree(EG(errors)); - EG(errors) = NULL; - EG(num_errors) = 0; + efree(EG(errors).errors); + memset(&EG(errors), 0, sizeof(EG(errors))); } ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format, ...) /* {{{ */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index dbd2a9039cfc..30ed4f5914cf 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -192,8 +192,7 @@ void init_executor(void) /* {{{ */ EG(get_gc_buffer).start = EG(get_gc_buffer).end = EG(get_gc_buffer).cur = NULL; EG(record_errors) = false; - EG(num_errors) = 0; - EG(errors) = NULL; + memset(&EG(errors), 0, sizeof(EG(errors))); EG(filename_override) = NULL; EG(lineno_override) = -1; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 31f54cd8284b..db202dda66cb 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -74,6 +74,7 @@ typedef struct _zend_vm_stack *zend_vm_stack; typedef struct _zend_ini_entry zend_ini_entry; typedef struct _zend_fiber_context zend_fiber_context; typedef struct _zend_fiber zend_fiber; +typedef struct _zend_error_info zend_error_info; typedef enum { ZEND_MEMOIZE_NONE, @@ -81,6 +82,12 @@ typedef enum { ZEND_MEMOIZE_FETCH, } zend_memoize_mode; +typedef struct zend_err_buf { + uint32_t size; + uint32_t capacity; + zend_error_info **errors; +} zend_err_buf; + struct _zend_compiler_globals { zend_stack loop_var_stack; @@ -298,8 +305,7 @@ struct _zend_executor_globals { * and their processing is delayed until zend_emit_recorded_errors() * is called or a fatal diagnostic is emitted. */ bool record_errors; - uint32_t num_errors; - zend_error_info **errors; + zend_err_buf errors; /* Override filename or line number of thrown errors and exceptions */ zend_string *filename_override; diff --git a/build/gen_stub.php b/build/gen_stub.php index 9aac6b23e78b..7808929c86cc 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -54,6 +54,24 @@ function array_any(array $array, callable $callback): bool { } } +if (function_exists('clone')) { + // Replace (\clone)(...) with \clone(...) once the minimally + // supported PHP version is 8.5. + define('clone', \clone(...)); +} else { + define( + 'clone', + /** + * @template T + * @param T $o + * @return T + */ + function (object $o): object { + return clone $o; + } + ); +} + /** * @return FileInfo[] */ @@ -2184,9 +2202,7 @@ public function toArgInfoCode(?int $minPHPCompatibility): string { public function __clone() { - foreach ($this->args as $key => $argInfo) { - $this->args[$key] = clone $argInfo; - } + $this->args = array_map((\clone)(...), $this->args); $this->return = clone $this->return; } } @@ -4154,17 +4170,9 @@ private function createIncludeElement(DOMDocument $doc, string $query, int $inde public function __clone() { - foreach ($this->constInfos as $key => $constInfo) { - $this->constInfos[$key] = clone $constInfo; - } - - foreach ($this->propertyInfos as $key => $propertyInfo) { - $this->propertyInfos[$key] = clone $propertyInfo; - } - - foreach ($this->funcInfos as $key => $funcInfo) { - $this->funcInfos[$key] = clone $funcInfo; - } + $this->constInfos = array_map((\clone)(...), $this->constInfos); + $this->propertyInfos = array_map((\clone)(...), $this->propertyInfos); + $this->funcInfos = array_map((\clone)(...), $this->funcInfos); } /** @@ -4275,17 +4283,9 @@ public function getAllConstInfos(): array { public function __clone() { - foreach ($this->constInfos as $key => $constInfo) { - $this->constInfos[$key] = clone $constInfo; - } - - foreach ($this->funcInfos as $key => $funcInfo) { - $this->funcInfos[$key] = clone $funcInfo; - } - - foreach ($this->classInfos as $key => $classInfo) { - $this->classInfos[$key] = clone $classInfo; - } + $this->constInfos = array_map((\clone)(...), $this->constInfos); + $this->funcInfos = array_map((\clone)(...), $this->funcInfos); + $this->classInfos = array_map((\clone)(...), $this->classInfos); } private function getMinimumPhpVersionIdCompatibility(): ?int { diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 3d6923d12158..7aa0e67c24a5 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -1968,8 +1968,8 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int if (persistent_script) { if (ZCG(accel_directives).record_warnings) { - persistent_script->num_warnings = EG(num_errors); - persistent_script->warnings = EG(errors); + persistent_script->num_warnings = EG(errors).size; + persistent_script->warnings = EG(errors).errors; } from_memory = false; @@ -2193,8 +2193,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) from_shared_memory = false; if (persistent_script) { if (ZCG(accel_directives).record_warnings) { - persistent_script->num_warnings = EG(num_errors); - persistent_script->warnings = EG(errors); + persistent_script->num_warnings = EG(errors).size; + persistent_script->warnings = EG(errors).errors; } /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */ @@ -2421,7 +2421,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, } ZCG(current_persistent_script) = &dummy; zend_persist_class_entry_calc(ce); - zend_persist_warnings_calc(EG(num_errors), EG(errors)); + zend_persist_warnings_calc(EG(errors).size, EG(errors).errors); size = dummy.size; zend_shared_alloc_clear_xlat_table(); @@ -2491,8 +2491,8 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, JIT_G(on) = jit_on_old; #endif - entry->num_warnings = EG(num_errors); - entry->warnings = zend_persist_warnings(EG(num_errors), EG(errors)); + entry->num_warnings = EG(errors).size; + entry->warnings = zend_persist_warnings(EG(errors).size, EG(errors).errors); entry->next = proto->inheritance_cache; proto->inheritance_cache = entry; @@ -4123,9 +4123,9 @@ static void preload_link(void) /* Remember the last error. */ zend_error_cb = orig_error_cb; EG(record_errors) = false; - ZEND_ASSERT(EG(num_errors) > 0); - zend_hash_update_ptr(&errors, key, EG(errors)[EG(num_errors)-1]); - EG(num_errors)--; + ZEND_ASSERT(EG(errors).size > 0); + zend_hash_update_ptr(&errors, key, EG(errors).errors[EG(errors).size-1]); + EG(errors).size--; } zend_end_try(); CG(in_compilation) = false; CG(compiled_filename) = NULL;