Skip to content

Commit e89c0d3

Browse files
committed
core: set zend_pass_function as default constructor
This is to make the semantics of when a class cannot be instiantiated obvious rather than needed to remember to call the get_constructor() handler.
1 parent 49fdf49 commit e89c0d3

29 files changed

+200
-92
lines changed

Zend/Optimizer/escape_analysis.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
169169
&& !ce->create_object
170170
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor
171171
&& ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
172-
&& !ce->constructor
172+
&& (!ce->constructor || ce->constructor == (zend_function*) &zend_pass_function)
173173
&& !ce->destructor
174174
&& !ce->__get
175175
&& !ce->__set
@@ -238,7 +238,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
238238
&& !ce->create_object
239239
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor
240240
&& ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
241-
&& !ce->constructor
241+
&& (!ce->constructor || ce->constructor == (zend_function*) &zend_pass_function)
242242
&& !ce->destructor
243243
&& !ce->__get
244244
&& !ce->__set

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3395,7 +3395,7 @@ static zend_always_inline zend_result _zend_update_type_info(
33953395
}
33963396
/* New objects without constructors cannot escape. */
33973397
if (ce
3398-
&& !ce->constructor
3398+
&& (!ce->constructor || ce->constructor == (zend_function*) &zend_pass_function)
33993399
&& !ce->create_object
34003400
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor) {
34013401
tmp &= ~MAY_BE_RCN;

Zend/zend_builtin_functions.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ ZEND_MINIT_FUNCTION(core) { /* {{{ */
3838
zend_register_default_classes();
3939

4040
zend_standard_class_def = register_class_stdClass();
41+
/* Assign dummy constructor */
42+
zend_standard_class_def->constructor = (zend_function*) &zend_pass_function;
4143

4244
return SUCCESS;
4345
}

Zend/zend_compile.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9351,6 +9351,11 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
93519351
ce->ce_flags |= ZEND_ACC_TOP_LEVEL;
93529352
}
93539353

9354+
/* Add a default "pass" constructor if none are defined */
9355+
if (ce->constructor == NULL && (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == 0) {
9356+
ce->constructor = (zend_function *) &zend_pass_function;
9357+
}
9358+
93549359
if (ce->__serialize == NULL && zend_hash_exists(&ce->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP))) {
93559360
zend_error(E_DEPRECATED, "The __sleep() serialization magic method has been deprecated."
93569361
" Implement __serialize() instead (or in addition, if support for old PHP versions is necessary)");

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,9 @@ struct _zend_execute_data {
634634
zend_array *extra_named_params;
635635
};
636636

637+
/* export zend_pass_function to allow comparisons against it */
638+
extern ZEND_API const zend_internal_function zend_pass_function;
639+
637640
#define ZEND_CALL_HAS_THIS IS_OBJECT_EX
638641

639642
/* Top 16 bits of Z_TYPE_INFO(EX(This)) are used as call_info flags */

Zend/zend_execute.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ ZEND_END_ARG_INFO()
142142
ZEND_API const zend_internal_function zend_pass_function = {
143143
ZEND_INTERNAL_FUNCTION, /* type */
144144
{0, 0, 0}, /* arg_flags */
145-
0, /* fn_flags */
145+
ZEND_ACC_PUBLIC, /* fn_flags */
146146
NULL, /* name */
147147
NULL, /* scope */
148148
NULL, /* prototype */

Zend/zend_execute.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re
5858
ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, const char *string_name, bool handle_exceptions);
5959
ZEND_API zend_result zend_eval_stringl_ex(const char *str, size_t str_len, zval *retval_ptr, const char *string_name, bool handle_exceptions);
6060

61-
/* export zend_pass_function to allow comparisons against it */
62-
extern ZEND_API const zend_internal_function zend_pass_function;
63-
6461
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data);
6562
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc);
6663
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc);

Zend/zend_inheritance.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */
187187
ce->__debugInfo = parent->__debugInfo;
188188
}
189189

190-
if (ce->constructor) {
190+
if (ce->constructor && ce->constructor != (zend_function*) &zend_pass_function) {
191191
if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) {
192192
zend_error_noreturn(E_ERROR, "Cannot override final %s::%s() with %s::%s()",
193193
ZSTR_VAL(parent->name), ZSTR_VAL(parent->constructor->common.function_name),

Zend/zend_vm_def.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
* php zend_vm_gen.php
2424
*/
2525

26+
#include "zend.h"
27+
#include "zend_portability.h"
28+
2629
ZEND_VM_HELPER(zend_add_helper, ANY, ANY, zval *op_1, zval *op_2)
2730
{
2831
USE_OPLINE
@@ -5969,7 +5972,15 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N
59695972
}
59705973

59715974
constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
5972-
if (constructor == NULL) {
5975+
if (UNEXPECTED(constructor == NULL)) {
5976+
/* No constructor implies that an internal get_constructor was overwritten and threw an exception. */
5977+
if (UNEXPECTED(!EG(exception))) {
5978+
zend_throw_error(NULL, "No constructor for %s class", ZSTR_VAL(ce->name));
5979+
}
5980+
HANDLE_EXCEPTION();
5981+
}
5982+
/* Pass function is special */
5983+
else if (constructor == (zend_function*) &zend_pass_function) {
59735984
/* If there are no arguments, skip over the DO_FCALL opcode. We check if the next
59745985
* opcode is DO_FCALL in case EXT instructions are used. */
59755986
if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) {
@@ -5982,7 +5993,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N
59825993

59835994
/* Perform a dummy function call */
59845995
call = zend_vm_stack_push_call_frame(
5985-
ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
5996+
ZEND_CALL_FUNCTION, constructor,
59865997
opline->extended_value, NULL);
59875998
} else {
59885999
if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) {

Zend/zend_vm_execute.h

Lines changed: 60 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)