Skip to content

Commit 4490fee

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 9659f3e commit 4490fee

33 files changed

+229
-95
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 || zend_is_pass_function(ce->constructor))
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 || zend_is_pass_function(ce->constructor))
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 || zend_is_pass_function(ce->constructor))
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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,13 @@ 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+
640+
static zend_always_inline bool zend_is_pass_function(const zend_function *fn) {
641+
return fn == (const zend_function*) &zend_pass_function;
642+
}
643+
637644
#define ZEND_CALL_HAS_THIS IS_OBJECT_EX
638645

639646
/* 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 && !zend_is_pass_function(ce->constructor)) {
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: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5969,7 +5969,15 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N
59695969
}
59705970

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

59835991
/* Perform a dummy function call */
59845992
call = zend_vm_stack_push_call_frame(
5985-
ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
5993+
ZEND_CALL_FUNCTION, constructor,
59865994
opline->extended_value, NULL);
59875995
} else {
59885996
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)