From 1b326db8169bcbf881906bf56df20916d6b74d19 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 22 Jan 2024 17:15:26 +0100 Subject: [PATCH 01/10] Generics experimentation (squashed) Co-authored-by: Arnaud Le Blanc --- Zend/Optimizer/dfa_pass.c | 4 +- Zend/Optimizer/escape_analysis.c | 4 +- Zend/Optimizer/zend_inference.c | 47 +- Zend/tests/bug77530.phpt | 2 +- Zend/tests/generics/basic.phpt | 20 + .../generics/duplicate_generic_param.phpt | 11 + .../generic_param_with_over_types.phpt | 73 +++ .../inheritance_bind_parent_param.phpt | 66 +++ .../inheritance_bind_parent_param_2.phpt | 87 ++++ ...nheritance_bind_passthru_parent_param.phpt | 30 ++ .../generics/inheritance_default_param.phpt | 49 ++ .../generics/inheritance_signature_check.phpt | 16 + .../inheritance_signature_check_2.phpt | 25 + .../inheritance_signature_check_3.phpt | 16 + .../generics/inheritance_too_few_args.phpt | 14 + .../generics/inheritance_too_few_args_2.phpt | 14 + .../generics/inheritance_too_few_args_3.phpt | 14 + .../generics/inheritance_too_many_args.phpt | 14 + .../generics/inheritance_too_many_args_2.phpt | 14 + .../generics/inheritance_too_many_args_3.phpt | 14 + Zend/tests/generics/param_same_as_class.phpt | 11 + .../generics/required_after_optional.phpt | 10 + Zend/tests/generics/syntax.phpt | 16 + Zend/tests/generics/type_params_in_types.phpt | 75 +++ .../type_with_incorrect_number_of_args.phpt | 48 ++ Zend/tests/oss_fuzz_59764.phpt | 2 +- Zend/zend.c | 1 - Zend/zend.h | 30 +- Zend/zend_API.c | 46 +- Zend/zend_API.h | 7 +- Zend/zend_ast.c | 56 ++- Zend/zend_ast.h | 4 + Zend/zend_builtin_functions.c | 4 +- Zend/zend_compile.c | 463 +++++++++++++----- Zend/zend_compile.h | 6 +- Zend/zend_constants.c | 8 +- Zend/zend_exceptions.c | 4 +- Zend/zend_exceptions_arginfo.h | 4 +- Zend/zend_execute.c | 347 ++++++++++--- Zend/zend_execute.h | 11 +- Zend/zend_execute_API.c | 10 +- Zend/zend_inheritance.c | 276 +++++++++-- Zend/zend_interfaces.c | 10 +- Zend/zend_language_parser.y | 76 ++- Zend/zend_object_handlers.c | 38 +- Zend/zend_opcode.c | 70 ++- Zend/zend_operators.c | 56 ++- Zend/zend_operators.h | 8 + Zend/zend_types.h | 160 +++++- Zend/zend_vm_def.h | 33 +- Zend/zend_vm_execute.h | 235 ++++----- build/gen_stub.php | 4 +- ext/date/php_date.c | 9 +- ext/date/php_date_arginfo.h | 8 +- ext/dom/php_dom_arginfo.h | 58 +-- ext/random/random_arginfo.h | 2 +- ext/reflection/php_reflection.c | 73 +-- ext/soap/soap_arginfo.h | 4 +- ext/spl/php_spl.c | 8 +- ext/spl/spl_array.c | 16 +- ext/spl/spl_dllist.c | 34 +- ext/spl/spl_fixedarray.c | 16 +- ext/spl/spl_functions.c | 5 +- ext/spl/spl_heap.c | 34 +- ext/spl/spl_observer.c | 51 +- ext/standard/var.c | 4 +- ext/standard/var_unserializer.re | 2 +- ext/zend_test/test_arginfo.h | 16 +- sapi/phpdbg/phpdbg_info.c | 10 +- 69 files changed, 2295 insertions(+), 718 deletions(-) create mode 100644 Zend/tests/generics/basic.phpt create mode 100644 Zend/tests/generics/duplicate_generic_param.phpt create mode 100644 Zend/tests/generics/generic_param_with_over_types.phpt create mode 100644 Zend/tests/generics/inheritance_bind_parent_param.phpt create mode 100644 Zend/tests/generics/inheritance_bind_parent_param_2.phpt create mode 100644 Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt create mode 100644 Zend/tests/generics/inheritance_default_param.phpt create mode 100644 Zend/tests/generics/inheritance_signature_check.phpt create mode 100644 Zend/tests/generics/inheritance_signature_check_2.phpt create mode 100644 Zend/tests/generics/inheritance_signature_check_3.phpt create mode 100644 Zend/tests/generics/inheritance_too_few_args.phpt create mode 100644 Zend/tests/generics/inheritance_too_few_args_2.phpt create mode 100644 Zend/tests/generics/inheritance_too_few_args_3.phpt create mode 100644 Zend/tests/generics/inheritance_too_many_args.phpt create mode 100644 Zend/tests/generics/inheritance_too_many_args_2.phpt create mode 100644 Zend/tests/generics/inheritance_too_many_args_3.phpt create mode 100644 Zend/tests/generics/param_same_as_class.phpt create mode 100644 Zend/tests/generics/required_after_optional.phpt create mode 100644 Zend/tests/generics/syntax.phpt create mode 100644 Zend/tests/generics/type_params_in_types.phpt create mode 100644 Zend/tests/generics/type_with_incorrect_number_of_args.phpt diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index b1f568da5d920..a26f26b65d65e 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -278,8 +278,8 @@ static inline bool can_elide_list_type( ZEND_ASSERT(!is_intersection); return can_elide_list_type(script, op_array, use_info, *single_type); } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type)); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(*single_type)); zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname); zend_string_release(lcname); bool result = ce && safe_instanceof(use_info->ce, ce); diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index b7c0a5ec4466a..3aa83a9468d6d 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -164,7 +164,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i /* These flags will always cause an exception */ ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT; - if (ce && !ce->parent && !ce->create_object && !ce->constructor && + if (ce && !ce->num_parents && !ce->create_object && !ce->constructor && !ce->destructor && !ce->__get && !ce->__set && !(ce->ce_flags & forbidden_flags) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { @@ -228,7 +228,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1( script, op_array, opline); if (ce && !ce->create_object && !ce->constructor && - !ce->destructor && !ce->__get && !ce->__set && !ce->parent) { + !ce->destructor && !ce->__get && !ce->__set && !ce->num_parents) { return 1; } break; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index f60f1f09a4b49..15f8c458181e9 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2385,8 +2385,8 @@ static uint32_t zend_convert_type(const zend_script *script, zend_type type, zen if (pce) { /* As we only have space to store one CE, * we use a plain object type for class unions. */ - if (ZEND_TYPE_HAS_NAME(type)) { - zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(type)); + if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *lcname = zend_string_tolower(ZEND_TYPE_PNR_NAME(type)); // TODO: Pass through op_array. *pce = zend_optimizer_get_class_entry(script, NULL, lcname); zend_string_release_ex(lcname, 0); @@ -2470,7 +2470,7 @@ static const zend_property_info *zend_fetch_static_prop_info(const zend_script * break; case ZEND_FETCH_CLASS_PARENT: if (op_array->scope && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { - ce = op_array->scope->parent; + ce = op_array->scope->parents[0]->ce; } break; } @@ -3341,8 +3341,8 @@ static zend_always_inline zend_result _zend_update_type_info( } break; case ZEND_FETCH_CLASS_PARENT: - if (op_array->scope && op_array->scope->parent && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { - UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_op->result_def); + if (op_array->scope && op_array->scope->parents[0]->ce && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { + UPDATE_SSA_OBJ_TYPE(op_array->scope->parents[0]->ce, 0, ssa_op->result_def); } else { UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); } @@ -3992,17 +3992,6 @@ ZEND_API zend_result zend_update_type_info( return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0); } -static uint32_t get_class_entry_rank(zend_class_entry *ce) { - uint32_t rank = 0; - if (ce->ce_flags & ZEND_ACC_LINKED) { - while (ce->parent) { - rank++; - ce = ce->parent; - } - } - return rank; -} - /* Compute least common ancestor on class inheritance tree only */ static zend_class_entry *join_class_entries( zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) { @@ -4014,22 +4003,20 @@ static zend_class_entry *join_class_entries( return NULL; } - rank1 = get_class_entry_rank(ce1); - rank2 = get_class_entry_rank(ce2); - - while (rank1 != rank2) { - if (rank1 > rank2) { - ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; - rank1--; - } else { - ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; - rank2--; - } + if (ce1->num_parents > ce2->num_parents) { + rank1 = ce1->num_parents - ce2->num_parents; + rank2 = 0; + } else { + rank1 = 0; + rank2 = ce2->num_parents - ce1->num_parents; } + ce1 = rank1 > 0 ? ce1->parents[rank1-1]->ce : ce1; + ce2 = rank2 > 0 ? ce2->parents[rank2-1]->ce : ce2; + while (ce1 != ce2) { - ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; - ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; + ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) || !ce1->num_parents ? NULL : ce1->parents[0]->ce; + ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) || !ce2->num_parents ? NULL : ce2->parents[0]->ce; } if (ce1) { @@ -5070,7 +5057,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op const zend_class_entry *ce = var_info->ce; if (var_info->is_instanceof || - !ce || ce->create_object || ce->__get || ce->__set || ce->parent) { + !ce || ce->create_object || ce->__get || ce->__set || ce->num_parents) { return 1; } diff --git a/Zend/tests/bug77530.phpt b/Zend/tests/bug77530.phpt index fdb2bac78b179..2cff416fda751 100644 --- a/Zend/tests/bug77530.phpt +++ b/Zend/tests/bug77530.phpt @@ -7,4 +7,4 @@ echo (2)::class; ?> --EXPECTF-- -Fatal error: Illegal class name in %s on line %d +Fatal error: Cannot use "::class" on int in %s on line %d diff --git a/Zend/tests/generics/basic.phpt b/Zend/tests/generics/basic.phpt new file mode 100644 index 0000000000000..cbcd897ea7008 --- /dev/null +++ b/Zend/tests/generics/basic.phpt @@ -0,0 +1,20 @@ +--TEST-- +Basic generic class declaration +--FILE-- + { +} + +class C { +} + +final class F { +} + +trait T

{ +} + +?> +--EXPECT-- + diff --git a/Zend/tests/generics/duplicate_generic_param.phpt b/Zend/tests/generics/duplicate_generic_param.phpt new file mode 100644 index 0000000000000..2b27daa8913bd --- /dev/null +++ b/Zend/tests/generics/duplicate_generic_param.phpt @@ -0,0 +1,11 @@ +--TEST-- +Duplicate generic parameter name +--FILE-- + { +} + +?> +--EXPECTF-- +Fatal error: Duplicate generic parameter T in %s on line %d diff --git a/Zend/tests/generics/generic_param_with_over_types.phpt b/Zend/tests/generics/generic_param_with_over_types.phpt new file mode 100644 index 0000000000000..d37945d3324f3 --- /dev/null +++ b/Zend/tests/generics/generic_param_with_over_types.phpt @@ -0,0 +1,73 @@ +--TEST-- +Combining a generic parameter with other types +--FILE-- + { + public ?T $prop; + public function method(?T $param) { + var_dump($param); + } +} +class ConcreteTest1 extends AbstractTest1 {} + +$obj = new ConcreteTest1; +$obj->method([]); +$obj->method(null); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +abstract class AbstractTest2 { + public T|int $prop; + public function method(T|int $param) { + var_dump($param); + } +} +class ConcreteTest2 extends AbstractTest2 {} + +$obj = new ConcreteTest2; +$obj->method([]); +$obj->method(42); +$obj->method("42"); + +echo "\n"; + +abstract class AbstractTest3 { + public T|stdClass $prop; + public function method(T|stdClass $param) { + var_dump($param); + } +} +class ConcreteTest3 extends AbstractTest3 {} + +$obj = new ConcreteTest3; +$obj->method([]); +$obj->method(new stdClass); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +array(0) { +} +NULL +AbstractTest1::method(): Argument #1 ($param) must be of type ?T (where T = array), string given, called in %s on line %d + +array(0) { +} +int(42) +int(42) + +array(0) { +} +object(stdClass)#3 (0) { +} +AbstractTest3::method(): Argument #1 ($param) must be of type T|stdClass (where T = array), string given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_bind_parent_param.phpt b/Zend/tests/generics/inheritance_bind_parent_param.phpt new file mode 100644 index 0000000000000..36a5d3dd208b6 --- /dev/null +++ b/Zend/tests/generics/inheritance_bind_parent_param.phpt @@ -0,0 +1,66 @@ +--TEST-- +Bind direct parent parameter during inheritance +--FILE-- + { + public T $prop; + + public function method(T $param) { + var_dump($param); + } +} + +class ConcreteInt extends WithParam { +} + +class ConcreteStdClass extends WithParam { +} + +class ConcreteSelf extends WithParam { +} + +$obj = new ConcreteInt; +$obj->method(42); +$obj->prop = 42; +var_dump($obj->prop); + +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $obj->prop = "string"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +$obj = new ConcreteStdClass; +$obj->method(new stdClass); +//$obj->prop = new stdClass; +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +/* TODO: This broke -- "self" resolves to WithParam here. +$obj = new ConcreteSelf; +$obj->method($obj); +try { + $obj->method(new stdClass); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +*/ + +?> +--EXPECTF-- +int(42) +int(42) +WithParam::method(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d +Cannot assign string to property WithParam::$prop of type T +object(stdClass)#1 (0) { +} +WithParam::method(): Argument #1 ($param) must be of type T (where T = stdClass), string given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_bind_parent_param_2.phpt b/Zend/tests/generics/inheritance_bind_parent_param_2.phpt new file mode 100644 index 0000000000000..48b24844b9d3e --- /dev/null +++ b/Zend/tests/generics/inheritance_bind_parent_param_2.phpt @@ -0,0 +1,87 @@ +--TEST-- +Bind multiple parent parameters during inheritance +--FILE-- + { + public function method(T1 $param1, T2 $param2) { + var_dump($param1); + var_dump($param2); + } +} + +abstract class WithSameParam extends WithParams { + public function method2(T $param) { + var_dump($param); + } +} + +abstract class WithOneFixedParam extends WithParams { + public function method2(T $param) { + var_dump($param); + } +} + +abstract class WithFirstNullableParam extends WithParams { +} + +class ConcreteIntInt extends WithSameParam { +} + +class ConcreteIntString extends WithOneFixedParam { +} + +class ConcreteNullableIntInt extends WithFirstNullableParam { +} + +$obj = new ConcreteIntInt; +$obj->method(42, 42); +$obj->method2(42); + +try { + $obj->method("string", "string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $obj->method2("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; +$obj = new ConcreteIntString; +$obj->method(42, "string"); +$obj->method2("string"); + +try { + $obj->method("string", 42); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $obj->method2(42); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; +$obj = new ConcreteNullableIntInt; +$obj->method(null, 42); + +?> +--EXPECTF-- +int(42) +int(42) +int(42) +WithParams::method(): Argument #1 ($param1) must be of type T1 (where T1 = int), string given, called in %s on line %d +WithSameParam::method2(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d + +int(42) +string(6) "string" +string(6) "string" +WithParams::method(): Argument #1 ($param1) must be of type T1 (where T1 = int), string given, called in %s on line %d +string(2) "42" + +NULL +int(42) diff --git a/Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt b/Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt new file mode 100644 index 0000000000000..eeb85db4d8dab --- /dev/null +++ b/Zend/tests/generics/inheritance_bind_passthru_parent_param.phpt @@ -0,0 +1,30 @@ +--TEST-- +Bind parent parameter passed through to grandparent during inheritance +--FILE-- + { + public function method(T $param) { + var_dump($param); + } +} + +abstract class WithParam2 extends WithParam { +} + +class Concrete extends WithParam2 { +} + +$obj = new Concrete; +$obj->method(42); + +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +int(42) +WithParam::method(): Argument #1 ($param) must be of type T (where T = int), string given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_default_param.phpt b/Zend/tests/generics/inheritance_default_param.phpt new file mode 100644 index 0000000000000..9f36247750e92 --- /dev/null +++ b/Zend/tests/generics/inheritance_default_param.phpt @@ -0,0 +1,49 @@ +--TEST-- +Defaulted type parameters during inheritance +--FILE-- + { + public function method(T1 $param1, T2 $param2) { + var_dump($param1); + var_dump($param2); + } +} + +class Concrete extends WithSimpleDefault { +} + +abstract class WithPrevParamDefault { + public function method(T1 $param1, T2 $param2) { + var_dump($param1); + var_dump($param2); + } +} + +class Concrete2 extends WithPrevParamDefault { +} + +$obj = new Concrete; +$obj->method("string", 42); +try { + $obj->method(42, "string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +$obj = new Concrete2; +$obj->method("string", "string"); +try { + $obj->method([], []); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +string(6) "string" +int(42) +WithSimpleDefault::method(): Argument #2 ($param2) must be of type T2 (where T2 = int), string given, called in %s on line %d +string(6) "string" +string(6) "string" +WithPrevParamDefault::method(): Argument #1 ($param1) must be of type T1 (where T1 = string), array given, called in %s on line %d diff --git a/Zend/tests/generics/inheritance_signature_check.phpt b/Zend/tests/generics/inheritance_signature_check.phpt new file mode 100644 index 0000000000000..2af4fa7770f50 --- /dev/null +++ b/Zend/tests/generics/inheritance_signature_check.phpt @@ -0,0 +1,16 @@ +--TEST-- +Validating signatures involving generic parameters +--FILE-- + { + public function method(T $param) {} +} + +class Test2 extends Test { + public function method(string $param) {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Test2::method(string $param) must be compatible with Test::method(T $param) in %s on line %d diff --git a/Zend/tests/generics/inheritance_signature_check_2.phpt b/Zend/tests/generics/inheritance_signature_check_2.phpt new file mode 100644 index 0000000000000..753688fe86bc8 --- /dev/null +++ b/Zend/tests/generics/inheritance_signature_check_2.phpt @@ -0,0 +1,25 @@ +--TEST-- +Validating signatures involving generic parameters +--FILE-- + { + public function method(T $param): T {} +} + +class Test2 extends Test { + public function method(int $param): int {} +} + +class Test3 extends Test { + public function method(T $param): T {} +} + +class Test4 extends Test { + public function method(?T $param): T {} +} + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/generics/inheritance_signature_check_3.phpt b/Zend/tests/generics/inheritance_signature_check_3.phpt new file mode 100644 index 0000000000000..ba17746039ce0 --- /dev/null +++ b/Zend/tests/generics/inheritance_signature_check_3.phpt @@ -0,0 +1,16 @@ +--TEST-- +Validating signatures involving generic parameters +--FILE-- + { + public function method(T $param): T {} +} + +class Test2 extends Test { + public function method(T2 $param): T2 {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Test2::method(T2 $param): T2 must be compatible with Test::method(T $param): T in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_few_args.phpt b/Zend/tests/generics/inheritance_too_few_args.phpt new file mode 100644 index 0000000000000..b045793b05ec9 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_few_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too few generic args to parent +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 2 generic arguments, but 1 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_few_args_2.phpt b/Zend/tests/generics/inheritance_too_few_args_2.phpt new file mode 100644 index 0000000000000..4b8ae56e4adc8 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_few_args_2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too few generic args to parent (2) +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects at least 2 generic arguments, but 1 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_few_args_3.phpt b/Zend/tests/generics/inheritance_too_few_args_3.phpt new file mode 100644 index 0000000000000..77f98873a5da9 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_few_args_3.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too few generic args to parent (3) +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 1 generic argument, but 0 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_many_args.phpt b/Zend/tests/generics/inheritance_too_many_args.phpt new file mode 100644 index 0000000000000..00f1ef24c3d91 --- /dev/null +++ b/Zend/tests/generics/inheritance_too_many_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too many generic args to parent +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 2 generic arguments, but 3 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_many_args_2.phpt b/Zend/tests/generics/inheritance_too_many_args_2.phpt new file mode 100644 index 0000000000000..262c21178541a --- /dev/null +++ b/Zend/tests/generics/inheritance_too_many_args_2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too many generic args to parent (2) +--FILE-- + { +} + +class C extends P { +} + +?> +--EXPECTF-- +Fatal error: Class P expects at most 2 generic arguments, but 3 provided in %s on line %d diff --git a/Zend/tests/generics/inheritance_too_many_args_3.phpt b/Zend/tests/generics/inheritance_too_many_args_3.phpt new file mode 100644 index 0000000000000..91f8bb3e966ba --- /dev/null +++ b/Zend/tests/generics/inheritance_too_many_args_3.phpt @@ -0,0 +1,14 @@ +--TEST-- +Passing too many generic args to parent (3) +--FILE-- + { +} + +?> +--EXPECTF-- +Fatal error: Class P expects exactly 0 generic arguments, but 3 provided in %s on line %d diff --git a/Zend/tests/generics/param_same_as_class.phpt b/Zend/tests/generics/param_same_as_class.phpt new file mode 100644 index 0000000000000..35f7249d3a90a --- /dev/null +++ b/Zend/tests/generics/param_same_as_class.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic parameter can't have same name as the class +--FILE-- + { +} + +?> +--EXPECTF-- +Fatal error: Generic parameter Foo has same name as class in %s on line %d diff --git a/Zend/tests/generics/required_after_optional.phpt b/Zend/tests/generics/required_after_optional.phpt new file mode 100644 index 0000000000000..8a0d696b9d9af --- /dev/null +++ b/Zend/tests/generics/required_after_optional.phpt @@ -0,0 +1,10 @@ +--TEST-- +Required generic parameter after optional +--FILE-- + {} + +?> +--EXPECTF-- +Fatal error: Required generic parameter T2 follows optional in %s on line %d diff --git a/Zend/tests/generics/syntax.phpt b/Zend/tests/generics/syntax.phpt new file mode 100644 index 0000000000000..a7fd2b3be60ab --- /dev/null +++ b/Zend/tests/generics/syntax.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test generic syntax +--FILE-- +::BAR); +var_dump(Foo::BAR); +var_dump(new Foo); +var_dump(new Foo(1)); +var_dump(new Foo); +var_dump(new Foo(1)); + +?> +--EXPECTF-- +Fatal error: Generic type arguments are currently not supported here yet in %s on line %d diff --git a/Zend/tests/generics/type_params_in_types.phpt b/Zend/tests/generics/type_params_in_types.phpt new file mode 100644 index 0000000000000..01de96896d075 --- /dev/null +++ b/Zend/tests/generics/type_params_in_types.phpt @@ -0,0 +1,75 @@ +--TEST-- +Concrete parameterized types used in type expressions +--FILE-- + { + public function method(T $param) { + var_dump($param); + } +} + +abstract class AbstractPassthru extends AbstractTest { +} + +abstract class AbstractDefaulted extends AbstractTest { +} + +class ConcreteInt extends AbstractTest { +} + +class ConcreteString extends AbstractTest { +} + +class ConcreteIntPassthru extends AbstractPassthru { +} + +class ConcreteIntDefaulted extends AbstractDefaulted { +} +class ConcreteStringDefaulted extends AbstractDefaulted { +} + +function test(AbstractTest $test) { + $test->method(42); +} + +test(new ConcreteInt); +test(new ConcreteIntPassthru); +test(new ConcreteIntDefaulted); +try { + test(new ConcreteString); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +function test2(AbstractDefaulted $test) { + $test->method(42); +} + +function test3(AbstractDefaulted $test) { + $test->method(42); +} + +test2(new ConcreteIntDefaulted); +test3(new ConcreteIntDefaulted); +try { + test2(new ConcreteStringDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + test3(new ConcreteStringDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +int(42) +int(42) +int(42) +test(): Argument #1 ($test) must be of type AbstractTest, ConcreteString given, called in %s on line %d +int(42) +int(42) +test2(): Argument #1 ($test) must be of type AbstractDefaulted, ConcreteStringDefaulted given, called in %s on line %d +test3(): Argument #1 ($test) must be of type AbstractDefaulted, ConcreteStringDefaulted given, called in %s on line %d diff --git a/Zend/tests/generics/type_with_incorrect_number_of_args.phpt b/Zend/tests/generics/type_with_incorrect_number_of_args.phpt new file mode 100644 index 0000000000000..98bd9f2e5313c --- /dev/null +++ b/Zend/tests/generics/type_with_incorrect_number_of_args.phpt @@ -0,0 +1,48 @@ +--TEST-- +Type that uses an incorrect number of generic args +--FILE-- + {} +class C2 {} +class C3 {} + +function test1(C1 $param) {} +function test2(C2 $param) {} +function test3(C2 $param) {} +function test4(C3 $param) {} +function test5(C3 $param) {} + +try { + test1(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test2(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test3(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test4(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + test5(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Class C1 expects exactly 1 generic argument, but 2 provided +Class C2 expects exactly 2 generic arguments, but 3 provided +Class C2 expects exactly 2 generic arguments, but 1 provided +Class C3 expects at most 3 generic arguments, but 4 provided +Class C3 expects at least 2 generic arguments, but 1 provided diff --git a/Zend/tests/oss_fuzz_59764.phpt b/Zend/tests/oss_fuzz_59764.phpt index c8ba573ff9a7e..2d3125251c22d 100644 --- a/Zend/tests/oss_fuzz_59764.phpt +++ b/Zend/tests/oss_fuzz_59764.phpt @@ -5,4 +5,4 @@ oss-fuzz #59764: Test const B = []::{A}; ?> --EXPECTF-- -Fatal error: Class name must be a valid object or a string in %s on line %d +Fatal error: Dynamic class names are not allowed in compile-time class constant references in %s on line %d diff --git a/Zend/zend.c b/Zend/zend.c index fb0af12f9e510..3277aa55f0ae7 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1050,7 +1050,6 @@ void zend_register_standard_ini_entries(void) /* {{{ */ } /* }}} */ - /* Unlink the global (r/o) copies of the class, function and constant tables, * and use a fresh r/w copy for the startup thread */ diff --git a/Zend/zend.h b/Zend/zend.h index d60da5f83ac11..05188b39672fd 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -144,17 +144,29 @@ struct _zend_inheritance_cache_entry { zend_class_entry *traits_and_interfaces[1]; }; +typedef struct _zend_generic_param { + zend_string *name; + zend_type bound_type; + zend_type default_type; +} zend_generic_param; + struct _zend_class_entry { char type; zend_string *name; - /* class_entry or string depending on ZEND_ACC_LINKED */ - union { - zend_class_entry *parent; - zend_string *parent_name; - }; int refcount; uint32_t ce_flags; + /* Before inheritance, this is either zero or one. + * After inheritance it includes grandparents as well. */ + uint32_t num_parents; + union { + /* List of parents. The direct parent is parents[0], + * below that are inherited grandparents. */ + zend_class_reference **parents; + /* Before linking, only the name of the direct parent is stored. */ + zend_packed_name_reference parent_name; + }; + int default_properties_count; int default_static_members_count; zval *default_properties_table; @@ -219,6 +231,14 @@ struct _zend_class_entry { uint32_t enum_backing_type; HashTable *backed_enum_table; + /* generic_params are the free generic parameters on this class. + * bound_generic_args are the bound generic parameters of parent classes. */ + uint32_t num_generic_params; + uint32_t num_required_generic_params; + uint32_t num_bound_generic_args; + zend_generic_param *generic_params; + zend_type *bound_generic_args; + union { struct { zend_string *filename; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index d065c4d61232a..0a597753a1baa 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -468,6 +468,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32_t arg_num) { zend_function *func = EG(current_execute_data)->func; + zend_class_entry *scope = EG(current_execute_data)->func->common.scope; ZEND_ASSERT(arg_num > 0); uint32_t arg_offset = arg_num - 1; if (arg_offset >= func->common.num_args) { @@ -481,7 +482,7 @@ static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32 /* If no type is specified in arginfo, use the specified fallback_type determined through * zend_parse_parameters instead. */ - zend_string *type_str = zend_type_to_string(arg_info->type); + zend_string *type_str = zend_type_to_string(arg_info->type, scope); const char *type = type_str ? ZSTR_VAL(type_str) : fallback_type; zend_error(E_DEPRECATED, "%s(): Passing null to parameter #%" PRIu32 "%s%s%s of type %s is deprecated", @@ -1424,7 +1425,7 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) { return FAILURE; } /* property initializers must always be evaluated with strict types */; - if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, /* strict */ 1))) { + if (UNEXPECTED(!zend_verify_property_type(NULL, prop_info, &tmp, /* strict */ 1))) { zval_ptr_dtor(&tmp); return FAILURE; } @@ -1491,8 +1492,8 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } } - if (class_type->parent) { - if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) { + if (class_type->num_parents) { + if (UNEXPECTED(zend_update_class_constants(class_type->parents[0]->ce) != SUCCESS)) { return FAILURE; } } @@ -1655,7 +1656,7 @@ ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properti zval tmp; ZVAL_COPY_VALUE(&tmp, prop); - if (UNEXPECTED(!zend_verify_property_type(property_info, &tmp, 0))) { + if (UNEXPECTED(!zend_verify_property_type(object, property_info, &tmp, 0))) { continue; } ZVAL_COPY_VALUE(slot, &tmp); @@ -2564,7 +2565,7 @@ static void zend_check_magic_method_arg_type(uint32_t arg_num, const zend_class_ zend_error(error_type, "%s::%s(): Parameter #%d ($%s) must be of type %s when declared", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name), arg_num + 1, ZSTR_VAL(fptr->common.arg_info[arg_num].name), - ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(arg_type)))); + ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(arg_type), ce))); } } @@ -2590,7 +2591,7 @@ static void zend_check_magic_method_return_type(const zend_class_entry *ce, cons if (extra_types || (is_complex_type && return_type != MAY_BE_OBJECT)) { zend_error(error_type, "%s::%s(): Return type must be %s when declared", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name), - ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(return_type)))); + ZSTR_VAL(zend_type_to_string((zend_type) ZEND_TYPE_INIT_MASK(return_type), ce))); } } @@ -2771,19 +2772,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arg_info_toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() static zend_always_inline void zend_normalize_internal_type(zend_type *type) { - ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*type)); zend_type *current; ZEND_TYPE_FOREACH(*type, current) { - if (ZEND_TYPE_HAS_NAME(*current)) { - zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*current)); + if (ZEND_TYPE_HAS_PNR(*current)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_PNR_NAME(*current)); zend_alloc_ce_cache(name); ZEND_TYPE_SET_PTR(*current, name); } else if (ZEND_TYPE_HAS_LIST(*current)) { zend_type *inner; ZEND_TYPE_FOREACH(*current, inner) { - ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*inner) && !ZEND_TYPE_HAS_LIST(*inner)); - if (ZEND_TYPE_HAS_NAME(*inner)) { - zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner)); + ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*inner)); + if (ZEND_TYPE_HAS_PNR(*inner)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_PNR_NAME(*inner)); zend_alloc_ce_cache(name); ZEND_TYPE_SET_PTR(*inner, name); } @@ -2862,7 +2862,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend internal_function->num_args--; } if (ZEND_TYPE_IS_SET(info->type)) { - if (ZEND_TYPE_HAS_NAME(info->type)) { + if (ZEND_TYPE_HAS_LITERAL_NAME(info->type)) { const char *type_name = ZEND_TYPE_LITERAL_NAME(info->type); if (!scope && (!strcasecmp(type_name, "self") || !strcasecmp(type_name, "parent"))) { zend_error_noreturn(E_CORE_ERROR, "Cannot declare a return type of %s outside of a class scope", type_name); @@ -2975,7 +2975,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend // As a temporary workaround, we split the type name on `|` characters, // converting it to an union type if necessary. const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type); - new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; size_t num_types = 1; const char *p = class_name; @@ -2989,7 +2988,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1); zend_alloc_ce_cache(str); ZEND_TYPE_SET_PTR(new_arg_info[i].type, str); - new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; + new_arg_info[i].type.type_mask |= _ZEND_TYPE_PNR_BIT; } else { /* Union type */ zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types)); @@ -3003,7 +3002,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend const char *end = strchr(start, '|'); zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1); zend_alloc_ce_cache(str); - list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0); + list->types[j] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(str), 0, 0); if (!end) { break; } @@ -3287,7 +3286,8 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */ { - zend_class_entry *class_entry = malloc(sizeof(zend_class_entry)); + void *ref = malloc(sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); + zend_class_entry *class_entry = zend_init_class_entry_header(ref); zend_string *lowercase_name; *class_entry = *orig_class_entry; @@ -3586,17 +3586,17 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc } else if (zend_string_equals_literal(lcname, "parent")) { if (!scope) { if (error) *error = estrdup("cannot access \"parent\" when no class scope is active"); - } else if (!scope->parent) { + } else if (!scope->num_parents) { if (error) *error = estrdup("cannot access \"parent\" when current class scope has no parent"); } else { if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"parent\" in callables is deprecated"); } fcc->called_scope = zend_get_called_scope(frame); - if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope->parent)) { - fcc->called_scope = scope->parent; + if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope->parents[0]->ce)) { + fcc->called_scope = scope->parents[0]->ce; } - fcc->calling_scope = scope->parent; + fcc->calling_scope = scope->parents[0]->ce; if (!fcc->object) { fcc->object = zend_get_this_object(frame); } @@ -4869,7 +4869,7 @@ ZEND_API zend_result zend_update_static_property_ex(zend_class_entry *scope, zen Z_TRY_ADDREF_P(value); if (ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_COPY_VALUE(&tmp, value); - if (!zend_verify_property_type(prop_info, &tmp, /* strict */ 0)) { + if (!zend_verify_property_type(NULL, prop_info, &tmp, /* strict */ 0)) { Z_TRY_DELREF_P(value); return FAILURE; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 6e44cdd7628b0..a9a5759443f05 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -303,7 +303,7 @@ typedef struct _zend_fcall_info_cache { class_container.__debugInfo = NULL; \ class_container.__serialize = NULL; \ class_container.__unserialize = NULL; \ - class_container.parent = NULL; \ + class_container.num_parents = 0; \ class_container.num_interfaces = 0; \ class_container.trait_names = NULL; \ class_container.num_traits = 0; \ @@ -2539,6 +2539,11 @@ static zend_always_inline bool zend_parse_arg_obj_or_str( return zend_parse_arg_str(arg, destination_string, allow_null, arg_num); } +/* Get root class at start of "extends" chain. */ +static zend_always_inline zend_class_entry *zend_class_entry_get_root(zend_class_entry *ce) { + return ce->num_parents ? ce->parents[ce->num_parents - 1]->ce : ce; +} + END_EXTERN_C() #endif /* ZEND_API_H */ diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 525d9dfe9a742..f6307dc71e7f8 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -604,6 +604,9 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( ZVAL_EMPTY_STRING(result); } break; + case ZEND_AST_CLASS_REF: + // TODO + return zend_ast_evaluate(&op1, ast->child[0], scope); case ZEND_AST_CLASS_NAME: if (!scope) { zend_throw_error(NULL, "Cannot use \"self\" when no class scope is active"); @@ -612,12 +615,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( if (ast->attr == ZEND_FETCH_CLASS_SELF) { ZVAL_STR_COPY(result, scope->name); } else if (ast->attr == ZEND_FETCH_CLASS_PARENT) { - if (!scope->parent) { + if (!scope->num_parents) { zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); return FAILURE; } - ZVAL_STR_COPY(result, scope->parent->name); + ZVAL_STR_COPY(result, scope->parents[0]->ce->name); } else { ZEND_ASSERT(0 && "Should have errored during compilation"); } @@ -833,7 +836,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } case ZEND_AST_CLASS_CONST: { - zend_string *class_name = zend_ast_get_str(ast->child[0]); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->kind == ZEND_AST_CLASS_REF); + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + + zend_string *class_name = zend_ast_get_str(class_ast->child[0]); if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) { return FAILURE; } @@ -869,7 +879,13 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } case ZEND_AST_NEW: { - zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->kind == ZEND_AST_CLASS_REF); + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + zend_class_entry *ce = zend_ast_fetch_class(class_ast->child[0], scope); if (!ce) { return FAILURE; } @@ -1328,6 +1344,16 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int zend_ast_export_ex(str, ast, priority, indent); } +static ZEND_COLD void zend_ast_export_class_name(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_CLASS_REF) { + ZEND_ASSERT(ast->child[1] == NULL && "Generic params not supported yet"); + zend_ast_export_ns_name(str, ast->child[0], priority, indent); + return; + } + zend_ast_export_ex(str, ast, priority, indent); +} + static ZEND_COLD bool zend_ast_valid_var_char(char ch) { unsigned char c = (unsigned char)ch; @@ -1440,7 +1466,7 @@ static ZEND_COLD void zend_ast_export_name_list_ex(smart_str *str, zend_ast_list if (i != 0) { smart_str_appends(str, separator); } - zend_ast_export_name(str, list->child[i], 0, indent); + zend_ast_export_class_name(str, list->child[i], 0, indent); i++; } } @@ -1604,7 +1630,7 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, zval *zv, int priorit static ZEND_COLD void zend_ast_export_class_no_header(smart_str *str, zend_ast_decl *decl, int indent) { if (decl->child[0]) { smart_str_appends(str, " extends "); - zend_ast_export_ns_name(str, decl->child[0], 0, indent); + zend_ast_export_class_name(str, decl->child[0], 0, indent); } if (decl->child[1]) { smart_str_appends(str, " implements "); @@ -1624,7 +1650,7 @@ static ZEND_COLD void zend_ast_export_attribute_group(smart_str *str, zend_ast * if (i) { smart_str_appends(str, ", "); } - zend_ast_export_ns_name(str, attr->child[0], 0, indent); + zend_ast_export_class_name(str, attr->child[0], 0, indent); if (attr->child[1]) { smart_str_appendc(str, '('); @@ -1686,7 +1712,7 @@ static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int in if (ast->attr & ZEND_TYPE_NULLABLE) { smart_str_appendc(str, '?'); } - zend_ast_export_ns_name(str, ast, 0, indent); + zend_ast_export_class_name(str, ast, 0, indent); } #define BINARY_OP(_op, _p, _pl, _pr) do { \ @@ -2095,7 +2121,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_var(str, ast->child[1], 0, indent); break; case ZEND_AST_STATIC_PROP: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::$"); zend_ast_export_var(str, ast->child[1], 0, indent); break; @@ -2109,7 +2135,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, "..."); break; case ZEND_AST_CLASS_CONST: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -2126,7 +2152,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio EMPTY_SWITCH_DEFAULT_CASE() } } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); } smart_str_appends(str, "::class"); break; @@ -2204,7 +2230,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } zend_ast_export_class_no_header(str, decl, indent); } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2213,7 +2239,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_INSTANCEOF: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, " instanceof "); - zend_ast_export_ns_name(str, ast->child[1], 0, indent); + zend_ast_export_class_name(str, ast->child[1], 0, indent); break; case ZEND_AST_YIELD: if (priority > 70) smart_str_appendc(str, '('); @@ -2341,7 +2367,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_METHOD_REFERENCE: if (ast->child[0]) { - zend_ast_export_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); } zend_ast_export_name(str, ast->child[1], 0, indent); @@ -2395,7 +2421,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendc(str, ')'); break; case ZEND_AST_STATIC_CALL: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_class_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_var(str, ast->child[1], 0, indent); smart_str_appendc(str, '('); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index b5001a9ff8f54..4d2894a646946 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -67,6 +67,8 @@ enum _zend_ast_kind { ZEND_AST_ATTRIBUTE_GROUP, ZEND_AST_MATCH_ARM_LIST, ZEND_AST_MODIFIER_LIST, + ZEND_AST_GENERIC_PARAM_LIST, + ZEND_AST_GENERIC_ARG_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -149,6 +151,7 @@ enum _zend_ast_kind { ZEND_AST_MATCH, ZEND_AST_MATCH_ARM, ZEND_AST_NAMED_ARG, + ZEND_AST_CLASS_REF, /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -162,6 +165,7 @@ enum _zend_ast_kind { ZEND_AST_PROP_ELEM, ZEND_AST_CONST_ELEM, ZEND_AST_CLASS_CONST_GROUP, + ZEND_AST_GENERIC_PARAM, // Pseudo node for initializing enums ZEND_AST_CONST_ENUM_INIT, diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 74290c1f5aee7..3fc5de03fb6e7 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -611,8 +611,8 @@ ZEND_FUNCTION(get_parent_class) ce = zend_get_executed_scope(); } - if (ce && ce->parent) { - RETURN_STR_COPY(ce->parent->name); + if (ce && ce->num_parents) { + RETURN_STR_COPY(ce->parents[0]->ce->name); } else { RETURN_FALSE; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index afce5150e08d3..fc970e3c7eea6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -20,6 +20,7 @@ #include #include "zend.h" +#include "zend_ast.h" #include "zend_attributes.h" #include "zend_compile.h" #include "zend_constants.h" @@ -35,6 +36,7 @@ #include "zend_enum.h" #include "zend_observer.h" #include "zend_call_stack.h" +#include "zend_smart_str.h" #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -1307,12 +1309,13 @@ static zend_string *add_type_string(zend_string *type, zend_string *new_type, bo return result; } -static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scope) { - if (scope) { +static zend_string *resolve_class_name( + zend_string *name, const zend_class_entry *scope, bool resolve) { + if (resolve && scope) { if (zend_string_equals_literal_ci(name, "self")) { name = scope->name; - } else if (zend_string_equals_literal_ci(name, "parent") && scope->parent) { - name = scope->parent->name; + } else if (zend_string_equals_literal_ci(name, "parent") && scope->num_parents) { + name = scope->parents[0]->ce->name; } } @@ -1326,18 +1329,55 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop return zend_string_copy(name); } +static zend_string *zend_format_generic_name(zend_string *name, zend_type_list *args) { + if (args->num_types == 0) { + return zend_string_copy(name); + } else { + smart_str str = {0}; + smart_str_append(&str, name); + smart_str_appendc(&str, '<'); + for (uint32_t i = 0; i < args->num_types; i++) { + if (i != 0) { + smart_str_appends(&str, ", "); + } + zend_string *type_str = zend_type_to_string(args->types[i], NULL); + smart_str_append(&str, type_str); + zend_string_release(type_str); + } + smart_str_appendc(&str, '>'); + return smart_str_extract(&str); + } +} + +static zend_string *zend_class_ref_to_string(zend_class_reference *ce_ref) { + return zend_format_generic_name(ce_ref->ce->name, &ce_ref->args); +} + +static zend_string *zend_name_ref_to_string(zend_name_reference *name_ref) { + return zend_format_generic_name(name_ref->name, &name_ref->args); +} + +static zend_string *zend_pnr_to_string(zend_packed_name_reference pnr) { + if (ZEND_PNR_IS_COMPLEX(pnr)) { + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(pnr); + return zend_name_ref_to_string(ref); + } else { + return zend_string_copy(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + } +} + static zend_string *add_intersection_type(zend_string *str, - zend_type_list *intersection_type_list, zend_class_entry *scope, - bool is_bracketed) + zend_type_list *intersection_type_list, const zend_class_entry *scope, + bool is_bracketed, bool resolve) { zend_type *single_type; zend_string *intersection_str = NULL; ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type)); - zend_string *name = ZEND_TYPE_NAME(*single_type); - zend_string *resolved = resolve_class_name(name, scope); + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*single_type)); + zend_string *name = ZEND_TYPE_PNR_NAME(*single_type); + zend_string *resolved = resolve_class_name(name, scope, resolve); intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true); zend_string_release(resolved); } ZEND_TYPE_LIST_FOREACH_END(); @@ -1354,30 +1394,54 @@ static zend_string *add_intersection_type(zend_string *str, return str; } -zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) { + +static zend_string *zend_type_to_string_impl( + zend_type type, const zend_class_entry *scope, bool resolve) { zend_string *str = NULL; /* Pure intersection type */ if (ZEND_TYPE_IS_INTERSECTION(type)) { ZEND_ASSERT(!ZEND_TYPE_IS_UNION(type)); - str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false); + str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false, resolve); } else if (ZEND_TYPE_HAS_LIST(type)) { /* A union type might not be a list */ zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { - str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), scope, /* is_bracketed */ true); + str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), scope, /* is_bracketed */ true, resolve); continue; } - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type)); - zend_string *name = ZEND_TYPE_NAME(*list_type); - zend_string *resolved = resolve_class_name(name, scope); - str = add_type_string(str, resolved, /* is_intersection */ false); - zend_string_release(resolved); + if (ZEND_TYPE_HAS_CLASS_REF(*list_type)) { + zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(*list_type); + zend_string *name = zend_class_ref_to_string(ce_ref); + str = add_type_string(str, name, /* is_intersection */ false); + zend_string_release(name); + } else if (ZEND_TYPE_HAS_PNR(*list_type)) { + zend_string *name = zend_pnr_to_string(ZEND_TYPE_PNR(*list_type)); + zend_string *resolved = resolve_class_name(name, scope, resolve); + str = add_type_string(str, resolved, /* is_intersection */ false); + zend_string_release(resolved); + zend_string_release(name); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + str = add_type_string(str, param->name, /* is_intersection */ false); + } } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(type)) { - str = resolve_class_name(ZEND_TYPE_NAME(type), scope); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *name = zend_pnr_to_string(ZEND_TYPE_PNR(type)); + str = resolve_class_name(name, scope, resolve); + zend_string_release(name); + } else if (ZEND_TYPE_HAS_CLASS_REF(type)) { + zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(type); + str = zend_class_ref_to_string(ce_ref); + } else if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + str = zend_string_copy(param->name); } uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); @@ -1444,8 +1508,12 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop return str; } -ZEND_API zend_string *zend_type_to_string(zend_type type) { - return zend_type_to_string_resolved(type, NULL); +ZEND_API zend_string *zend_type_to_string(zend_type type, const zend_class_entry *scope) { + return zend_type_to_string_impl(type, scope, 0); +} + +zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry *scope) { + return zend_type_to_string_impl(type, scope, 1); } static bool is_generator_compatible_class_type(zend_string *name) { @@ -1467,8 +1535,8 @@ static void zend_mark_function_as_generator(void) /* {{{ */ if (!valid_type) { zend_type *single_type; ZEND_TYPE_FOREACH(return_type, single_type) { - if (ZEND_TYPE_HAS_NAME(*single_type) - && is_generator_compatible_class_type(ZEND_TYPE_NAME(*single_type))) { + if (ZEND_TYPE_HAS_PNR(*single_type) + && is_generator_compatible_class_type(ZEND_TYPE_PNR_NAME(*single_type))) { valid_type = 1; break; } @@ -1476,7 +1544,7 @@ static void zend_mark_function_as_generator(void) /* {{{ */ } if (!valid_type) { - zend_string *str = zend_type_to_string(return_type); + zend_string *str = zend_type_to_string(return_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Generator return type must be a supertype of Generator, %s given", ZSTR_VAL(str)); @@ -1656,6 +1724,15 @@ static inline bool class_name_refers_to_active_ce(zend_string *class_name, uint3 } /* }}} */ +static zend_ast *unwrap_non_generic_class_ref(zend_ast *class_ast) +{ + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + return class_ast->child[0]; +} + uint32_t zend_get_class_fetch_type(const zend_string *name) /* {{{ */ { if (zend_string_equals_literal_ci(name, "self")) { @@ -1681,15 +1758,57 @@ static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ } /* }}} */ -static zend_string *zend_resolve_const_class_name_reference(zend_ast *ast, const char *type) +static zend_string *zend_resolve_const_class_name_reference(zend_ast *class_ast, const char *type) { - zend_string *class_name = zend_ast_get_str(ast); - if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(ast)) { + zend_ast *name_ast = unwrap_non_generic_class_ref(class_ast); + zend_string *class_name = zend_ast_get_str(name_ast); + if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(name_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as %s, as it is reserved", ZSTR_VAL(class_name), type); } - return zend_resolve_class_name(class_name, ast->attr); + return zend_resolve_class_name(class_name, name_ast->attr); +} + +static zend_type zend_compile_typename(zend_ast *ast); + +static zend_name_reference *zend_compile_name_reference( + zend_string *name, zend_ast *args_ast) { + zend_ast_list *list = args_ast ? zend_ast_get_list(args_ast) : NULL; + uint32_t num_types = list ? list->children : 0; + size_t alloc_size = ZEND_CLASS_REF_SIZE(num_types); + zend_name_reference *ref = zend_arena_alloc(&CG(arena), alloc_size); + ref->name = name; + ref->args.num_types = num_types; + if (list) { + for (uint32_t i = 0; i < num_types; i++) { + zend_ast *type_ast = list->child[i]; + ref->args.types[i] = zend_compile_typename(type_ast); + } + } + return ref; +} + +static zend_packed_name_reference zend_compile_pnr( + zend_string *name, zend_ast *args_ast) { + if (args_ast) { + return ZEND_PNR_ENCODE_REF(zend_compile_name_reference(name, args_ast)); + } else { + return ZEND_PNR_ENCODE_NAME(name); + } +} + +static zend_packed_name_reference zend_compile_default_pnr( + zend_ast *class_ast, const char *type) { + zend_ast *name_ast = class_ast->child[0]; + zend_string *class_name = zend_ast_get_str(name_ast); + if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(name_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use '%s' as %s, as it is reserved", + ZSTR_VAL(class_name), type); + } + class_name = zend_resolve_class_name(class_name, name_ast->attr); + return zend_compile_pnr(class_name, class_ast->child[1]); } static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ @@ -1712,12 +1831,14 @@ static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *c { uint32_t fetch_type; zval *class_name; + zend_ast *name_ast; - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { return 0; } - class_name = zend_ast_get_zval(class_ast); + name_ast = unwrap_non_generic_class_ref(class_ast); + class_name = zend_ast_get_zval(name_ast); if (Z_TYPE_P(class_name) != IS_STRING) { zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name"); @@ -1734,16 +1855,16 @@ static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *c } return 0; case ZEND_FETCH_CLASS_PARENT: - if (CG(active_class_entry) && CG(active_class_entry)->parent_name + if (CG(active_class_entry) && CG(active_class_entry)->num_parents && zend_is_scope_known()) { - ZVAL_STR_COPY(zv, CG(active_class_entry)->parent_name); + ZVAL_STR_COPY(zv, ZEND_PNR_GET_NAME(CG(active_class_entry)->parent_name)); return 1; } return 0; case ZEND_FETCH_CLASS_STATIC: return 0; case ZEND_FETCH_CLASS_DEFAULT: - ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast)); + ZVAL_STR(zv, zend_resolve_class_name_ast(name_ast)); return 1; EMPTY_SWITCH_DEFAULT_CASE() } @@ -1770,13 +1891,14 @@ static bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry if (ce == scope) { return 1; } - if (!ce->parent) { + if (!ce->num_parents) { break; } if (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT) { - ce = ce->parent; + ce = ce->parents[0]->ce; } else { - ce = zend_hash_find_ptr_lc(CG(class_table), ce->parent_name); + zend_string *parent_name = ZEND_PNR_GET_NAME(ce->parent_name); + ce = zend_hash_find_ptr_lc(CG(class_table), parent_name); if (!ce) { break; } @@ -2016,11 +2138,14 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand ce->iterator_funcs_ptr = NULL; ce->arrayaccess_funcs_ptr = NULL; ce->get_static_method = NULL; - ce->parent = NULL; - ce->parent_name = NULL; + ce->num_parents = 0; + ce->parent_name = 0; ce->num_interfaces = 0; ce->interfaces = NULL; ce->num_traits = 0; + ce->num_generic_params = 0; + ce->num_required_generic_params = 0; + ce->num_bound_generic_args = 0; ce->trait_names = NULL; ce->trait_aliases = NULL; ce->trait_precedences = NULL; @@ -2697,13 +2822,14 @@ static inline bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */ } /* }}} */ -static inline bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */ +static inline bool zend_is_const_default_class_ref(zend_ast *class_ast) /* {{{ */ { - if (name_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { return 0; } - return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type_ast(name_ast); + return ZEND_FETCH_CLASS_DEFAULT == + zend_get_class_fetch_type_ast(unwrap_non_generic_class_ref(class_ast)); } /* }}} */ @@ -2751,14 +2877,14 @@ static inline void zend_set_class_name_op1(zend_op *opline, znode *class_node) / } /* }}} */ -static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t fetch_flags) /* {{{ */ +static void zend_compile_class_ref(znode *result, zend_ast *class_ast, uint32_t fetch_flags) /* {{{ */ { uint32_t fetch_type; - if (name_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { znode name_node; - zend_compile_expr(&name_node, name_ast); + zend_compile_expr(&name_node, class_ast); if (name_node.op_type == IS_CONST) { zend_string *name; @@ -2787,6 +2913,8 @@ static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t f return; } + zend_ast *name_ast = unwrap_non_generic_class_ref(class_ast); + /* Fully qualified names are always default refs */ if (name_ast->attr == ZEND_NAME_FQ) { result->op_type = IS_CONST; @@ -6146,7 +6274,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */ opline->opcode = ZEND_CATCH; opline->op1_type = IS_CONST; opline->op1.constant = zend_add_class_name_literal( - zend_resolve_class_name_ast(class_ast)); + zend_resolve_class_name_ast(unwrap_non_generic_class_ref(class_ast))); opline->extended_value = zend_alloc_cache_slot(); if (var_name && zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -6410,6 +6538,20 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */ } /* }}} */ +static uint32_t lookup_generic_param_id(zend_string *name) { + if (!CG(active_class_entry) || CG(active_class_entry)->num_generic_params == 0) { + return (uint32_t) -1; + } + + for (uint32_t i = 0; i < CG(active_class_entry)->num_generic_params; i++) { + zend_generic_param *param = &CG(active_class_entry)->generic_params[i]; + if (zend_string_equals(param->name, name)) { + return i; + } + } + return (uint32_t) -1; +} + static zend_type zend_compile_single_typename(zend_ast *ast) { ZEND_ASSERT(!(ast->attr & ZEND_TYPE_NULLABLE)); @@ -6421,11 +6563,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast) return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0); } else { - zend_string *class_name = zend_ast_get_str(ast); + ZEND_ASSERT(ast->kind == ZEND_AST_CLASS_REF); + zend_ast *name_ast = ast->child[0]; + zend_ast *args_ast = ast->child[1]; + zend_string *class_name = zend_ast_get_str(name_ast); uint8_t type_code = zend_lookup_builtin_type_by_name(class_name); if (type_code != 0) { - if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { + if ((name_ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { zend_error_noreturn(E_COMPILE_ERROR, "Type declaration '%s' must be unqualified", ZSTR_VAL(zend_string_tolower(class_name))); @@ -6442,17 +6587,22 @@ static zend_type zend_compile_single_typename(zend_ast *ast) return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0); } else { const char *correct_name; - zend_string *orig_name = zend_ast_get_str(ast); - uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); + zend_string *orig_name = zend_ast_get_str(name_ast); + uint32_t generic_param_id = lookup_generic_param_id(orig_name); + if (name_ast->attr == ZEND_NAME_NOT_FQ && generic_param_id != (uint32_t) -1) { + return (zend_type) ZEND_TYPE_INIT_GENERIC_PARAM(generic_param_id, 0); + } + + uint32_t fetch_type = zend_get_class_fetch_type_ast(name_ast); if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { - class_name = zend_resolve_class_name_ast(ast); + class_name = zend_resolve_class_name_ast(name_ast); zend_assert_valid_class_name(class_name); } else { zend_ensure_valid_class_fetch_type(fetch_type); zend_string_addref(class_name); } - if (ast->attr == ZEND_NAME_NOT_FQ + if (name_ast->attr == ZEND_NAME_NOT_FQ && !args_ast && zend_is_confusable_type(orig_name, &correct_name) && zend_is_not_imported(orig_name)) { const char *extra = @@ -6472,8 +6622,8 @@ static zend_type zend_compile_single_typename(zend_ast *ast) } class_name = zend_new_interned_string(class_name); - zend_alloc_ce_cache(class_name); - return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0); + zend_packed_name_reference pnr = zend_compile_pnr(class_name, args_ast); + return (zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, _ZEND_TYPE_ARENA_BIT); } } } @@ -6501,7 +6651,7 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type) zend_type *inner_type; ZEND_TYPE_LIST_FOREACH(larger_type_list, inner_type) - if (zend_string_equals_ci(ZEND_TYPE_NAME(*inner_type), ZEND_TYPE_NAME(*outer_type))) { + if (zend_string_equals_ci(ZEND_TYPE_PNR_NAME(*inner_type), ZEND_TYPE_PNR_NAME(*outer_type))) { sum++; break; } @@ -6512,11 +6662,11 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type zend_string *smaller_type_str; zend_string *larger_type_str; if (flipped) { - smaller_type_str = zend_type_to_string(right_type); - larger_type_str = zend_type_to_string(left_type); + smaller_type_str = zend_type_to_string(right_type, CG(active_class_entry)); + larger_type_str = zend_type_to_string(left_type, CG(active_class_entry)); } else { - smaller_type_str = zend_type_to_string(left_type); - larger_type_str = zend_type_to_string(right_type); + smaller_type_str = zend_type_to_string(left_type, CG(active_class_entry)); + larger_type_str = zend_type_to_string(right_type, CG(active_class_entry)); } if (smaller_type_list->num_types == larger_type_list->num_types) { zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant with type %s", @@ -6535,9 +6685,9 @@ static void zend_is_intersection_type_redundant_by_single_type(zend_type interse zend_type *single_intersection_type = NULL; ZEND_TYPE_FOREACH(intersection_type, single_intersection_type) - if (zend_string_equals_ci(ZEND_TYPE_NAME(*single_intersection_type), ZEND_TYPE_NAME(single_type))) { - zend_string *single_type_str = zend_type_to_string(single_type); - zend_string *complete_type = zend_type_to_string(intersection_type); + if (zend_string_equals_ci(ZEND_TYPE_PNR_NAME(*single_intersection_type), ZEND_TYPE_PNR_NAME(single_type))) { + zend_string *single_type_str = zend_type_to_string(single_type, CG(active_class_entry)); + zend_string *complete_type = zend_type_to_string(intersection_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant as it is more restrictive than type %s", ZSTR_VAL(complete_type), ZSTR_VAL(single_type_str)); } @@ -6553,15 +6703,23 @@ static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list zend_is_intersection_type_redundant_by_single_type(type_list->types[i], type); continue; } - if (zend_string_equals_ci(ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(type))) { - zend_string *single_type_str = zend_type_to_string(type); - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + if (ZEND_TYPE_HAS_PNR(type)) { + if (ZEND_TYPE_HAS_PNR(type_list->types[i]) + && zend_string_equals_ci(ZEND_TYPE_PNR_NAME(type_list->types[i]), ZEND_TYPE_PNR_NAME(type))) { + zend_string *single_type_str = zend_type_to_string(type, CG(active_class_entry)); + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(type)); + if (ZEND_TYPE_HAS_GENERIC_PARAM(type_list->types[i]) + && ZEND_TYPE_GENERIC_PARAM_ID(type_list->types[i]) == ZEND_TYPE_GENERIC_PARAM_ID(type)) { + zend_string *single_type_str = zend_type_to_string(type, CG(active_class_entry)); + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } } } } -static zend_type zend_compile_typename(zend_ast *ast); - static zend_type zend_compile_typename_ex( zend_ast *ast, bool force_allow_null, bool *forced_allow_null) /* {{{ */ { @@ -6631,7 +6789,8 @@ static zend_type zend_compile_typename_ex( uint32_t type_mask_overlap = ZEND_TYPE_PURE_MASK(type) & single_type_mask; if (type_mask_overlap) { zend_type overlap_type = ZEND_TYPE_INIT_MASK(type_mask_overlap); - zend_string *overlap_type_str = zend_type_to_string(overlap_type); + zend_string *overlap_type_str = + zend_type_to_string(overlap_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str)); } @@ -6646,11 +6805,16 @@ static zend_type zend_compile_typename_ex( if (ZEND_TYPE_IS_COMPLEX(single_type)) { if (!ZEND_TYPE_IS_COMPLEX(type) && !is_composite) { - /* The first class type can be stored directly as the type ptr payload. */ - ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type)); - ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT; + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(single_type) || ZEND_TYPE_HAS_GENERIC_PARAM(single_type)); + /* The first class type or generic type parameter can be + * stored as the type payload */ + uint32_t extra_type_mask = ZEND_TYPE_PURE_MASK(type); + type = single_type; + ZEND_TYPE_FULL_MASK(type) |= extra_type_mask; } else { + ZEND_ASSERT(!ZEND_TYPE_HAS_GENERIC_PARAM(single_type) && "Not implemented"); if (type_list->num_types == 0) { + /* Switch from single name to name list. */ type_list->num_types = 1; type_list->types[0] = type; @@ -6681,7 +6845,7 @@ static zend_type zend_compile_typename_ex( uint32_t type_mask = ZEND_TYPE_FULL_MASK(type); if ((type_mask & MAY_BE_OBJECT) && ((!has_only_iterable_class && ZEND_TYPE_IS_COMPLEX(type)) || (type_mask & MAY_BE_STATIC))) { - zend_string *type_str = zend_type_to_string(type); + zend_string *type_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s contains both object and a class type, which is redundant", ZSTR_VAL(type_str)); @@ -6704,23 +6868,23 @@ static zend_type zend_compile_typename_ex( /* An intersection of union types cannot exist so invalidate it * Currently only can happen with iterable getting canonicalized to Traversable|array */ if (ZEND_TYPE_IS_ITERABLE_FALLBACK(single_type)) { - zend_string *standard_type_str = zend_type_to_string(single_type); + zend_string *standard_type_str = zend_type_to_string(single_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str)); zend_string_release_ex(standard_type_str, false); } /* An intersection of standard types cannot exist so invalidate it */ if (ZEND_TYPE_IS_ONLY_MASK(single_type)) { - zend_string *standard_type_str = zend_type_to_string(single_type); + zend_string *standard_type_str = zend_type_to_string(single_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str)); zend_string_release_ex(standard_type_str, false); } /* Check for "self" and "parent" too */ - if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self") - || zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) { + if (zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "self") + || zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "parent")) { zend_error_noreturn(E_COMPILE_ERROR, - "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type))); + "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_PNR_NAME(single_type))); } /* Add type to the type list */ @@ -6836,7 +7000,7 @@ static void zend_compile_attributes( "Cannot create Closure as attribute argument"); } - zend_string *name = zend_resolve_class_name_ast(el->child[0]); + zend_string *name = zend_resolve_class_name_ast(unwrap_non_generic_class_ref(el->child[0])); zend_string *lcname = zend_string_tolower_ex(name, false); zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL; @@ -7057,7 +7221,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 if (default_type != IS_UNDEF && default_type != IS_CONSTANT_AST && !force_nullable && !zend_is_valid_default_value(arg_info->type, &default_node.u.constant)) { - zend_string *type_str = zend_type_to_string(arg_info->type); + zend_string *type_str = zend_type_to_string(arg_info->type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as default value for parameter $%s of type %s", zend_get_type_by_const(default_type), @@ -7127,7 +7291,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 ZSTR_VAL(scope->name), ZSTR_VAL(name)); } if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_CALLABLE) { - zend_string *str = zend_type_to_string(arg_info->type); + zend_string *str = zend_type_to_string(arg_info->type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Property %s::$%s cannot have type %s", ZSTR_VAL(scope->name), ZSTR_VAL(name), ZSTR_VAL(str)); @@ -7717,7 +7881,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f type = zend_compile_typename(type_ast); if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_NEVER|MAY_BE_CALLABLE)) { - zend_string *str = zend_type_to_string(type); + zend_string *str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Property %s::$%s cannot have type %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(str)); @@ -7739,10 +7903,10 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv) && !zend_is_valid_default_value(type, &value_zv)) { - zend_string *str = zend_type_to_string(type); + zend_string *str = zend_type_to_string(type, CG(active_class_entry)); if (Z_TYPE(value_zv) == IS_NULL && !ZEND_TYPE_IS_INTERSECTION(type)) { ZEND_TYPE_FULL_MASK(type) |= MAY_BE_NULL; - zend_string *nullable_str = zend_type_to_string(type); + zend_string *nullable_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Default value for property of type %s may not be null. " @@ -7834,7 +7998,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); if (type_mask != MAY_BE_ANY && (type_mask & (MAY_BE_CALLABLE|MAY_BE_VOID|MAY_BE_NEVER))) { - zend_string *type_str = zend_type_to_string(type); + zend_string *type_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Class constant %s::%s cannot have type %s", ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); @@ -7851,7 +8015,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { - zend_string *type_str = zend_type_to_string(type); + zend_string *type_str = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s", zend_zval_type_name(&value_zv), ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); @@ -7948,7 +8112,7 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */ zend_ast *trait_ast = traits->child[i]; if (ce->ce_flags & ZEND_ACC_INTERFACE) { - zend_string *name = zend_ast_get_str(trait_ast); + zend_string *name = zend_ast_get_str(unwrap_non_generic_class_ref(trait_ast)); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use traits inside of interfaces. " "%s is used in %s", ZSTR_VAL(name), ZSTR_VAL(ce->name)); } @@ -8025,7 +8189,7 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type type = zend_compile_typename(enum_backing_type_ast); uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); if (ZEND_TYPE_IS_COMPLEX(type) || (type_mask != MAY_BE_LONG && type_mask != MAY_BE_STRING)) { - zend_string *type_string = zend_type_to_string(type); + zend_string *type_string = zend_type_to_string(type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Enum backing type must be int or string, %s given", ZSTR_VAL(type_string)); @@ -8039,15 +8203,78 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type_release(type, 0); } +static void zend_compile_generic_params(zend_ast *params_ast) +{ + zend_ast_list *list = zend_ast_get_list(params_ast); + zend_generic_param *generic_params = emalloc(list->children * sizeof(zend_generic_param)); + CG(active_class_entry)->generic_params = generic_params; + + bool have_optional = 0; + for (uint32_t i = 0; i < list->children; i++) { + zend_ast *param_ast = list->child[i]; + zend_string *name = zend_ast_get_str(param_ast->child[0]); + zend_type bound_type = ZEND_TYPE_INIT_NONE(0); + zend_type default_type = ZEND_TYPE_INIT_NONE(0); + + if (zend_string_equals(name, CG(active_class_entry)->name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic parameter %s has same name as class", ZSTR_VAL(name)); + } + + for (uint32_t j = 0; j < i; j++) { + if (zend_string_equals(name, generic_params[j].name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Duplicate generic parameter %s", ZSTR_VAL(name)); + } + } + + if (param_ast->child[1]) { + bound_type = zend_compile_typename(param_ast->child[1]); + } + if (param_ast->child[2]) { + default_type = zend_compile_typename(param_ast->child[2]); + } + + if (ZEND_TYPE_IS_SET(default_type)) { + have_optional = 1; + } else if (have_optional) { + zend_error_noreturn(E_COMPILE_ERROR, + "Required generic parameter %s follows optional", ZSTR_VAL(name)); + } + + generic_params[i].name = zend_string_copy(name); + generic_params[i].bound_type = bound_type; + generic_params[i].default_type = default_type; + // TODO: Validate potential additional constraints on the types. + // For example, can "void" be used? + + /* Update number of parameters on the fly, so that previous parameters can be + * referenced in the type bound or default of following parameters. */ + CG(active_class_entry)->num_generic_params = i + 1; + if (!have_optional) { + CG(active_class_entry)->num_required_generic_params = i + 1; + } + } +} + +zend_class_entry *zend_init_class_entry_header(void *ptr) { + zend_class_reference *ref = ptr; + zend_class_entry *ce = (zend_class_entry *) ((char *) ptr + ZEND_CLASS_ENTRY_HEADER_SIZE); + ref->ce = ce; + ref->args.num_types = 0; + return ce; +} + static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; zend_ast *extends_ast = decl->child[0]; zend_ast *implements_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; - zend_ast *enum_backing_type_ast = decl->child[4]; zend_string *name, *lcname; - zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); + void *ce_ref = zend_arena_alloc(&CG(arena), + sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); + zend_class_entry *ce = zend_init_class_entry_header(ce_ref); zend_op *opline; zend_class_entry *original_ce = CG(active_class_entry); @@ -8114,12 +8341,19 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; } - if (extends_ast) { - ce->parent_name = - zend_resolve_const_class_name_reference(extends_ast, "class name"); + CG(active_class_entry) = ce; + + if (!(ce->ce_flags & ZEND_ACC_ENUM)) { + zend_ast *generic_params_ast = decl->child[4]; + if (generic_params_ast) { + zend_compile_generic_params(generic_params_ast); + } } - CG(active_class_entry) = ce; + if (extends_ast) { + ce->parent_name = zend_compile_default_pnr(extends_ast, "class name"); + ce->num_parents = 1; + } if (decl->child[3]) { zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); @@ -8130,6 +8364,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) } if (ce->ce_flags & ZEND_ACC_ENUM) { + zend_ast *enum_backing_type_ast = decl->child[4]; if (enum_backing_type_ast != NULL) { zend_compile_enum_backing_type(ce, enum_backing_type_ast); } @@ -8158,7 +8393,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) if (toplevel) { if (extends_ast) { zend_class_entry *parent_ce = zend_lookup_class_ex( - ce->parent_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + ZEND_PNR_GET_NAME(ce->parent_name), NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (parent_ce && ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES)) @@ -8192,7 +8427,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) if (ce->parent_name) { /* Lowercased parent name */ - zend_string *lc_parent_name = zend_string_tolower(ce->parent_name); + zend_string *lc_parent_name = zend_string_tolower(ZEND_PNR_GET_NAME(ce->parent_name)); opline->op2_type = IS_CONST; LITERAL_STR(opline->op2, lc_parent_name); } @@ -9868,9 +10103,10 @@ static void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */ return; } - if (class_ast->kind == ZEND_AST_ZVAL) { + if (class_ast->kind == ZEND_AST_CLASS_REF) { zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); - opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + opline->op1.num = + zend_get_class_fetch_type(zend_ast_get_str(unwrap_non_generic_class_ref(class_ast))); } else { znode expr_node; zend_compile_expr(&expr_node, class_ast); @@ -10076,7 +10312,7 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM || kind == ZEND_AST_UNPACK || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST - || kind == ZEND_AST_CLASS_NAME + || kind == ZEND_AST_CLASS_NAME || kind == ZEND_AST_CLASS_REF || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE || kind == ZEND_AST_CONST_ENUM_INIT || kind == ZEND_AST_NEW || kind == ZEND_AST_ARG_LIST @@ -10089,18 +10325,20 @@ static void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *class_ast = ast->child[0]; + zend_ast *name_ast; zend_string *class_name; int fetch_type; - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Dynamic class names are not allowed in compile-time class constant references"); } - if (Z_TYPE_P(zend_ast_get_zval(class_ast)) != IS_STRING) { + if (Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) != IS_STRING) { zend_throw_error(NULL, "Class name must be a valid object or a string"); } - class_name = zend_ast_get_str(class_ast); + name_ast = unwrap_non_generic_class_ref(class_ast); + class_name = zend_ast_get_str(name_ast); fetch_type = zend_get_class_fetch_type(class_name); if (ZEND_FETCH_CLASS_STATIC == fetch_type) { @@ -10109,13 +10347,13 @@ static void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ } if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) { - zend_string *tmp = zend_resolve_class_name_ast(class_ast); + zend_string *tmp = zend_resolve_class_name_ast(name_ast); zend_string_release_ex(class_name, 0); if (tmp != class_name) { - zval *zv = zend_ast_get_zval(class_ast); + zval *zv = zend_ast_get_zval(name_ast); ZVAL_STR(zv, tmp); - class_ast->attr = ZEND_NAME_FQ; + name_ast->attr = ZEND_NAME_FQ; } } @@ -10127,12 +10365,12 @@ static void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *class_ast = ast->child[0]; - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { zend_error_noreturn(E_COMPILE_ERROR, "(expression)::class cannot be used in constant expressions"); } - zend_string *class_name = zend_ast_get_str(class_ast); + zend_string *class_name = zend_ast_get_str(unwrap_non_generic_class_ref(class_ast)); uint32_t fetch_type = zend_get_class_fetch_type(class_name); switch (fetch_type) { @@ -10197,22 +10435,23 @@ static void zend_compile_const_expr_new(zend_ast **ast_ptr) zend_error_noreturn(E_COMPILE_ERROR, "Cannot use anonymous class in constant expression"); } - if (class_ast->kind != ZEND_AST_ZVAL) { + if (class_ast->kind != ZEND_AST_CLASS_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use dynamic class name in constant expression"); } - zend_string *class_name = zend_resolve_class_name_ast(class_ast); + zend_ast *name_ast = unwrap_non_generic_class_ref(class_ast); + zend_string *class_name = zend_resolve_class_name_ast(name_ast); int fetch_type = zend_get_class_fetch_type(class_name); if (ZEND_FETCH_CLASS_STATIC == fetch_type) { zend_error_noreturn(E_COMPILE_ERROR, "\"static\" is not allowed in compile-time constants"); } - zval *class_ast_zv = zend_ast_get_zval(class_ast); - zval_ptr_dtor_nogc(class_ast_zv); - ZVAL_STR(class_ast_zv, class_name); - class_ast->attr = fetch_type << ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT; + zval *name_ast_zv = zend_ast_get_zval(name_ast); + zval_ptr_dtor_nogc(name_ast_zv); + ZVAL_STR(name_ast_zv, class_name); + name_ast->attr = fetch_type << ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT; } static void zend_compile_const_expr_args(zend_ast **ast_ptr) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 765e54fb56ee8..d2f9998a5ffc0 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -869,6 +869,7 @@ ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_type_release(zend_type type, bool persistent); ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name); +void zend_packed_name_reference_release(zend_packed_name_reference ref, bool persistent); ZEND_API ZEND_COLD void zend_user_exception_handler(void); @@ -908,6 +909,7 @@ ZEND_API void pass_two(zend_op_array *op_array); ZEND_API bool zend_is_compiling(void); ZEND_API char *zend_make_compiled_string_description(const char *name); ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers); +zend_class_entry *zend_init_class_entry_header(void *ptr); uint32_t zend_get_class_fetch_type(const zend_string *name); ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc); ZEND_API bool zend_is_smart_branch(const zend_op *opline); @@ -931,8 +933,8 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem); void zend_assert_valid_class_name(const zend_string *const_name); -zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope); -ZEND_API zend_string *zend_type_to_string(zend_type type); +ZEND_API zend_string *zend_type_to_string(zend_type type, const zend_class_entry *scope); +zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry *scope); /* BEGIN: OPCODES */ diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 6530c1f063dd8..f30ddb1f07522 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -320,11 +320,11 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * if (UNEXPECTED(!scope)) { zend_throw_error(NULL, "Cannot access \"parent\" when no class scope is active"); goto failure; - } else if (UNEXPECTED(!scope->parent)) { + } else if (UNEXPECTED(!scope->num_parents)) { zend_throw_error(NULL, "Cannot access \"parent\" when current class scope has no parent"); goto failure; } else { - ce = scope->parent; + ce = scope->parents[0]->ce; } } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_STATIC))) { ce = zend_get_called_scope(EG(current_execute_data)); @@ -433,11 +433,11 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, if (UNEXPECTED(!scope)) { zend_throw_error(NULL, "Cannot access \"parent\" when no class scope is active"); goto failure; - } else if (UNEXPECTED(!scope->parent)) { + } else if (UNEXPECTED(!scope->num_parents)) { zend_throw_error(NULL, "Cannot access \"parent\" when current class scope has no parent"); goto failure; } else { - ce = scope->parent; + ce = scope->parents[0]->ce; } } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_STATIC))) { ce = zend_get_called_scope(EG(current_execute_data)); diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7dcf2ad65b7f6..d015fed13674e 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -59,8 +59,8 @@ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entr /* zend_ce_exception and zend_ce_error may not be initialized yet when this is called (e.g when * implementing Throwable for Exception itself). Perform a manual inheritance check. */ zend_class_entry *root = class_type; - while (root->parent) { - root = root->parent; + if (root->num_parents) { + root = root->parents[root->num_parents-1]->ce; } if (zend_string_equals_literal(root->name, "Exception") || zend_string_equals_literal(root->name, "Error")) { diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h index c0e274d64ef70..2154ba1e658a3 100644 --- a/Zend/zend_exceptions_arginfo.h +++ b/Zend/zend_exceptions_arginfo.h @@ -246,7 +246,7 @@ static zend_class_entry *register_class_Exception(zend_class_entry *class_entry_ ZVAL_NULL(&property_previous_default_value); zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1); zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); - zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previous_class_Throwable, 0, MAY_BE_NULL)); zend_string_release(property_previous_name); return class_entry; @@ -316,7 +316,7 @@ static zend_class_entry *register_class_Error(zend_class_entry *class_entry_Thro ZVAL_NULL(&property_previous_default_value); zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1); zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); - zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previous_class_Throwable, 0, MAY_BE_NULL)); zend_string_release(property_previous_name); return class_entry; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 470d52922c39c..870b660cc58de 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -18,6 +18,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_types.h" #define ZEND_INTENSIVE_DEBUGGING 0 #include @@ -44,6 +45,7 @@ #include "zend_system_id.h" #include "zend_call_stack.h" #include "Optimizer/zend_func_info.h" +#include "zend_bitset.h" /* Virtual current working directory support */ #include "zend_virtual_cwd.h" @@ -567,9 +569,10 @@ static inline void zend_assign_to_variable_reference(zval *variable_ptr, zval *v ZVAL_REF(variable_ptr, ref); } -static zend_never_inline zval* zend_assign_to_typed_property_reference(zend_property_info *prop_info, zval *prop, zval *value_ptr, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) +static zend_never_inline zval* zend_assign_to_typed_property_reference( + zend_object *obj, zend_property_info *prop_info, zval *prop, zval *value_ptr, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) { - if (!zend_verify_prop_assignable_by_ref(prop_info, value_ptr, EX_USES_STRICT_TYPES())) { + if (!zend_verify_prop_assignable_by_ref(obj, prop_info, value_ptr, EX_USES_STRICT_TYPES())) { return &EG(uninitialized_zval); } if (Z_ISREF_P(prop)) { @@ -606,7 +609,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg } static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); zend_type_error( "Cannot auto-initialize an array inside property %s::$%s of type %s", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), @@ -616,7 +619,7 @@ static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_ } static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); zend_type_error( "Cannot auto-initialize an array inside a reference held by property %s::$%s of type %s", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), @@ -668,6 +671,40 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_erro } } +static void zend_verify_type_generic_params_explainer(smart_str *dest, zend_class_entry *scope, zend_type type) { + // TODO: It's all a hack... + zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + + if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + zend_type real_type = called_scope->bound_generic_args[generic_param_id]; + zend_string *real_type_string = zend_type_to_string(real_type, called_scope); + smart_str_append(dest, param->name); + smart_str_appends(dest, " = "); + smart_str_append(dest, real_type_string); + zend_string_release(real_type_string); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *name; + const zend_type_list *args; + const zend_type *arg; + ZEND_PNR_UNPACK(ZEND_TYPE_PNR(type), name, args); + (void) name; + ZEND_TYPE_LIST_FOREACH_CONST(args, arg) { + zend_verify_type_generic_params_explainer(dest, scope, *arg); + } ZEND_TYPE_LIST_FOREACH_CONST_END(); + } else if (ZEND_TYPE_HAS_LIST(type)) { + ZEND_ASSERT(ZEND_TYPE_IS_UNION(type) || ZEND_TYPE_IS_INTERSECTION(type)); + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + zend_verify_type_generic_params_explainer(dest, scope, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + } +} + static ZEND_COLD void zend_verify_type_error_common( const zend_function *zf, const zend_arg_info *arg_info, zval *value, const char **fname, const char **fsep, const char **fclass, @@ -681,9 +718,24 @@ static ZEND_COLD void zend_verify_type_error_common( *fsep = ""; *fclass = ""; } - *need_msg = zend_type_to_string_resolved(arg_info->type, zf->common.scope); + smart_str generic_params_explainer = {0}; + zend_verify_type_generic_params_explainer(&generic_params_explainer, + zf->common.scope, arg_info->type); + + if (smart_str_get_len(&generic_params_explainer)) { + smart_str str = {0}; + smart_str_append(&str, *need_msg); + smart_str_appends(&str, " (where "); + smart_str_append_smart_str(&str, &generic_params_explainer); + smart_str_appends(&str, ")"); + zend_string_release(*need_msg); + *need_msg = smart_str_extract(&str); + } + + smart_str_free(&generic_params_explainer); + if (value) { *given_kind = zend_zval_value_name(value); } else { @@ -825,7 +877,7 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant) { - zend_string *type_str = zend_type_to_string(c->type); + zend_string *type_str = zend_type_to_string(c->type, NULL); zend_type_error("Cannot assign %s to class constant %s::%s of type %s", zend_zval_type_name(constant), ZSTR_VAL(c->ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str)); @@ -842,7 +894,7 @@ ZEND_COLD zend_never_inline void zend_verify_property_type_error(const zend_prop return; } - type_str = zend_type_to_string(info->type); + type_str = zend_type_to_string(info->type, info->ce); zend_type_error("Cannot assign %s to property %s::$%s of type %s", zend_zval_value_name(property), ZSTR_VAL(info->ce->name), @@ -858,7 +910,7 @@ ZEND_COLD zend_never_inline void zend_magic_get_property_type_inconsistency_erro return; } - zend_string *type_str = zend_type_to_string(info->type); + zend_string *type_str = zend_type_to_string(info->type, info->ce); zend_type_error("Value of type %s returned from %s::__get() must be compatible with unset property %s::$%s of type %s", zend_zval_type_name(property), ZSTR_VAL(info->ce->name), @@ -914,7 +966,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons if (zend_string_equals_literal_ci(name, "self")) { return self_ce; } else if (zend_string_equals_literal_ci(name, "parent")) { - return self_ce->parent; + return self_ce->num_parents ? self_ce->parents[0]->ce : NULL; } else { return zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); } @@ -922,8 +974,8 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons static zend_always_inline const zend_class_entry *zend_ce_from_type( const zend_class_entry *scope, const zend_type *type) { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type)); - zend_string *name = ZEND_TYPE_NAME(*type); + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*type)); + zend_string *name = ZEND_TYPE_PNR_NAME(*type); if (ZSTR_HAS_CE_CACHE(name)) { zend_class_entry *ce = ZSTR_GET_CE_CACHE(name); if (!ce) { @@ -941,8 +993,18 @@ static bool zend_check_intersection_for_property_or_class_constant_class_type( ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(scope, list_type); - if (!ce || !instanceof_function(value_ce, ce)) { + const zend_class_entry *ce; + if (ZEND_TYPE_HAS_PNR(*list_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(*list_type); + ce = resolve_single_class_type(name, scope); + if (!ce) { + return false; + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_CLASS_REF(*list_type)); + ce = ZEND_TYPE_CLASS_REF(*list_type)->ce; + } + if (!instanceof_function(value_ce, ce)) { return false; } } ZEND_TYPE_LIST_FOREACH_END(); @@ -966,8 +1028,17 @@ static bool zend_check_and_resolve_property_or_class_constant_class_type( continue; } ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - const zend_class_entry *ce = zend_ce_from_type(scope, list_type); - if (ce && instanceof_function(value_ce, ce)) { + const zend_class_entry *ce; + if (ZEND_TYPE_HAS_PNR(*list_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(*list_type); + ce = resolve_single_class_type(name, scope); + if (!ce) { + continue; + } + } else { + ce = ZEND_TYPE_CLASS_REF(*list_type)->ce; + } + if (instanceof_function(value_ce, ce)) { return true; } } ZEND_TYPE_LIST_FOREACH_END(); @@ -980,15 +1051,41 @@ static bool zend_check_and_resolve_property_or_class_constant_class_type( } } else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC) && value_ce == scope) { return true; - } else if (ZEND_TYPE_HAS_NAME(member_type)) { - const zend_class_entry *ce = zend_ce_from_type(scope, &member_type); - return ce && instanceof_function(value_ce, ce); + } else if (UNEXPECTED(ZEND_TYPE_HAS_PNR(member_type) || ZEND_TYPE_HAS_CLASS_REF(member_type))) { + const zend_class_entry *ce; + if (ZEND_TYPE_HAS_PNR(member_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(member_type); + ce = resolve_single_class_type(name, scope); + if (UNEXPECTED(!ce)) { + return false; + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_CLASS_REF(member_type)); + ce = ZEND_TYPE_CLASS_REF(member_type)->ce; + } + return instanceof_function(value_ce, ce); } return false; } -static zend_always_inline bool i_zend_check_property_type(const zend_property_info *info, zval *property, bool strict) +#if 0 +static zend_never_inline bool check_property_type_generic( + zend_type real_type, zval *property, bool strict) { + /*if (ZEND_TYPE_HAS_CLASS(info->type) && Z_TYPE_P(property) == IS_OBJECT + && zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(property))) { + return 1; + }*/ + + ZEND_ASSERT(!(ZEND_TYPE_FULL_MASK(real_type) & MAY_BE_CALLABLE)); + if ((ZEND_TYPE_FULL_MASK(real_type) & MAY_BE_ITERABLE) && zend_is_iterable(property)) { + return 1; + } + return zend_verify_scalar_type_hint(ZEND_TYPE_FULL_MASK(real_type), property, strict, 0); +} +#endif + +static zend_always_inline bool i_zend_check_property_type(const zend_object *obj, const zend_property_info *info, zval *property, bool strict) { ZEND_ASSERT(!Z_ISREF_P(property)); if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) { @@ -1000,14 +1097,25 @@ static zend_always_inline bool i_zend_check_property_type(const zend_property_in return 1; } + if (ZEND_TYPE_HAS_GENERIC_PARAM(info->type)) { + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(info->type); + ZEND_ASSERT(param_id < obj->ce->num_bound_generic_args); + zend_type real_type = obj->ce->bound_generic_args[param_id]; + if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(property))) { + return 1; + } + /*return zend_check_type_slow( + real_type, arg, ref, cache_slot, scope, is_return_type, is_internal);*/ + } + uint32_t type_mask = ZEND_TYPE_FULL_MASK(info->type); ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_STATIC|MAY_BE_NEVER|MAY_BE_VOID))); return zend_verify_scalar_type_hint(type_mask, property, strict, 0); } -static zend_always_inline bool i_zend_verify_property_type(const zend_property_info *info, zval *property, bool strict) +static zend_always_inline bool i_zend_verify_property_type(const zend_object *obj, const zend_property_info *info, zval *property, bool strict) { - if (i_zend_check_property_type(info, property, strict)) { + if (i_zend_check_property_type(obj, info, property, strict)) { return 1; } @@ -1015,11 +1123,13 @@ static zend_always_inline bool i_zend_verify_property_type(const zend_property_i return 0; } -ZEND_API bool zend_never_inline zend_verify_property_type(const zend_property_info *info, zval *property, bool strict) { - return i_zend_verify_property_type(info, property, strict); +ZEND_API bool zend_never_inline zend_verify_property_type( + zend_object *obj, const zend_property_info *info, zval *property, bool strict) { + return i_zend_verify_property_type(obj, info, property, strict); } -static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) +static zend_never_inline zval *zend_assign_to_typed_prop( + zend_object *obj, zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) { zval tmp; @@ -1031,7 +1141,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf ZVAL_DEREF(value); ZVAL_COPY(&tmp, value); - if (UNEXPECTED(!i_zend_verify_property_type(info, &tmp, EX_USES_STRICT_TYPES()))) { + if (UNEXPECTED(!i_zend_verify_property_type(obj, info, &tmp, EX_USES_STRICT_TYPES()))) { zval_ptr_dtor(&tmp); return &EG(uninitialized_zval); } @@ -1053,6 +1163,37 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) { return instanceof_function(Z_OBJCE_P(zv), called_scope); } +static ZEND_COLD void zend_validate_generic_args_error( + zend_class_entry *ce, const zend_type_list *args) { + if (args->num_types > ce->num_generic_params) { + zend_throw_error(NULL, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at most", + ce->num_generic_params, ce->num_generic_params == 1 ? "" : "s", + args->num_types); + } else { + ZEND_ASSERT(args->num_types < ce->num_required_generic_params); + zend_throw_error(NULL, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at least", + ce->num_required_generic_params, ce->num_required_generic_params == 1 ? "" : "s", + args->num_types); + } +} + +static zend_always_inline bool zend_validate_generic_args( + zend_class_entry *ce, const zend_type_list *args) { + if (EXPECTED(args->num_types <= ce->num_generic_params + && args->num_types >= ce->num_required_generic_params)) { + return 1; + } + + zend_validate_generic_args_error(ce, args); + return 0; +} + /* The cache_slot may only be NULL in debug builds, where arginfo verification of * internal functions is enabled. Avoid unnecessary checks in release builds. */ #if ZEND_DEBUG @@ -1070,7 +1211,7 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( return (zend_class_entry *) *cache_slot; } - zend_string *name = ZEND_TYPE_NAME(*type); + zend_string *name = ZEND_TYPE_PNR_NAME(*type); zend_class_entry *ce; if (ZSTR_HAS_CE_CACHE(name)) { ce = ZSTR_GET_CE_CACHE(name); @@ -1094,65 +1235,128 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( return ce; } +static zend_always_inline bool zend_check_type_slow( + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type, bool is_internal); + static bool zend_check_intersection_type_from_cache_slot(zend_type_list *intersection_type_list, - zend_class_entry *arg_ce, void ***cache_slot_ptr) + zval *arg, zend_reference *ref, void ***cache_slot_ptr, + zend_class_entry *scope, bool is_return_type, bool is_internal) { void **cache_slot = *cache_slot_ptr; zend_class_entry *ce; zend_type *list_type; - bool status = true; + bool status = Z_TYPE_P(arg) == IS_OBJECT; + ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { /* Only check classes if the type might be valid */ if (status) { - ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); - /* If type is not an instance of one of the types taking part in the - * intersection it cannot be a valid instance of the whole intersection type. */ - if (!ce || !instanceof_function(arg_ce, ce)) { - status = false; + if (ZEND_TYPE_HAS_PNR(*list_type)) { + if (Z_TYPE_P(arg) == IS_OBJECT) { + ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); + /* If type is not an instance of one of the types taking part in the + * intersection it cannot be a valid instance of the whole intersection type. */ + if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { + status = false; + } + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); + // TODO: Deduplicate this code. + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); + zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + ZEND_ASSERT(param_id < called_scope->num_bound_generic_args); + zend_type real_type = called_scope->bound_generic_args[param_id]; + if (!zend_check_type_slow( + &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { + status = false; + } } } - PROGRESS_CACHE_SLOT(); + if (ZEND_TYPE_HAS_PNR(*list_type)) { + PROGRESS_CACHE_SLOT(); + } } ZEND_TYPE_LIST_FOREACH_END(); + if (HAVE_CACHE_SLOT) { *cache_slot_ptr = cache_slot; } + return status; } static zend_always_inline bool zend_check_type_slow( zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, - bool is_return_type, bool is_internal) + zend_class_entry *scope, bool is_return_type, bool is_internal) { uint32_t type_mask; - if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { + // TODO: Figure out how to deal with cache_slot with generic types + // and try to get rid of these cache_slot checks. We probably need + // a separate code-path for this which makes use of a polymorphic cache. + if (ZEND_TYPE_IS_COMPLEX(*type)) { zend_class_entry *ce; if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) { zend_type *list_type; if (ZEND_TYPE_IS_INTERSECTION(*type)) { - return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot); + return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), arg, ref, &cache_slot, scope, is_return_type, is_internal); } else { ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { - if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) { + if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), arg, ref, &cache_slot, scope, is_return_type, is_internal)) { return true; } /* The cache_slot is progressed in zend_check_intersection_type_from_cache_slot() */ } else { - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); - ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); - /* Instance of a single type part of a union is sufficient to pass the type check */ - if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { - return true; + if (ZEND_TYPE_HAS_PNR(*list_type)) { + if (Z_TYPE_P(arg) == IS_OBJECT) { + ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); + /* Instance of a single type part of a union is sufficient to pass the type check */ + if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + return true; + } + } + PROGRESS_CACHE_SLOT(); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); + // TODO: Deduplicate this code. + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); + zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + ZEND_ASSERT(param_id < called_scope->num_bound_generic_args); + zend_type real_type = called_scope->bound_generic_args[param_id]; + if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { + return true; + } + if (zend_check_type_slow( + &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { + return true; + } } - PROGRESS_CACHE_SLOT(); } } ZEND_TYPE_LIST_FOREACH_END(); } - } else { + } else if (ZEND_TYPE_HAS_PNR(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { ce = zend_fetch_ce_from_cache_slot(cache_slot, type); + zend_string *name; + const zend_type_list *args; + ZEND_PNR_UNPACK(ZEND_TYPE_PNR(*type), name, args); + (void) name; /* If we have a CE we check if it satisfies the type constraint, * otherwise it will check if a standard type satisfies it. */ - if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + if (ce && zend_validate_generic_args(ce, args) + && instanceof_unpacked(ZEND_CE_TO_REF(Z_OBJCE_P(arg)), ce, args)) { + return true; + } + } else if (UNEXPECTED(ZEND_TYPE_HAS_GENERIC_PARAM(*type))) { + // TODO: This doesn't handle free generic parameters. + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(*type); + zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + ZEND_ASSERT(param_id < called_scope->num_bound_generic_args); + zend_type *real_type = &called_scope->bound_generic_args[param_id]; + if (ZEND_TYPE_CONTAINS_CODE(*real_type, Z_TYPE_P(arg))) { + return true; + } + if (zend_check_type_slow( + real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { return true; } } @@ -1201,14 +1405,15 @@ static zend_always_inline bool zend_check_type( return 1; } - return zend_check_type_slow(type, arg, ref, cache_slot, is_return_type, is_internal); + return zend_check_type_slow(type, arg, ref, cache_slot, scope, is_return_type, is_internal); } ZEND_API bool zend_check_user_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type) + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type) { return zend_check_type_slow( - type, arg, ref, cache_slot, is_return_type, /* is_internal */ false); + type, arg, ref, cache_slot, scope, is_return_type, /* is_internal */ false); } static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot) @@ -1615,7 +1820,9 @@ static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *re } } -static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_info *prop_info, zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline void zend_binary_assign_op_typed_prop( + zend_object *obj, zend_property_info *prop_info, + zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC) { zval z_copy; @@ -1627,7 +1834,7 @@ static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_inf } zend_binary_op(&z_copy, zptr, value OPLINE_CC); - if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) { + if (EXPECTED(zend_verify_property_type(obj, prop_info, &z_copy, EX_USES_STRICT_TYPES()))) { zval_ptr_dtor(zptr); ZVAL_COPY_VALUE(zptr, &z_copy); } else { @@ -1890,7 +2097,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re static ZEND_COLD zend_long zend_throw_incdec_ref_error( zend_reference *ref, zend_property_info *error_prop OPLINE_DC) { - zend_string *type_str = zend_type_to_string(error_prop->type); + zend_string *type_str = zend_type_to_string(error_prop->type, error_prop->ce); if (ZEND_IS_INCREMENT(opline->opcode)) { zend_type_error( "Cannot increment a reference held by property %s::$%s of type %s past its maximal value", @@ -1911,7 +2118,7 @@ static ZEND_COLD zend_long zend_throw_incdec_ref_error( } static ZEND_COLD zend_long zend_throw_incdec_prop_error(zend_property_info *prop OPLINE_DC) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); if (ZEND_IS_INCREMENT(opline->opcode)) { zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value", ZSTR_VAL(prop->ce->name), @@ -1961,7 +2168,7 @@ static void zend_incdec_typed_ref(zend_reference *ref, zval *copy OPLINE_DC EXEC } } -static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, zval *copy OPLINE_DC EXECUTE_DATA_DC) +static void zend_incdec_typed_prop(zend_object *obj, zend_property_info *prop_info, zval *var_ptr, zval *copy OPLINE_DC EXECUTE_DATA_DC) { zval tmp; @@ -1982,7 +2189,7 @@ static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, zend_long val = zend_throw_incdec_prop_error(prop_info OPLINE_CC); ZVAL_LONG(var_ptr, val); } - } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) { + } else if (UNEXPECTED(!zend_verify_property_type(obj, prop_info, var_ptr, EX_USES_STRICT_TYPES()))) { zval_ptr_dtor(var_ptr); ZVAL_COPY_VALUE(var_ptr, copy); ZVAL_UNDEF(copy); @@ -1991,7 +2198,7 @@ static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, } } -static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) +static void zend_pre_incdec_property_zval(zend_object *obj, zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) { if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) { if (ZEND_IS_INCREMENT(opline->opcode)) { @@ -2016,7 +2223,7 @@ static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_i } if (UNEXPECTED(prop_info)) { - zend_incdec_typed_prop(prop_info, prop, NULL OPLINE_CC EXECUTE_DATA_CC); + zend_incdec_typed_prop(obj, prop_info, prop, NULL OPLINE_CC EXECUTE_DATA_CC); } else if (ZEND_IS_INCREMENT(opline->opcode)) { increment_function(prop); } else { @@ -2029,7 +2236,8 @@ static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_i } } -static void zend_post_incdec_property_zval(zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) +static void zend_post_incdec_property_zval( + zend_object *obj, zval *prop, zend_property_info *prop_info OPLINE_DC EXECUTE_DATA_DC) { if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(prop)); @@ -2054,7 +2262,7 @@ static void zend_post_incdec_property_zval(zval *prop, zend_property_info *prop_ } if (UNEXPECTED(prop_info)) { - zend_incdec_typed_prop(prop_info, prop, EX_VAR(opline->result.var) OPLINE_CC EXECUTE_DATA_CC); + zend_incdec_typed_prop(obj, prop_info, prop, EX_VAR(opline->result.var) OPLINE_CC EXECUTE_DATA_CC); } else { ZVAL_COPY(EX_VAR(opline->result.var), prop); if (ZEND_IS_INCREMENT(opline->opcode)) { @@ -3310,7 +3518,7 @@ static zend_always_inline void zend_assign_to_property_reference(zval *container } if (UNEXPECTED(prop_info)) { - variable_ptr = zend_assign_to_typed_property_reference(prop_info, variable_ptr, value_ptr, &garbage EXECUTE_DATA_CC); + variable_ptr = zend_assign_to_typed_property_reference(Z_OBJ_P(container), prop_info, variable_ptr, value_ptr, &garbage EXECUTE_DATA_CC); } else { zend_assign_to_variable_reference(variable_ptr, value_ptr, &garbage); } @@ -3468,8 +3676,8 @@ static zend_always_inline zend_result zend_fetch_static_property_address(zval ** } ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv) { - zend_string *type1_str = zend_type_to_string(prop1->type); - zend_string *type2_str = zend_type_to_string(prop2->type); + zend_string *type1_str = zend_type_to_string(prop1->type, prop1->ce); + zend_string *type2_str = zend_type_to_string(prop2->type, prop2->ce); zend_type_error("Reference with value of type %s held by property %s::$%s of type %s is not compatible with property %s::$%s of type %s", zend_zval_type_name(zv), ZSTR_VAL(prop1->ce->name), @@ -3484,7 +3692,7 @@ ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info } ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(const zend_property_info *prop, const zval *zv) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s", zend_zval_value_name(zv), ZSTR_VAL(prop->ce->name), @@ -3495,8 +3703,8 @@ ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(const zend_property_info } ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv) { - zend_string *type1_str = zend_type_to_string(prop1->type); - zend_string *type2_str = zend_type_to_string(prop2->type); + zend_string *type1_str = zend_type_to_string(prop1->type, prop1->ce); + zend_string *type2_str = zend_type_to_string(prop2->type, prop2->ce); zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s and property %s::$%s of type %s, as this would result in an inconsistent type conversion", zend_zval_value_name(zv), ZSTR_VAL(prop1->ce->name), @@ -3672,7 +3880,8 @@ ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, ui return result; } -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context) { +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex( + const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context) { zval *val = orig_val; if (Z_ISREF_P(val) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(val))) { int result; @@ -3698,7 +3907,7 @@ ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_pro } } else { ZVAL_DEREF(val); - if (i_zend_check_property_type(prop_info, val, strict)) { + if (i_zend_check_property_type(obj, prop_info, val, strict)) { return 1; } } @@ -3713,8 +3922,8 @@ ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_pro return 0; } -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(const zend_property_info *prop_info, zval *orig_val, bool strict) { - return zend_verify_prop_assignable_by_ref_ex(prop_info, orig_val, strict, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_ASSIGNMENT); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict) { + return zend_verify_prop_assignable_by_ref_ex(obj, prop_info, orig_val, strict, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_ASSIGNMENT); } ZEND_API void ZEND_FASTCALL zend_ref_add_type_source(zend_property_info_source_list *source_list, zend_property_info *prop) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index d0c18040ab46d..6cf39aac01a72 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -72,8 +72,9 @@ typedef enum { ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_ASSIGNMENT, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET, } zend_verify_prop_assignable_by_ref_context; -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context); -ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(const zend_property_info *prop_info, zval *orig_val, bool strict); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref_ex(const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict, zend_verify_prop_assignable_by_ref_context context); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref( + const zend_object *obj, const zend_property_info *prop_info, zval *orig_val, bool strict); ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(const zend_property_info *prop, const zval *zv); ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv); @@ -99,7 +100,8 @@ ZEND_API ZEND_COLD void zend_verify_never_error( const zend_function *zf); ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type); + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call); @@ -486,11 +488,10 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal #define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS) #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS) - ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant); ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant); -ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); +ZEND_API bool zend_verify_property_type(zend_object *obj, const zend_property_info *info, zval *property, bool strict); ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property); ZEND_COLD void zend_magic_get_property_type_inconsistency_error(const zend_property_info *info, const zval *property); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index feeff659bd5f4..ec229b492e684 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1660,10 +1660,11 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type) zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active"); return NULL; } - if (UNEXPECTED(!scope->parent)) { + if (UNEXPECTED(!scope->num_parents)) { zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when current class scope has no parent"); + return NULL; } - return scope->parent; + return scope->parents[0]->ce; case ZEND_FETCH_CLASS_STATIC: ce = zend_get_called_scope(EG(current_execute_data)); if (UNEXPECTED(!ce)) { @@ -1704,10 +1705,11 @@ zend_class_entry *zend_fetch_class_with_scope( zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active"); return NULL; } - if (UNEXPECTED(!scope->parent)) { + if (UNEXPECTED(!scope->num_parents)) { zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when current class scope has no parent"); + return NULL; } - return scope->parent; + return scope->parents[0]->ce; case 0: break; /* Other fetch types are not supported by this function. */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 0785c7bcafa0d..a294ddbe0fa27 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -87,8 +87,9 @@ static void zend_type_list_copy_ctor( static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list_copy_ctor(type, use_arena, persistent); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string_addref(ZEND_TYPE_NAME(*type)); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + // TODO: Duplicate name reference... + zend_string_addref(ZEND_TYPE_PNR_NAME(*type)); } } @@ -129,7 +130,7 @@ static zend_always_inline zend_function *zend_duplicate_function(zend_function * static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ { - zend_class_entry *parent = ce->parent; + zend_class_entry *parent = ce->parents[0]->ce; ZEND_ASSERT(parent != NULL); @@ -211,11 +212,11 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */ static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) { ZEND_ASSERT(scope); - if (zend_string_equals_literal_ci(name, "parent") && scope->parent) { + if (zend_string_equals_literal_ci(name, "parent") && scope->num_parents) { if (scope->ce_flags & ZEND_ACC_RESOLVED_PARENT) { - return scope->parent->name; + return scope->parents[0]->ce->name; } else { - return scope->parent_name; + return ZEND_PNR_GET_NAME(scope->parent_name); } } else if (zend_string_equals_literal_ci(name, "self")) { return scope->name; @@ -303,12 +304,12 @@ static bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { return instanceof_function(ce1, ce2); } - if (ce1->parent) { + if (ce1->num_parents) { zend_class_entry *parent_ce; if (ce1->ce_flags & ZEND_ACC_RESOLVED_PARENT) { - parent_ce = ce1->parent; + parent_ce = ce1->parents[0]->ce; } else { - parent_ce = zend_lookup_class_ex(ce1->parent_name, NULL, + parent_ce = zend_lookup_class_ex(ZEND_PNR_GET_NAME(ce1->parent_name), NULL, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD); } @@ -356,8 +357,8 @@ static bool zend_type_permits_self( * for this case. */ zend_type *single_type; ZEND_TYPE_FOREACH(type, single_type) { - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *name = resolve_class_name(scope, ZEND_TYPE_NAME(*single_type)); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *name = resolve_class_name(scope, ZEND_TYPE_PNR_NAME(*single_type)); zend_class_entry *ce = lookup_class(self, name); if (ce && unlinked_instanceof(self, ce)) { return 1; @@ -367,6 +368,31 @@ static bool zend_type_permits_self( return 0; } +/* Resolve generic type parameters that have been determined through inheritance. */ +static void zend_type_resolve_generic_params(zend_type *type, zend_class_entry *ce) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(*type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*type); + if (generic_param_id < ce->num_bound_generic_args) { + uint32_t orig_type_mask = ZEND_TYPE_PURE_MASK(*type); + *type = ce->bound_generic_args[generic_param_id]; + ZEND_TYPE_FULL_MASK(*type) |= orig_type_mask; + zend_type_copy_ctor(type, /* use_arena */ true, /* persistent */ false); + } + } else if (ZEND_TYPE_HAS_LIST(*type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); + if (generic_param_id < ce->num_bound_generic_args) { + ZEND_ASSERT(0); + //*type = ce->parent_generic_args[generic_param_id]; + //zend_type_copy_ctor(type, /* use_arena */ true, /* persistent */ false); + } + } + } ZEND_TYPE_LIST_FOREACH_END(); + } +} + static void track_class_dependency(zend_class_entry *ce, zend_string *class_name) { HashTable *ht; @@ -424,9 +450,9 @@ static inheritance_status zend_is_intersection_subtype_of_class( ZEND_TYPE_FOREACH(fe_type, single_type) { zend_class_entry *fe_ce; zend_string *fe_class_name = NULL; - if (ZEND_TYPE_HAS_NAME(*single_type)) { + if (ZEND_TYPE_HAS_PNR(*single_type)) { fe_class_name = - resolve_class_name(fe_scope, ZEND_TYPE_NAME(*single_type)); + resolve_class_name(fe_scope, ZEND_TYPE_PNR_NAME(*single_type)); if (zend_string_equals_ci(fe_class_name, proto_class_name)) { return INHERITANCE_SUCCESS; } @@ -505,9 +531,9 @@ static inheritance_status zend_is_class_subtype_of_type( zend_class_entry *proto_ce; zend_string *proto_class_name = NULL; - if (ZEND_TYPE_HAS_NAME(*single_type)) { + if (ZEND_TYPE_HAS_PNR(*single_type)) { proto_class_name = - resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type)); + resolve_class_name(proto_scope, ZEND_TYPE_PNR_NAME(*single_type)); if (zend_string_equals_ci(fe_class_name, proto_class_name)) { if (!is_intersection) { return INHERITANCE_SUCCESS; @@ -547,8 +573,8 @@ static inheritance_status zend_is_class_subtype_of_type( } static zend_string *get_class_from_type(zend_class_entry *scope, zend_type single_type) { - if (ZEND_TYPE_HAS_NAME(single_type)) { - return resolve_class_name(scope, ZEND_TYPE_NAME(single_type)); + if (ZEND_TYPE_HAS_PNR(single_type)) { + return resolve_class_name(scope, ZEND_TYPE_PNR_NAME(single_type)); } return NULL; } @@ -560,8 +586,8 @@ static void register_unresolved_classes(zend_class_entry *scope, zend_type type) register_unresolved_classes(scope, *single_type); continue; } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *class_name = resolve_class_name(scope, ZEND_TYPE_NAME(*single_type)); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *class_name = resolve_class_name(scope, ZEND_TYPE_PNR_NAME(*single_type)); lookup_class_ex(scope, class_name, /* register_unresolved */ true); } } ZEND_TYPE_FOREACH_END(); @@ -635,9 +661,12 @@ static inheritance_status zend_is_intersection_subtype_of_type( static inheritance_status zend_perform_covariant_type_check( zend_class_entry *fe_scope, zend_type fe_type, - zend_class_entry *proto_scope, zend_type proto_type) + zend_class_entry *proto_scope, zend_type proto_type, + zend_class_entry *generic_scope) /* {{{ */ { ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type)); + zend_type_resolve_generic_params(&fe_type, generic_scope); + zend_type_resolve_generic_params(&proto_type, generic_scope); /* Apart from void, everything is trivially covariant to the mixed type. * Handle this case separately to ensure it never requires class loading. */ @@ -684,7 +713,15 @@ static inheritance_status zend_perform_covariant_type_check( if (status == INHERITANCE_UNRESOLVED) { have_unresolved = true; } + } else if (UNEXPECTED(ZEND_TYPE_HAS_GENERIC_PARAM(fe_type))) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(proto_type)) { + if (ZEND_TYPE_GENERIC_PARAM_ID(fe_type) == ZEND_TYPE_GENERIC_PARAM_ID(proto_type)) { + return INHERITANCE_SUCCESS; + } + } + return INHERITANCE_ERROR; } else { + ZEND_ASSERT(ZEND_TYPE_HAS_LIST(fe_type) || ZEND_TYPE_HAS_PNR(fe_type) || !ZEND_TYPE_IS_COMPLEX(fe_type)); /* U_1|...|U_n < V_1|...|V_m if forall U_i. exists V_j. U_i < V_j. * U_1|...|U_n < V_1&...&V_m if forall U_i. forall V_j. U_i < V_j. * We need to iterate over fe_type (U_i) first and the logic is independent of @@ -741,7 +778,9 @@ static inheritance_status zend_do_perform_arg_type_hint_check( /* Contravariant type check is performed as a covariant type check with swapped * argument order. */ return zend_perform_covariant_type_check( - proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type); + proto_scope, proto_arg_info->type, + fe_scope, fe_arg_info->type, + fe_scope); } /* }}} */ @@ -843,7 +882,9 @@ static inheritance_status zend_do_perform_implementation_check( } local_status = zend_perform_covariant_type_check( - fe_scope, fe->common.arg_info[-1].type, proto_scope, proto->common.arg_info[-1].type); + fe_scope, fe->common.arg_info[-1].type, + proto_scope, proto->common.arg_info[-1].type, + fe->common.scope); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { if (local_status == INHERITANCE_ERROR @@ -974,7 +1015,9 @@ static ZEND_COLD zend_string *zend_get_function_declaration( if (ast->kind == ZEND_AST_CONSTANT) { smart_str_append(&str, zend_ast_get_constant_name(ast)); } else if (ast->kind == ZEND_AST_CLASS_CONST) { - smart_str_append(&str, zend_ast_get_str(ast->child[0])); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->child[1] == NULL && "Generic params not supported yet"); + smart_str_append(&str, zend_ast_get_str(class_ast->child[0])); smart_str_appends(&str, "::"); smart_str_append(&str, zend_ast_get_str(ast->child[1])); } else { @@ -1246,7 +1289,7 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function inheritance_status property_types_compatible( const zend_property_info *parent_info, const zend_property_info *child_info) { if (ZEND_TYPE_PURE_MASK(parent_info->type) == ZEND_TYPE_PURE_MASK(child_info->type) - && ZEND_TYPE_NAME(parent_info->type) == ZEND_TYPE_NAME(child_info->type)) { + && parent_info->type.ptr == child_info->type.ptr) { return INHERITANCE_SUCCESS; } @@ -1256,9 +1299,9 @@ inheritance_status property_types_compatible( /* Perform a covariant type check in both directions to determined invariance. */ inheritance_status status1 = zend_perform_covariant_type_check( - child_info->ce, child_info->type, parent_info->ce, parent_info->type); + child_info->ce, child_info->type, parent_info->ce, parent_info->type, NULL); inheritance_status status2 = zend_perform_covariant_type_check( - parent_info->ce, parent_info->type, child_info->ce, child_info->type); + parent_info->ce, parent_info->type, child_info->ce, child_info->type, NULL); if (status1 == INHERITANCE_SUCCESS && status2 == INHERITANCE_SUCCESS) { return INHERITANCE_SUCCESS; } @@ -1405,7 +1448,7 @@ static inheritance_status class_constant_types_compatible(const zend_class_const return INHERITANCE_ERROR; } - return zend_perform_covariant_type_check(child->ce, child->type, parent->ce, parent->type); + return zend_perform_covariant_type_check(child->ce, child->type, parent->ce, parent->type, NULL); } static bool do_inherit_constant_check( @@ -1424,7 +1467,7 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; - if (ce->parent->ce_flags & ZEND_ACC_IMMUTABLE) { + if (ce->parents[0]->ce->ce_flags & ZEND_ACC_IMMUTABLE) { c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); memcpy(c, parent_const, sizeof(zend_class_constant)); parent_const = c; @@ -1460,15 +1503,16 @@ void zend_build_properties_info_table(zend_class_entry *ce) /* Dead slots may be left behind during inheritance. Make sure these are NULLed out. */ memset(table, 0, size); - if (ce->parent && ce->parent->default_properties_count != 0) { - zend_property_info **parent_table = ce->parent->properties_info_table; + if (ce->num_parents && ce->parents[0]->ce->default_properties_count != 0) { + zend_class_entry *parent_ce = ce->parents[0]->ce; + zend_property_info **parent_table = parent_ce->properties_info_table; memcpy( table, parent_table, - sizeof(zend_property_info *) * ce->parent->default_properties_count + sizeof(zend_property_info *) * parent_ce->default_properties_count ); /* Child did not add any new properties, we are done */ - if (ce->default_properties_count == ce->parent->default_properties_count) { + if (ce->default_properties_count == parent_ce->default_properties_count) { return; } } @@ -1480,6 +1524,147 @@ void zend_build_properties_info_table(zend_class_entry *ce) } ZEND_HASH_FOREACH_END(); } +static void zend_type_fixup(zend_type *type, uint32_t generic_offset) { + zend_type *single_type; + ZEND_TYPE_FOREACH(*type, single_type) { + if (ZEND_TYPE_HAS_GENERIC_PARAM(*single_type)) { + ZEND_TYPE_SET_GENERIC_PARAM_ID(*single_type, + ZEND_TYPE_GENERIC_PARAM_ID(*single_type) + generic_offset); + } + } ZEND_TYPE_FOREACH_END(); +} + +static void zend_bind_parent_generic_args( + zend_class_entry *ce, zend_class_entry *parent_ce, const zend_type_list *parent_args) { + uint32_t num_required_params = parent_ce->num_required_generic_params; + if (parent_args->num_types > parent_ce->num_generic_params) { + zend_error(E_COMPILE_ERROR, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(parent_ce->name), + num_required_params == parent_ce->num_generic_params ? "exactly" : "at most", + parent_ce->num_generic_params, parent_ce->num_generic_params == 1 ? "" : "s", + parent_args->num_types); + } else if (parent_args->num_types < num_required_params) { + zend_error(E_COMPILE_ERROR, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(parent_ce->name), + num_required_params == parent_ce->num_generic_params ? "exactly" : "at least", + num_required_params, num_required_params == 1 ? "" : "s", + parent_args->num_types); + } + + // TODO: Validate type bounds. + + uint32_t num_inherited_generic_args = + parent_ce->num_generic_params + parent_ce->num_bound_generic_args; + zend_type *inherited_generic_args = emalloc(num_inherited_generic_args * sizeof(zend_type)); + for (uint32_t i = 0; i < parent_ce->num_bound_generic_args; i++) { + /* Inherit generic args for all parent classes. */ + inherited_generic_args[i] = parent_ce->bound_generic_args[i]; + zend_type_copy_ctor(&inherited_generic_args[i], /* use_arena */ true, /* persistent */ false); + } + + uint32_t offset = parent_ce->num_bound_generic_args; + for (uint32_t i = 0; i < parent_ce->num_generic_params; i++) { + /* Substitute generic args to our direct parent (or use defaults). */ + if (i < parent_args->num_types) { + inherited_generic_args[i + offset] = parent_args->types[i]; + zend_type_fixup(&inherited_generic_args[i + offset], num_inherited_generic_args); + } else { + inherited_generic_args[i + offset] = parent_ce->generic_params[i].default_type; + zend_type_copy_ctor(&inherited_generic_args[i + offset], /* use_arena */ true, /* persistent */ false); + } + } + + ce->num_bound_generic_args = num_inherited_generic_args; + ce->bound_generic_args = inherited_generic_args; + + for (uint32_t i = 0; i < ce->num_bound_generic_args; i++) { + zend_type_resolve_generic_params(&ce->bound_generic_args[i], ce); + } + + /* Fixup all generic parameter references (outside of opcodes) */ + zend_function *func; + ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { + zend_arg_info *arg_info = func->common.arg_info; + uint32_t num_args = func->common.num_args; + if (!(func->common.fn_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_HAS_RETURN_TYPE))) { + continue; + } + if (func->common.fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + arg_info--; + num_args++; + } + + for (uint32_t i = 0; i < num_args; i++) { + zend_type_fixup(&arg_info[i].type, num_inherited_generic_args); + } + } ZEND_HASH_FOREACH_END(); + + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + zend_type_fixup(&prop->type, num_inherited_generic_args); + } ZEND_HASH_FOREACH_END(); + } +} + +static void update_parents(zend_class_entry *ce, zend_class_entry *parent_ce) { + /* We copy the bound generic arguments into the parent references. These copies + * are shallow, in that the structures are considered owned by the bound generic + * args. Note that the bound args and parents have classes in the reverse order, + * so start with a pointer to the end, which will be decremented below. */ + zend_type *generic_args = ce->num_bound_generic_args + ? ce->bound_generic_args + ce->num_bound_generic_args : NULL; + + zend_class_reference **parents = + pemalloc(sizeof(zend_class_reference *) * (parent_ce->num_parents + 1), + ce->type == ZEND_INTERNAL_CLASS); + if (ce->parent_name) { + if (ZEND_PNR_IS_COMPLEX(ce->parent_name)) { + /* Ownership of the type arguments has been taken by generic arg binding. */ + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ce->parent_name); + zend_string_release(ref->name); + // efree(ref); + } else { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(ce->parent_name)); + } + + zend_class_reference *ref = emalloc(ZEND_CLASS_REF_SIZE(parent_ce->num_generic_params)); + ref->ce = parent_ce; + ref->args.num_types = parent_ce->num_generic_params; + generic_args -= ref->args.num_types; + memcpy(ref->args.types, generic_args, sizeof(zend_type) * ref->args.num_types); + parents[0] = ref; + } else { + /* Internal inheritance. */ + parents[0] = ZEND_CE_TO_REF(parent_ce); + } + + for (uint32_t i = 0; i < parent_ce->num_parents; i++) { + zend_class_reference *ref = parent_ce->parents[i]; + if (ZEND_REF_IS_TRIVIAL(ref)) { + parents[i + 1] = ref; + } else { + zend_class_reference *new_ref = emalloc(ZEND_CLASS_REF_SIZE(ref->args.num_types)); + new_ref->ce = ref->ce; + new_ref->args.num_types = ref->args.num_types; + generic_args -= ref->args.num_types; + memcpy(new_ref->args.types, generic_args, sizeof(zend_type) * ref->args.num_types); + parents[i + 1] = new_ref; + } + } + + ZEND_ASSERT(generic_args == NULL || generic_args == ce->bound_generic_args); + ce->num_parents = parent_ce->num_parents + 1; + ce->parents = parents; + ce->default_object_handlers = parent_ce->default_object_handlers; + ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT; +} + ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */ { zend_property_info *property_info; @@ -1512,12 +1697,17 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ); } - if (ce->parent_name) { - zend_string_release_ex(ce->parent_name, 0); + const zend_type_list *parent_args = &zend_empty_type_list; + if (ce->parent_name && ZEND_PNR_IS_COMPLEX(ce->parent_name)) { + parent_args = &ZEND_PNR_COMPLEX_GET_REF(ce->parent_name)->args; } - ce->parent = parent_ce; - ce->default_object_handlers = parent_ce->default_object_handlers; - ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT; + + if (parent_args->num_types || parent_ce->num_generic_params) { + zend_bind_parent_generic_args(ce, parent_ce, parent_args); + } + + /* Update parents list, including grandparents. */ + update_parents(ce, parent_ce); /* Inherit properties */ if (parent_ce->default_properties_count) { @@ -1813,7 +2003,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry { uint32_t i, ignore = 0; uint32_t current_iface_num = ce->num_interfaces; - uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0; + uint32_t parent_iface_num = ce->num_parents ? ce->parents[0]->ce->num_interfaces : 0; zend_string *key; zend_class_constant *c; @@ -1854,7 +2044,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry **interfaces) /* {{{ */ { zend_class_entry *iface; - uint32_t num_parent_interfaces = ce->parent ? ce->parent->num_interfaces : 0; + uint32_t num_parent_interfaces = ce->num_parents ? ce->parents[0]->ce->num_interfaces : 0; uint32_t num_interfaces = num_parent_interfaces; zend_string *key; zend_class_constant *c; @@ -2373,8 +2563,8 @@ static bool do_trait_constant_check( emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); return false; } else if (ZEND_TYPE_IS_SET(trait_constant->type)) { - inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type); - inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type); + inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type, NULL); + inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type, NULL); if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) { emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait); return false; @@ -3010,7 +3200,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string if (ce->parent_name) { parent = zend_fetch_class_by_name( - ce->parent_name, lc_parent_name, + ZEND_PNR_GET_NAME(ce->parent_name), lc_parent_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION); if (!parent) { check_unrecoverable_load_failure(ce); @@ -3336,7 +3526,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ zend_class_entry *orig_linking_class; if (ce->ce_flags & ZEND_ACC_LINKED) { - ZEND_ASSERT(ce->parent == NULL); + ZEND_ASSERT(ce->num_parents == 0); if (UNEXPECTED(!register_early_bound_ce(delayed_early_binding, lcname, ce))) { return NULL; } diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 5148668d945c7..73069872e7f2a 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -306,7 +306,7 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_new_iterator) { /* get_iterator was explicitly assigned for an internal class. */ - if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) { + if (!class_type->num_parents || class_type->parents[0]->ce->get_iterator != class_type->get_iterator) { ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS); return SUCCESS; } @@ -352,7 +352,7 @@ static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry &class_type->function_table, "next", sizeof("next") - 1); if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) { - if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) { + if (!class_type->num_parents || class_type->parents[0]->ce->get_iterator != class_type->get_iterator) { /* get_iterator was explicitly assigned for an internal class. */ ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS); return SUCCESS; @@ -460,9 +460,9 @@ ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const uns /* {{{ zend_implement_serializable */ static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type) { - if (class_type->parent - && (class_type->parent->serialize || class_type->parent->unserialize) - && !zend_class_implements_interface(class_type->parent, zend_ce_serializable)) { + if (class_type->num_parents + && (class_type->parents[0]->ce->serialize || class_type->parents[0]->ce->unserialize) + && !zend_class_implements_interface(class_type->parents[0]->ce, zend_ce_serializable)) { return FAILURE; } if (!class_type->serialize) { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 298eaf95ad055..795bd88bc2e1b 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -42,11 +42,15 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); #include "zend_compile.h" } +/* The conflicts are caused by generics syntax. + * foo(new Bar(42)) is ambiguous and resolved in favor of generics. */ +%glr-parser %define api.prefix {zend} -%define api.pure full +%define api.pure true %define api.value.type {zend_parser_stack_elem} %define parse.error verbose -%expect 0 +%expect 1 +%expect-rr 1 %destructor { zend_ast_destroy($$); } %destructor { if ($$) zend_string_release_ex($$, 0); } @@ -256,7 +260,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_expr anonymous_class class_name class_name_reference simple_variable -%type internal_functions_in_yacc +%type internal_functions_in_yacc simple_class_name generic_arg_list %type exit_expr scalar backticks_expr lexical_var function_call member_name property_name %type variable_class_name dereferenceable_scalar constant class_constant %type fully_dereferenceable array_object_dereferenceable @@ -279,6 +283,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list %type enum_declaration_statement enum_backing_type enum_case enum_case_expr %type function_name non_empty_member_modifiers +%type optional_generic_params generic_params generic_param %type returns_ref function fn is_reference is_variadic property_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -586,13 +591,34 @@ is_variadic: | T_ELLIPSIS { $$ = ZEND_PARAM_VARIADIC; } ; +optional_generic_params: + %empty { $$ = NULL; } + | '<' generic_params '>' { $$ = $2; } +; + +generic_params: + generic_param + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_PARAM_LIST, $1); } + | generic_params ',' generic_param + { $$ = zend_ast_list_add($1, $3); } +; + +/* TODO: in/out indicator */ +generic_param: + T_STRING { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, NULL, NULL); } + | T_STRING ':' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $3, NULL); } + | T_STRING '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, NULL, $3); } + | T_STRING ':' type_expr '=' type_expr + { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $3, $5); } +; + class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } + T_STRING optional_generic_params extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, NULL, $5); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } + T_STRING optional_generic_params extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, NULL, $4); } ; class_modifiers: @@ -621,14 +647,14 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } - T_STRING backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); } + T_STRING optional_generic_params backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $5, zend_ast_get_str($3), NULL, NULL, $7, NULL, $4); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } - T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); } + T_STRING optional_generic_params interface_extends_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4); } ; enum_declaration_statement: @@ -859,7 +885,9 @@ type_expr_without_static: type_without_static: T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } - | name { $$ = $1; } + | name { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, NULL); } + | name '<' generic_arg_list '>' + { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, $3); } ; union_type_without_static_element: @@ -892,10 +920,14 @@ argument_list: | '(' T_ELLIPSIS ')' { $$ = zend_ast_create(ZEND_AST_CALLABLE_CONVERT); } ; +/* The %dprec's resolve the foo(new Bar(42)) ambiguity in a somewhat indirect way: + * We give precedence to the interpretation that has fewer function call arguments, which + * implies that there will be more generic arguments, thus favoring generics. It is necessary + * to place the %dprec's here, as this is where the diverging parses will ultimately merge. */ non_empty_argument_list: - argument + argument %dprec 2 { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } - | non_empty_argument_list ',' argument + | non_empty_argument_list ',' argument %dprec 1 { $$ = zend_ast_list_add($1, $3); } ; @@ -1330,13 +1362,27 @@ function_call: } ; -class_name: +simple_class_name: T_STATIC { zval zv; ZVAL_INTERNED_STR(&zv, ZSTR_KNOWN(ZEND_STR_STATIC)); $$ = zend_ast_create_zval_ex(&zv, ZEND_NAME_NOT_FQ); } | name { $$ = $1; } ; +class_name: + simple_class_name + { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, NULL); } + | simple_class_name '<' generic_arg_list '>' + { $$ = zend_ast_create(ZEND_AST_CLASS_REF, $1, $3); } +; + +generic_arg_list: + type_expr + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_ARG_LIST, $1); } + | generic_arg_list ',' type_expr + { $$ = zend_ast_list_add($1, $3); } +; + class_name_reference: class_name { $$ = $1; } | new_variable { $$ = $1; } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 573d9eb106df1..b6aef6d8bdc1c 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -225,14 +225,11 @@ static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zv static zend_always_inline bool is_derived_class(const zend_class_entry *child_class, const zend_class_entry *parent_class) /* {{{ */ { - child_class = child_class->parent; - while (child_class) { - if (child_class == parent_class) { + for (uint32_t i = 0; i < child_class->num_parents; i++) { + if (child_class->parents[i]->ce == parent_class) { return 1; } - child_class = child_class->parent; } - return 0; } /* }}} */ @@ -742,7 +739,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } if (UNEXPECTED(prop_info)) { - zend_verify_prop_assignable_by_ref_ex(prop_info, retval, (zobj->ce->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET); + zend_verify_prop_assignable_by_ref_ex(zobj, prop_info, retval, (zobj->ce->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET); } OBJ_RELEASE(zobj); @@ -837,7 +834,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva ZVAL_COPY_VALUE(&tmp, value); // Increase refcount to prevent object from being released in __toString() GC_ADDREF(zobj); - bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()); + bool type_matched = zend_verify_property_type(zobj, prop_info, &tmp, property_uses_strict_types()); if (UNEXPECTED(GC_DELREF(zobj) == 0)) { zend_object_released_while_assigning_to_property_error(prop_info); zend_objects_store_del(zobj); @@ -940,7 +937,7 @@ found:; ZVAL_COPY_VALUE(&tmp, value); // Increase refcount to prevent object from being released in __toString() GC_ADDREF(zobj); - bool type_matched = zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()); + bool type_matched = zend_verify_property_type(zobj, prop_info, &tmp, property_uses_strict_types()); if (UNEXPECTED(GC_DELREF(zobj) == 0)) { zend_object_released_while_assigning_to_property_error(prop_info); zend_objects_store_del(zobj); @@ -1296,27 +1293,32 @@ static zend_never_inline zend_function *zend_get_parent_private_method(zend_clas */ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_entry *scope) /* {{{ */ { - const zend_class_entry *fbc_scope = ce; + if (ce == scope) { + return 1; + } + + if (scope == NULL) { + return 0; + } /* Is the context that's calling the function, the same as one of * the function's parents? */ - while (fbc_scope) { - if (fbc_scope==scope) { + for (uint32_t i = 0; i < ce->num_parents; i++) { + if (ce->parents[i]->ce == scope) { return 1; } - fbc_scope = fbc_scope->parent; } /* Is the function's scope the same as our current object context, * or any of the parents of our context? */ - while (scope) { - if (scope==ce) { + for (uint32_t i = 0; i < scope->num_parents; i++) { + if (scope->parents[i]->ce == ce) { return 1; } - scope = scope->parent; } + return 0; } /* }}} */ @@ -1558,15 +1560,15 @@ ZEND_API void zend_class_init_statics(zend_class_entry *class_type) /* {{{ */ zval *p; if (class_type->default_static_members_count && !CE_STATIC_MEMBERS(class_type)) { - if (class_type->parent) { - zend_class_init_statics(class_type->parent); + if (class_type->num_parents) { + zend_class_init_statics(class_type->parents[0]->ce); } ZEND_MAP_PTR_SET(class_type->static_members_table, emalloc(sizeof(zval) * class_type->default_static_members_count)); for (i = 0; i < class_type->default_static_members_count; i++) { p = &class_type->default_static_members_table[i]; if (Z_TYPE_P(p) == IS_INDIRECT) { - zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i]; + zval *q = &CE_STATIC_MEMBERS(class_type->parents[0]->ce)[i]; ZVAL_DEINDIRECT(q); ZVAL_INDIRECT(&CE_STATIC_MEMBERS(class_type)[i], q); } else { diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 93195a1be4c54..572810bc18116 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -108,17 +108,43 @@ ZEND_API void destroy_zend_function(zend_function *function) zend_function_dtor(&tmp); } +void zend_pnr_release(zend_packed_name_reference pnr, bool uses_arena, bool persistent); + +void zend_name_reference_release( + zend_name_reference *name_ref, bool uses_arena, bool persistent) { + zend_type *arg_type; + zend_string_release(name_ref->name); + ZEND_TYPE_LIST_FOREACH(&name_ref->args, arg_type) { + if (ZEND_TYPE_HAS_PNR(*arg_type)) { + zend_pnr_release(ZEND_TYPE_PNR(*arg_type), uses_arena, persistent); + } + } ZEND_TYPE_LIST_FOREACH_END(); + if (!uses_arena) { + pefree(name_ref, persistent); + } +} + +void zend_pnr_release(zend_packed_name_reference pnr, bool uses_arena, bool persistent) { + if (ZEND_PNR_IS_COMPLEX(pnr)) { + zend_name_reference_release(ZEND_PNR_COMPLEX_GET_REF(pnr), uses_arena, persistent); + } else { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + } +} + + ZEND_API void zend_type_release(zend_type type, bool persistent) { + bool uses_arena = ZEND_TYPE_USES_ARENA(type); if (ZEND_TYPE_HAS_LIST(type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { zend_type_release(*list_type, persistent); } ZEND_TYPE_LIST_FOREACH_END(); - if (!ZEND_TYPE_USES_ARENA(type)) { + if (!uses_arena) { pefree(ZEND_TYPE_LIST(type), persistent); } - } else if (ZEND_TYPE_HAS_NAME(type)) { - zend_string_release(ZEND_TYPE_NAME(type)); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_pnr_release(ZEND_TYPE_PNR(type), uses_arena, persistent); } } @@ -330,8 +356,20 @@ ZEND_API void destroy_zend_class(zval *zv) switch (ce->type) { case ZEND_USER_CLASS: if (!(ce->ce_flags & ZEND_ACC_CACHED)) { - if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { - zend_string_release_ex(ce->parent_name, 0); + if (ce->num_parents) { + if (!(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { + zend_pnr_release(ce->parent_name, /* uses_arena */ 1, /* persistent */ 0); + } else { + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(ref)) { + /* The type arguments are owned by bound_generic_args, + * and will be destroyed there. */ + efree(ref); + } + } + efree(ce->parents); + } } zend_string_release_ex(ce->name, 0); @@ -358,6 +396,23 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->num_traits > 0) { _destroy_zend_class_traits_info(ce); } + + if (ce->num_generic_params > 0) { + for (uint32_t i = 0; i < ce->num_generic_params; i++) { + zend_generic_param *param = &ce->generic_params[i]; + zend_string_release(param->name); + zend_type_release(param->bound_type, /* persistent */ 0); + zend_type_release(param->default_type, /* persistent */ 0); + } + efree(ce->generic_params); + } + if (ce->num_bound_generic_args > 0) { + for (uint32_t i = 0; i < ce->num_bound_generic_args; i++) { + zend_type *type = &ce->bound_generic_args[i]; + zend_type_release(*type, /* persistent */ 0); + } + efree(ce->bound_generic_args); + } } if (ce->default_properties_table) { @@ -422,6 +477,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->backed_enum_table) { zend_hash_release(ce->backed_enum_table); } + if (ce->num_parents) { + free(ce->parents); + } if (ce->default_properties_table) { zval *p = ce->default_properties_table; zval *end = p + ce->default_properties_count; @@ -510,7 +568,7 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->attributes) { zend_hash_release(ce->attributes); } - free(ce); + free((char *) ce - ZEND_CLASS_ENTRY_HEADER_SIZE); break; } } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 8853d24549fb2..50503d21f9a64 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2476,19 +2476,61 @@ ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *ins } return 0; } else { - while (1) { - instance_ce = instance_ce->parent; - if (instance_ce == ce) { - return 1; - } - if (instance_ce == NULL) { - return 0; + if (instance_ce->num_parents) { + for (uint32_t i = 0; i < instance_ce->num_parents; i++) { + zend_class_reference *parent_ref = instance_ce->parents[i]; + if (parent_ref->ce == ce) { + return 1; + } } } + return 0; } } /* }}} */ +static zend_always_inline bool zend_type_lists_compatible( + const zend_type_list *list1, const zend_type_list *list2, const zend_class_entry *ce) { + /* list1 is complete, while list2 may have defaulted arguments. */ + uint32_t i = 0; + for (; i < list2->num_types; i++) { + const zend_type *type1 = &list1->types[i]; + const zend_type *type2 = &list2->types[i]; + // TODO: Implement proper type comparison. + if (type1->type_mask != type2->type_mask || type1->ptr != type2->ptr) { + return 0; + } + } + for (; i < list1->num_types; i++) { + const zend_type *type1 = &list1->types[i]; + const zend_type *type2 = &ce->generic_params[i].default_type; + // TODO: Implement proper type comparison. + if (type1->type_mask != type2->type_mask || type1->ptr != type2->ptr) { + return 0; + } + } + return 1; +} + +ZEND_API bool ZEND_FASTCALL instanceof_unpacked_slow( + const zend_class_reference *instance_ref, const zend_class_entry *ce, const zend_type_list *args) { + zend_class_entry *instance_ce = instance_ref->ce; + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + return instanceof_function(instance_ce, ce); + } else { + if (instance_ce == ce && zend_type_lists_compatible(&instance_ref->args, args, ce)) { + return 1; + } + for (uint32_t i = 0; i < instance_ce->num_parents; i++) { + zend_class_reference *parent_ref = instance_ce->parents[i]; + if (parent_ref->ce == ce && zend_type_lists_compatible(&parent_ref->args, args, ce)) { + return 1; + } + } + return 0; + } +} + #define LOWER_CASE 1 #define UPPER_CASE 2 #define NUMERIC 3 diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 899567a953c63..3874c49ad9ac0 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -66,12 +66,20 @@ ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zv ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce); ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce); +ZEND_API bool ZEND_FASTCALL instanceof_unpacked_slow(const zend_class_reference *ce_ref, const zend_class_entry *ce, const zend_type_list *args); static zend_always_inline bool instanceof_function( const zend_class_entry *instance_ce, const zend_class_entry *ce) { return instance_ce == ce || instanceof_function_slow(instance_ce, ce); } +static zend_always_inline bool instanceof_unpacked( + const zend_class_reference *ce_ref, const zend_class_entry *ce, + const zend_type_list *args) { + return (ce_ref->ce == ce && ce_ref->args.num_types == 0 && args->num_types == 0) + || instanceof_unpacked_slow(ce_ref, ce, args); +} + ZEND_API bool zend_string_only_has_ascii_alphanumeric(const zend_string *str); /** diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f2f538a2c2c88..e55c01b48d3e7 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,16 +142,19 @@ typedef struct { zend_type types[1] ZEND_ELEMENT_COUNT(num_types); } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 -#define _ZEND_TYPE_MASK ((1u << 25) - 1) +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 26 +#define _ZEND_TYPE_MASK ((1u << 26) - 1) + /* Only one of these bits may be set. */ -#define _ZEND_TYPE_NAME_BIT (1u << 24) -// Used to signify that type.ptr is not a `zend_string*` but a `const char*`, -#define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 23) -#define _ZEND_TYPE_LIST_BIT (1u << 22) -#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) +#define _ZEND_TYPE_PNR_BIT (1u << 25) +#define _ZEND_TYPE_CLASS_REF_BIT (1u << 24) +#define _ZEND_TYPE_LIST_BIT (1u << 23) /* For BC behaviour with iterable type */ -#define _ZEND_TYPE_ITERABLE_BIT (1u << 21) +#define _ZEND_TYPE_ITERABLE_BIT (1u << 22) +#define _ZEND_TYPE_GENERIC_PARAM_BIT (1u << 21) +#define _ZEND_TYPE_CLASS_MASK \ + (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_CLASS_REF_BIT|_ZEND_TYPE_PNR_BIT) +#define _ZEND_TYPE_COMPLEX_MASK (_ZEND_TYPE_CLASS_MASK|_ZEND_TYPE_GENERIC_PARAM_BIT) /* Whether the type list is arena allocated */ #define _ZEND_TYPE_ARENA_BIT (1u << 20) /* Whether the type list is an intersection type */ @@ -163,19 +166,35 @@ typedef struct { /* Must have same value as MAY_BE_NULL */ #define _ZEND_TYPE_NULLABLE_BIT 0x2u +/* Both tag 0 and 1 represent a PNR, as it internally tags with the low bit. */ +#define _ZEND_TYPE_LIST_CLASS_REF_TAG ((uintptr_t) 2) +#define _ZEND_TYPE_LIST_GENERIC_PARAM_TAG ((uintptr_t) 3) +#define _ZEND_TYPE_LIST_TAG_MASK ((uintptr_t) 3) + #define ZEND_TYPE_IS_SET(t) \ (((t).type_mask & _ZEND_TYPE_MASK) != 0) -/* If a type is complex it means it's either a list with a union or intersection, - * or the void pointer is a class name */ +#define ZEND_TYPE_HAS_CLASS(t) \ + ((((t).type_mask) & _ZEND_TYPE_CLASS_MASK) != 0) + #define ZEND_TYPE_IS_COMPLEX(t) \ - ((((t).type_mask) & _ZEND_TYPE_KIND_MASK) != 0) + ((((t).type_mask) & _ZEND_TYPE_COMPLEX_MASK) != 0) -#define ZEND_TYPE_HAS_NAME(t) \ - ((((t).type_mask) & _ZEND_TYPE_NAME_BIT) != 0) +#define ZEND_TYPE_HAS_CLASS_REF(t) \ + ((((t).type_mask) & _ZEND_TYPE_CLASS_REF_BIT) != 0) +#define ZEND_TYPE_HAS_PNR(t) \ + ((((t).type_mask) & _ZEND_TYPE_PNR_BIT) != 0) + +/* Only used for arginfo, reuse bit. */ #define ZEND_TYPE_HAS_LITERAL_NAME(t) \ - ((((t).type_mask) & _ZEND_TYPE_LITERAL_NAME_BIT) != 0) + ZEND_TYPE_HAS_PNR(t) + +#define ZEND_TYPE_HAS_SIMPLE_PNR(t) \ + (ZEND_TYPE_HAS_PNR(t) && ZEND_PNR_IS_SIMPLE(ZEND_TYPE_PNR(t))) + +#define ZEND_TYPE_HAS_COMPLEX_PNR(t) \ + (ZEND_TYPE_HAS_PNR(t) && ZEND_PNR_IS_COMPLEX(ZEND_TYPE_PNR(t))) #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) @@ -189,21 +208,39 @@ typedef struct { #define ZEND_TYPE_IS_UNION(t) \ ((((t).type_mask) & _ZEND_TYPE_UNION_BIT) != 0) +#define ZEND_TYPE_HAS_GENERIC_PARAM(t) \ + ((((t).type_mask) & _ZEND_TYPE_GENERIC_PARAM_BIT) != 0) + #define ZEND_TYPE_USES_ARENA(t) \ ((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0) #define ZEND_TYPE_IS_ONLY_MASK(t) \ (ZEND_TYPE_IS_SET(t) && (t).ptr == NULL) -#define ZEND_TYPE_NAME(t) \ - ((zend_string *) (t).ptr) +#define ZEND_TYPE_PNR(t) \ + ((zend_packed_name_reference) (t).ptr) + +#define ZEND_TYPE_PNR_SIMPLE_NAME(t) \ + ZEND_PNR_SIMPLE_GET_NAME(ZEND_TYPE_PNR(t)) + +#define ZEND_TYPE_PNR_NAME(t) \ + ZEND_PNR_GET_NAME(ZEND_TYPE_PNR(t)) #define ZEND_TYPE_LITERAL_NAME(t) \ ((const char *) (t).ptr) +#define ZEND_TYPE_CLASS_REF(t) \ + ((zend_class_reference *) (t).ptr) + +#define ZEND_TYPE_PNR(t) \ + ((zend_packed_name_reference) (t).ptr) + #define ZEND_TYPE_LIST(t) \ ((zend_type_list *) (t).ptr) +#define ZEND_TYPE_GENERIC_PARAM_ID(t) \ + ((uint32_t) (uintptr_t) (t).ptr) + #define ZEND_TYPE_LIST_SIZE(num_types) \ (sizeof(zend_type_list) + ((num_types) - 1) * sizeof(zend_type)) @@ -218,6 +255,17 @@ typedef struct { } \ } while (0) +#define ZEND_TYPE_LIST_FOREACH_CONST(list, type_ptr) do { \ + const zend_type *_list = (list)->types; \ + const zend_type *_end = _list + (list)->num_types; \ + for (; _list < _end; _list++) { \ + type_ptr = _list; + +#define ZEND_TYPE_LIST_FOREACH_CONST_END() \ + } \ +} while (0) + + /* This iterates over any zend_type. If it's a type list, all list elements will * be visited. If it's a single type, only the single type is visited. */ #define ZEND_TYPE_FOREACH(type, type_ptr) do { \ @@ -240,12 +288,18 @@ typedef struct { #define ZEND_TYPE_SET_PTR(t, _ptr) \ ((t).ptr = (_ptr)) +#define ZEND_TYPE_SET_GENERIC_PARAM_ID(t, _id) \ + ((t).ptr = (void *) (uintptr_t) (_id)) + #define ZEND_TYPE_SET_PTR_AND_KIND(t, _ptr, kind_bit) do { \ (t).ptr = (_ptr); \ - (t).type_mask &= ~_ZEND_TYPE_KIND_MASK; \ + (t).type_mask &= ~_ZEND_TYPE_COMPLEX_MASK; \ (t).type_mask |= (kind_bit); \ } while (0) +#define ZEND_TYPE_SET_CLASS_REF(t, ce_ref) \ + ZEND_TYPE_SET_PTR_AND_KIND(t, ce_ref, _ZEND_TYPE_CLASS_REF_BIT) + #define ZEND_TYPE_SET_LIST(t, list) \ ZEND_TYPE_SET_PTR_AND_KIND(t, list, _ZEND_TYPE_LIST_BIT) @@ -300,17 +354,79 @@ typedef struct { #define ZEND_TYPE_INIT_INTERSECTION(ptr, extra_flags) \ _ZEND_TYPE_PREFIX { (void *) (ptr), (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_INTERSECTION_BIT) | (extra_flags) } -#define ZEND_TYPE_INIT_CLASS(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags) +#define ZEND_TYPE_INIT_CLASS_REF(ce_ref, allow_null, extra_flags) \ + ZEND_TYPE_INIT_PTR(ce_ref, _ZEND_TYPE_CLASS_REF_BIT, allow_null, extra_flags) + +#define ZEND_TYPE_INIT_PNR(pnr, allow_null, extra_flags) \ + ZEND_TYPE_INIT_PTR((void *) pnr, _ZEND_TYPE_PNR_BIT, allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) + ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_PNR_BIT | (type_mask)) #define ZEND_TYPE_INIT_CLASS_CONST(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_LITERAL_NAME_BIT, allow_null, extra_flags) + ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_PNR_BIT, allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_CONST_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_LITERAL_NAME_BIT | (type_mask))) + ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_PNR_BIT | (type_mask))) + +#define ZEND_TYPE_INIT_GENERIC_PARAM(param_id, extra_flags) \ + { (void *) (uintptr_t) param_id, _ZEND_TYPE_GENERIC_PARAM_BIT | (extra_flags) } + +/* Represents a class entry together with bound generic type arguments. Normal class entries + * are prefixed with a compatible header, such that any class can be cheaply reinterpreted as + * a zero-argument reference to itself. */ +typedef struct _zend_class_reference { + zend_class_entry *ce; + zend_type_list args; +} zend_class_reference; + +/* The same, but for unresolved cases where we only have the name available. + * This should be structurally the same zend_class_reference to permit in-place resolution. */ +typedef struct _zend_name_reference { + zend_string *name; + zend_type_list args; +} zend_name_reference; + +#define ZEND_CLASS_ENTRY_HEADER_SIZE (2 * sizeof(void*)) + +#define ZEND_CE_TO_REF(ce) \ + ((zend_class_reference *) ((char *) (ce) - ZEND_CLASS_ENTRY_HEADER_SIZE)) + +#define ZEND_REF_IS_TRIVIAL(ref) \ + ((ref)->ce == (zend_class_entry *) ((char *) (ref) + ZEND_CLASS_ENTRY_HEADER_SIZE)) + +#define ZEND_CLASS_REF_SIZE(num_types) \ + (sizeof(zend_class_reference) - sizeof(zend_type) + (num_types) * sizeof(zend_type)) + +/* A packed name reference is used to either store a simple name string, + * or a full zend_name_reference structure. The low bit is reserved for the tag. */ +typedef uintptr_t zend_packed_name_reference; + +// TODO: Make this non-static? +static const zend_type_list zend_empty_type_list = {0}; + +#define ZEND_PNR_IS_COMPLEX(pnr) ((pnr) & 1) +#define ZEND_PNR_IS_SIMPLE(pnr) !ZEND_PNR_IS_COMPLEX(pnr) +#define ZEND_PNR_SIMPLE_GET_NAME(pnr) ((zend_string *) (pnr)) +#define ZEND_PNR_COMPLEX_GET_REF(pnr) ((zend_name_reference *) (pnr - 1)) +#define ZEND_PNR_COMPLEX_GET_NAME(pnr) ZEND_PNR_COMPLEX_GET_REF(pnr)->name + +#define ZEND_PNR_ENCODE_NAME(name) ((uintptr_t) (name)) +#define ZEND_PNR_ENCODE_REF(ref) ((uintptr_t) (ref) + 1) + +#define ZEND_PNR_GET_NAME(pnr) \ + (ZEND_PNR_IS_COMPLEX(pnr) ? ZEND_PNR_COMPLEX_GET_NAME(pnr) : ZEND_PNR_SIMPLE_GET_NAME(pnr)) + +#define ZEND_PNR_UNPACK(pnr, name_var, args_var) do { \ + if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + (name_var) = __ref->name; \ + (args_var) = &__ref->args; \ + } else { \ + (name_var) = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + (args_var) = &zend_empty_type_list; \ + } \ +} while (0) typedef union _zend_value { zend_long lval; /* long value */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f731653a24813..40f3e7c98171e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1076,7 +1076,8 @@ ZEND_VM_C_LABEL(assign_op_object): } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -1133,7 +1134,8 @@ ZEND_VM_HANDLER(29, ZEND_ASSIGN_STATIC_PROP_OP, ANY, ANY, OP) if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + NULL, prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(prop, prop, value OPLINE_CC); } @@ -1331,9 +1333,9 @@ ZEND_VM_C_LABEL(pre_incdec_object): if (OP2_TYPE == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -1402,10 +1404,10 @@ ZEND_VM_C_LABEL(post_incdec_object): if (OP2_TYPE == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -1439,7 +1441,7 @@ ZEND_VM_HANDLER(38, ZEND_PRE_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT) HANDLE_EXCEPTION(); } - zend_pre_incdec_property_zval(prop, + zend_pre_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -1465,7 +1467,7 @@ ZEND_VM_HANDLER(40, ZEND_POST_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT) HANDLE_EXCEPTION(); } - zend_post_incdec_property_zval(prop, + zend_post_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -2141,7 +2143,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy): if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -2418,7 +2420,7 @@ ZEND_VM_C_LABEL(assign_object): zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); ZEND_VM_C_GOTO(free_and_exit_assign_obj); } else { ZEND_VM_C_LABEL(fast_assign_obj): @@ -2534,7 +2536,7 @@ ZEND_VM_HANDLER(25, ZEND_ASSIGN_STATIC_PROP, ANY, ANY, CACHE_SLOT, SPEC(OP_DATA= value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); FREE_OP_DATA(); } else { value = zend_assign_to_variable_ex(prop, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES(), &garbage); @@ -2832,7 +2834,8 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_STATIC_PROP_REF, ANY, ANY, CACHE_SLOT|SRC) prop = &EG(uninitialized_zval); } } else if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - prop = zend_assign_to_typed_property_reference(prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); + prop = zend_assign_to_typed_property_reference( + NULL, prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); } else { zend_assign_to_variable_reference(prop, value_ptr, &garbage); } @@ -4316,7 +4319,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -8751,14 +8754,14 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, CV|TMPVAR|UNUSED|CLASS_FETCH, ANY) ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index fbec492f7d70a..60b7534c7710a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -793,7 +793,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_OP_SPEC_HAN if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + NULL, prop_info, prop, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(prop, prop, value OPLINE_CC); } @@ -821,7 +822,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_STATIC_PROP_SPEC_HANDL HANDLE_EXCEPTION(); } - zend_pre_incdec_property_zval(prop, + zend_pre_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -841,7 +842,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND HANDLE_EXCEPTION(); } - zend_post_incdec_property_zval(prop, + zend_post_incdec_property_zval(NULL, prop, ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -949,7 +950,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = RT_CONSTANT((opline+1), (opline+1)->op1); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); } else { value = zend_assign_to_variable_ex(prop, value, IS_CONST, EX_USES_STRICT_TYPES(), &garbage); @@ -985,7 +986,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } else { value = zend_assign_to_variable_ex(prop, value, IS_TMP_VAR, EX_USES_STRICT_TYPES(), &garbage); @@ -1021,7 +1022,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } else { value = zend_assign_to_variable_ex(prop, value, IS_VAR, EX_USES_STRICT_TYPES(), &garbage); @@ -1057,7 +1058,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT value = _get_zval_ptr_cv_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - value = zend_assign_to_typed_prop(prop_info, prop, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(NULL, prop_info, prop, value, &garbage EXECUTE_DATA_CC); } else { value = zend_assign_to_variable_ex(prop, value, IS_CV, EX_USES_STRICT_TYPES(), &garbage); @@ -1097,7 +1098,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_REF_SPEC_HA prop = &EG(uninitialized_zval); } } else if (UNEXPECTED(ZEND_TYPE_IS_SET(prop_info->type))) { - prop = zend_assign_to_typed_property_reference(prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); + prop = zend_assign_to_typed_property_reference( + NULL, prop_info, prop, value_ptr, &garbage EXECUTE_DATA_CC); } else { zend_assign_to_variable_reference(prop, value_ptr, &garbage); } @@ -6494,7 +6496,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -9014,7 +9016,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -10402,7 +10404,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -11401,7 +11403,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -15126,14 +15128,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_TMPVAR_H ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -15783,7 +15785,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -17228,7 +17230,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -18588,7 +18590,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -20888,7 +20890,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -22928,7 +22930,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CONST_H } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -23135,9 +23138,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CONST_HAN if (IS_CONST == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -23200,10 +23203,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CONST_HA if (IS_CONST == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -23411,7 +23414,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23545,7 +23548,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23679,7 +23682,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -23813,7 +23816,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -25849,7 +25852,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_TMPVAR_ } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -26058,9 +26062,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -26124,10 +26128,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_H if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -26337,7 +26341,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26471,7 +26475,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26605,7 +26609,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -26739,7 +26743,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -29227,7 +29231,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -30146,7 +30150,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HAND } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -30353,9 +30358,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLE if (IS_CV == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -30418,10 +30423,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDL if (IS_CV == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -30629,7 +30634,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -30763,7 +30768,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -30897,7 +30902,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -31031,7 +31036,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -32657,14 +32662,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_H ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -32750,7 +32755,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONS } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -32827,9 +32833,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_ if (IS_CONST == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -32892,10 +32898,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST if (IS_CONST == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -33015,7 +33021,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -33262,7 +33268,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33396,7 +33402,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33530,7 +33536,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -33664,7 +33670,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -34807,7 +34813,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_TMPV } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -34884,9 +34891,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -34950,10 +34957,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -35074,7 +35081,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -35316,7 +35323,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35450,7 +35457,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35584,7 +35591,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -35718,7 +35725,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -36728,7 +36735,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -37305,7 +37312,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_H } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -37382,9 +37390,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HAN if (IS_CV == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -37447,10 +37455,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HA if (IS_CV == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -37570,7 +37578,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -37812,7 +37820,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -37946,7 +37954,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -38080,7 +38088,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -38214,7 +38222,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -40484,14 +40492,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_CV_HANDL ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name); break; case ZEND_FETCH_CLASS_PARENT: - if (UNEXPECTED(scope->parent == NULL)) { + if (UNEXPECTED(!scope->num_parents)) { SAVE_OPLINE(); zend_throw_error(NULL, "Cannot use \"parent\" when current class scope has no parent"); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } - ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name); + ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parents[0]->ce->name); break; case ZEND_FETCH_CLASS_STATIC: if (Z_TYPE(EX(This)) == IS_OBJECT) { @@ -41333,7 +41341,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CONST_HA } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -41540,9 +41549,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HAND if (IS_CONST == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -41605,10 +41614,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CONST_HAN if (IS_CONST == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -41840,7 +41849,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -42087,7 +42096,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42221,7 +42230,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42355,7 +42364,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -42489,7 +42498,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -45173,7 +45182,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_TMPVAR_H } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -45382,9 +45392,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HAN if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -45448,10 +45458,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -45684,7 +45694,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -45926,7 +45936,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -46060,7 +46070,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -46194,7 +46204,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -46328,7 +46338,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -49142,7 +49152,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU } SAVE_OPLINE(); - if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) { + if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, EX(func)->common.scope, 1, 0))) { zend_verify_return_error(EX(func), retval_ptr); HANDLE_EXCEPTION(); } @@ -50568,7 +50578,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CV_HANDL } if (UNEXPECTED(prop_info)) { /* special case for typed properties */ - zend_binary_assign_op_typed_prop(prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_typed_prop( + zobj, prop_info, zptr, value OPLINE_CC EXECUTE_DATA_CC); } else { zend_binary_op(zptr, zptr, value OPLINE_CC); } @@ -50775,9 +50786,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CV_HANDLER if (IS_CV == IS_CONST) { prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_pre_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_pre_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -50840,10 +50851,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CV_HANDLE if (IS_CV == IS_CONST) { prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr); + prop_info = zend_object_fetch_property_type_info(zobj, zptr); } - zend_post_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); + zend_post_incdec_property_zval(zobj, zptr, prop_info OPLINE_CC EXECUTE_DATA_CC); } } else { zend_post_incdec_overloaded_property(zobj, name, cache_slot OPLINE_CC EXECUTE_DATA_CC); @@ -51075,7 +51086,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) { ZVAL_OPT_DEREF(retval); - zend_verify_property_type(prop_info, retval, /* strict */ true); + zend_verify_property_type(zobj, prop_info, retval, /* strict */ true); } #endif @@ -51317,7 +51328,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51451,7 +51462,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51585,7 +51596,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: @@ -51719,7 +51730,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); if (UNEXPECTED(prop_info != NULL)) { - value = zend_assign_to_typed_prop(prop_info, property_val, value, &garbage EXECUTE_DATA_CC); + value = zend_assign_to_typed_prop(zobj, prop_info, property_val, value, &garbage EXECUTE_DATA_CC); goto free_and_exit_assign_obj; } else { fast_assign_obj: diff --git a/build/gen_stub.php b/build/gen_stub.php index d597d30a72f3f..02c352987aa7b 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2294,7 +2294,7 @@ protected function getTypeCode(string $variableLikeName, string &$code): string foreach ($arginfoType->classTypes as $k => $classType) { $escapedClassName = $classType->toEscapedName(); - $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->types[$k] = (zend_type) ZEND_TYPE_INIT_CLASS({$variableLikeType}_{$variableLikeName}_class_{$escapedClassName}, 0, 0);\n"; + $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->types[$k] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST({$variableLikeType}_{$variableLikeName}_class_{$escapedClassName}, 0, 0);\n"; } $typeMaskCode = $this->type->toArginfoType()->toTypeMask(); @@ -2310,7 +2310,7 @@ protected function getTypeCode(string $variableLikeName, string &$code): string $varEscapedClassName = $arginfoType->classTypes[0]->toVarEscapedName(); $code .= "\tzend_string *{$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName} = zend_string_init(\"{$escapedClassName}\", sizeof(\"{$escapedClassName}\")-1, 1);\n"; - $typeCode = "(zend_type) ZEND_TYPE_INIT_CLASS({$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName}, 0, " . $arginfoType->toTypeMask() . ")"; + $typeCode = "(zend_type) ZEND_TYPE_INIT_CLASS_CONST({$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName}, 0, " . $arginfoType->toTypeMask() . ")"; } } else { $typeCode = "(zend_type) ZEND_TYPE_INIT_MASK(" . $arginfoType->toTypeMask() . ")"; diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 02236d6676ea7..52e6e98260ec5 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -314,10 +314,7 @@ static void date_throw_uninitialized_error(zend_class_entry *ce) if (ce->type == ZEND_INTERNAL_CLASS) { zend_throw_error(date_ce_date_object_error, "Object of type %s has not been correctly initialized by calling parent::__construct() in its constructor", ZSTR_VAL(ce->name)); } else { - zend_class_entry *ce_ptr = ce; - while (ce_ptr && ce_ptr->parent && ce_ptr->type == ZEND_USER_CLASS) { - ce_ptr = ce_ptr->parent; - } + zend_class_entry *ce_ptr = zend_class_entry_get_root(ce); if (ce_ptr->type != ZEND_INTERNAL_CLASS) { zend_throw_error(date_ce_date_object_error, "Object of type %s not been correctly initialized by calling parent::__construct() in its constructor", ZSTR_VAL(ce->name)); } @@ -1596,8 +1593,8 @@ static zend_class_entry *get_base_date_class(zend_class_entry *start_ce) { zend_class_entry *tmp = start_ce; - while (tmp != date_ce_date && tmp != date_ce_immutable && tmp->parent) { - tmp = tmp->parent; + while (tmp != date_ce_date && tmp != date_ce_immutable && tmp->num_parents) { + tmp = tmp->parents[0]->ce; } return tmp; diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 027bec67cb08d..f71f34a6dc8ea 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1104,28 +1104,28 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry ZVAL_UNDEF(&property_start_default_value); zend_string *property_start_name = zend_string_init("start", sizeof("start") - 1, 1); zend_string *property_start_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); - zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_start_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_start_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_start_name); zval property_current_default_value; ZVAL_UNDEF(&property_current_default_value); zend_string *property_current_name = zend_string_init("current", sizeof("current") - 1, 1); zend_string *property_current_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); - zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_current_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_current_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_current_name); zval property_end_default_value; ZVAL_UNDEF(&property_end_default_value); zend_string *property_end_name = zend_string_init("end", sizeof("end") - 1, 1); zend_string *property_end_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); - zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_end_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_end_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_end_name); zval property_interval_default_value; ZVAL_UNDEF(&property_interval_default_value); zend_string *property_interval_name = zend_string_init("interval", sizeof("interval") - 1, 1); zend_string *property_interval_class_DateInterval = zend_string_init("DateInterval", sizeof("DateInterval")-1, 1); - zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_interval_class_DateInterval, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_interval_class_DateInterval, 0, MAY_BE_NULL)); zend_string_release(property_interval_name); zval property_recurrences_default_value; diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index 3a2ec9eddc034..ab2e0a643c63b 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1169,14 +1169,14 @@ static zend_class_entry *register_class_DOMDocumentType(zend_class_entry *class_ ZVAL_UNDEF(&property_entities_default_value); zend_string *property_entities_name = zend_string_init("entities", sizeof("entities") - 1, 1); zend_string *property_entities_class_DOMNamedNodeMap = zend_string_init("DOMNamedNodeMap", sizeof("DOMNamedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_entities_name, &property_entities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_entities_class_DOMNamedNodeMap, 0, 0)); + zend_declare_typed_property(class_entry, property_entities_name, &property_entities_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_entities_class_DOMNamedNodeMap, 0, 0)); zend_string_release(property_entities_name); zval property_notations_default_value; ZVAL_UNDEF(&property_notations_default_value); zend_string *property_notations_name = zend_string_init("notations", sizeof("notations") - 1, 1); zend_string *property_notations_class_DOMNamedNodeMap = zend_string_init("DOMNamedNodeMap", sizeof("DOMNamedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_notations_name, &property_notations_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_notations_class_DOMNamedNodeMap, 0, 0)); + zend_declare_typed_property(class_entry, property_notations_name, &property_notations_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_notations_class_DOMNamedNodeMap, 0, 0)); zend_string_release(property_notations_name); zval property_publicId_default_value; @@ -1310,56 +1310,56 @@ static zend_class_entry *register_class_DOMNode(void) ZVAL_UNDEF(&property_parentNode_default_value); zend_string *property_parentNode_name = zend_string_init("parentNode", sizeof("parentNode") - 1, 1); zend_string *property_parentNode_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_parentNode_name); zval property_parentElement_default_value; ZVAL_UNDEF(&property_parentElement_default_value); zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1); zend_string *property_parentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_parentElement_name); zval property_childNodes_default_value; ZVAL_UNDEF(&property_childNodes_default_value); zend_string *property_childNodes_name = zend_string_init("childNodes", sizeof("childNodes") - 1, 1); zend_string *property_childNodes_class_DOMNodeList = zend_string_init("DOMNodeList", sizeof("DOMNodeList")-1, 1); - zend_declare_typed_property(class_entry, property_childNodes_name, &property_childNodes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_childNodes_class_DOMNodeList, 0, 0)); + zend_declare_typed_property(class_entry, property_childNodes_name, &property_childNodes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_childNodes_class_DOMNodeList, 0, 0)); zend_string_release(property_childNodes_name); zval property_firstChild_default_value; ZVAL_UNDEF(&property_firstChild_default_value); zend_string *property_firstChild_name = zend_string_init("firstChild", sizeof("firstChild") - 1, 1); zend_string *property_firstChild_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_firstChild_name, &property_firstChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstChild_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstChild_name, &property_firstChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstChild_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_firstChild_name); zval property_lastChild_default_value; ZVAL_UNDEF(&property_lastChild_default_value); zend_string *property_lastChild_name = zend_string_init("lastChild", sizeof("lastChild") - 1, 1); zend_string *property_lastChild_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_lastChild_name, &property_lastChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastChild_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastChild_name, &property_lastChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastChild_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_lastChild_name); zval property_previousSibling_default_value; ZVAL_UNDEF(&property_previousSibling_default_value); zend_string *property_previousSibling_name = zend_string_init("previousSibling", sizeof("previousSibling") - 1, 1); zend_string *property_previousSibling_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_previousSibling_name, &property_previousSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousSibling_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previousSibling_name, &property_previousSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previousSibling_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_previousSibling_name); zval property_nextSibling_default_value; ZVAL_UNDEF(&property_nextSibling_default_value); zend_string *property_nextSibling_name = zend_string_init("nextSibling", sizeof("nextSibling") - 1, 1); zend_string *property_nextSibling_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_nextSibling_name, &property_nextSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextSibling_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_nextSibling_name, &property_nextSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_nextSibling_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_nextSibling_name); zval property_attributes_default_value; ZVAL_UNDEF(&property_attributes_default_value); zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); zend_string *property_attributes_class_DOMNamedNodeMap = zend_string_init("DOMNamedNodeMap", sizeof("DOMNamedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_DOMNamedNodeMap, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_attributes_class_DOMNamedNodeMap, 0, MAY_BE_NULL)); zend_string_release(property_attributes_name); zval property_isConnected_default_value; @@ -1372,7 +1372,7 @@ static zend_class_entry *register_class_DOMNode(void) ZVAL_UNDEF(&property_ownerDocument_default_value); zend_string *property_ownerDocument_name = zend_string_init("ownerDocument", sizeof("ownerDocument") - 1, 1); zend_string *property_ownerDocument_class_DOM_Document = zend_string_init("DOM\\Document", sizeof("DOM\\Document")-1, 1); - zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerDocument_class_DOM_Document, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_ownerDocument_class_DOM_Document, 0, MAY_BE_NULL)); zend_string_release(property_ownerDocument_name); zval property_namespaceURI_default_value; @@ -1462,21 +1462,21 @@ static zend_class_entry *register_class_DOMNameSpaceNode(void) ZVAL_UNDEF(&property_ownerDocument_default_value); zend_string *property_ownerDocument_name = zend_string_init("ownerDocument", sizeof("ownerDocument") - 1, 1); zend_string *property_ownerDocument_class_DOMDocument = zend_string_init("DOMDocument", sizeof("DOMDocument")-1, 1); - zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerDocument_class_DOMDocument, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_ownerDocument_name, &property_ownerDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_ownerDocument_class_DOMDocument, 0, MAY_BE_NULL)); zend_string_release(property_ownerDocument_name); zval property_parentNode_default_value; ZVAL_UNDEF(&property_parentNode_default_value); zend_string *property_parentNode_name = zend_string_init("parentNode", sizeof("parentNode") - 1, 1); zend_string *property_parentNode_class_DOMNode = zend_string_init("DOMNode", sizeof("DOMNode")-1, 1); - zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentNode_class_DOMNode, 0, MAY_BE_NULL)); zend_string_release(property_parentNode_name); zval property_parentElement_default_value; ZVAL_UNDEF(&property_parentElement_default_value); zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1); zend_string *property_parentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_parentElement_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_parentElement_name); return class_entry; @@ -1505,14 +1505,14 @@ static zend_class_entry *register_class_DOMDocumentFragment(zend_class_entry *cl ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); zend_string *property_firstElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_firstElementChild_name); zval property_lastElementChild_default_value; ZVAL_UNDEF(&property_lastElementChild_default_value); zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); zend_string *property_lastElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_lastElementChild_name); zval property_childElementCount_default_value; @@ -1567,14 +1567,14 @@ static zend_class_entry *register_class_DOMCharacterData(zend_class_entry *class ZVAL_UNDEF(&property_previousElementSibling_default_value); zend_string *property_previousElementSibling_name = zend_string_init("previousElementSibling", sizeof("previousElementSibling") - 1, 1); zend_string *property_previousElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_previousElementSibling_name); zval property_nextElementSibling_default_value; ZVAL_UNDEF(&property_nextElementSibling_default_value); zend_string *property_nextElementSibling_name = zend_string_init("nextElementSibling", sizeof("nextElementSibling") - 1, 1); zend_string *property_nextElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_nextElementSibling_name); return class_entry; @@ -1610,7 +1610,7 @@ static zend_class_entry *register_class_DOMAttr(zend_class_entry *class_entry_DO ZVAL_UNDEF(&property_ownerElement_default_value); zend_string *property_ownerElement_name = zend_string_init("ownerElement", sizeof("ownerElement") - 1, 1); zend_string *property_ownerElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_ownerElement_name, &property_ownerElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_ownerElement_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_ownerElement_name, &property_ownerElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_ownerElement_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_ownerElement_name); zval property_schemaTypeInfo_default_value; @@ -1659,14 +1659,14 @@ static zend_class_entry *register_class_DOMElement(zend_class_entry *class_entry ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); zend_string *property_firstElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_firstElementChild_name); zval property_lastElementChild_default_value; ZVAL_UNDEF(&property_lastElementChild_default_value); zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); zend_string *property_lastElementChild_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastElementChild_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_lastElementChild_name); zval property_childElementCount_default_value; @@ -1679,14 +1679,14 @@ static zend_class_entry *register_class_DOMElement(zend_class_entry *class_entry ZVAL_UNDEF(&property_previousElementSibling_default_value); zend_string *property_previousElementSibling_name = zend_string_init("previousElementSibling", sizeof("previousElementSibling") - 1, 1); zend_string *property_previousElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_previousElementSibling_name, &property_previousElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_previousElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_previousElementSibling_name); zval property_nextElementSibling_default_value; ZVAL_UNDEF(&property_nextElementSibling_default_value); zend_string *property_nextElementSibling_name = zend_string_init("nextElementSibling", sizeof("nextElementSibling") - 1, 1); zend_string *property_nextElementSibling_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1); - zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_nextElementSibling_class_DOMElement, 0, MAY_BE_NULL)); zend_string_release(property_nextElementSibling_name); return class_entry; @@ -1703,7 +1703,7 @@ static zend_class_entry *register_class_DOMDocument(zend_class_entry *class_entr ZVAL_UNDEF(&property_implementation_default_value); zend_string *property_implementation_name = zend_string_init("implementation", sizeof("implementation") - 1, 1); zend_string *property_implementation_class_DOMImplementation = zend_string_init("DOMImplementation", sizeof("DOMImplementation")-1, 1); - zend_declare_typed_property(class_entry, property_implementation_name, &property_implementation_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_implementation_class_DOMImplementation, 0, 0)); + zend_declare_typed_property(class_entry, property_implementation_name, &property_implementation_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_implementation_class_DOMImplementation, 0, 0)); zend_string_release(property_implementation_name); zval property_actualEncoding_default_value; @@ -1958,7 +1958,7 @@ static zend_class_entry *register_class_DOMXPath(void) ZVAL_UNDEF(&property_document_default_value); zend_string *property_document_name = zend_string_init("document", sizeof("document") - 1, 1); zend_string *property_document_class_DOM_Document = zend_string_init("DOM\\Document", sizeof("DOM\\Document")-1, 1); - zend_declare_typed_property(class_entry, property_document_name, &property_document_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_document_class_DOM_Document, 0, 0)); + zend_declare_typed_property(class_entry, property_document_name, &property_document_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_document_class_DOM_Document, 0, 0)); zend_string_release(property_document_name); zval property_registerNodeNamespaces_default_value; @@ -1984,14 +1984,14 @@ static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_ent ZVAL_UNDEF(&property_doctype_default_value); zend_string *property_doctype_name = zend_string_init("doctype", sizeof("doctype") - 1, 1); zend_string *property_doctype_class_DOM_DocumentType = zend_string_init("DOM\\DocumentType", sizeof("DOM\\DocumentType")-1, 1); - zend_declare_typed_property(class_entry, property_doctype_name, &property_doctype_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_doctype_class_DOM_DocumentType, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_doctype_name, &property_doctype_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_doctype_class_DOM_DocumentType, 0, MAY_BE_NULL)); zend_string_release(property_doctype_name); zval property_documentElement_default_value; ZVAL_UNDEF(&property_documentElement_default_value); zend_string *property_documentElement_name = zend_string_init("documentElement", sizeof("documentElement") - 1, 1); zend_string *property_documentElement_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_documentElement_class_DOM_Element, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_documentElement_class_DOM_Element, 0, MAY_BE_NULL)); zend_string_release(property_documentElement_name); zval property_encoding_default_value; @@ -2016,14 +2016,14 @@ static zend_class_entry *register_class_DOM_Document(zend_class_entry *class_ent ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); zend_string *property_firstElementChild_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_DOM_Element, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_firstElementChild_class_DOM_Element, 0, MAY_BE_NULL)); zend_string_release(property_firstElementChild_name); zval property_lastElementChild_default_value; ZVAL_UNDEF(&property_lastElementChild_default_value); zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); zend_string *property_lastElementChild_class_DOM_Element = zend_string_init("DOM\\Element", sizeof("DOM\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_DOM_Element, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_lastElementChild_class_DOM_Element, 0, MAY_BE_NULL)); zend_string_release(property_lastElementChild_name); zval property_childElementCount_default_value; diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index 2ba8ced481b7f..c808653320ed3 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -343,7 +343,7 @@ static zend_class_entry *register_class_Random_Randomizer(void) ZVAL_UNDEF(&property_engine_default_value); zend_string *property_engine_name = zend_string_init("engine", sizeof("engine") - 1, 1); zend_string *property_engine_class_Random_Engine = zend_string_init("Random\\Engine", sizeof("Random\\Engine")-1, 1); - zend_declare_typed_property(class_entry, property_engine_name, &property_engine_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_engine_class_Random_Engine, 0, 0)); + zend_declare_typed_property(class_entry, property_engine_name, &property_engine_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_engine_class_Random_Engine, 0, 0)); zend_string_release(property_engine_name); return class_entry; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 53e978604ad91..09cbf2bd2065b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -233,8 +233,8 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ case REF_TYPE_TYPE: { type_reference *type_ref = intern->ptr; - if (ZEND_TYPE_HAS_NAME(type_ref->type)) { - zend_string_release(ZEND_TYPE_NAME(type_ref->type)); + if (ZEND_TYPE_HAS_SIMPLE_PNR(type_ref->type)) { + zend_string_release(ZEND_TYPE_PNR_SIMPLE_NAME(type_ref->type)); } efree(type_ref); break; @@ -350,8 +350,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char smart_str_append_printf(str, "class "); } smart_str_append_printf(str, "%s", ZSTR_VAL(ce->name)); - if (ce->parent) { - smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parent->name)); + if (ce->num_parents) { + smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parents[0]->ce->name)); } if (ce->num_interfaces) { @@ -564,7 +564,7 @@ static void _class_const_string(smart_str *str, zend_string *name, zend_class_co const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c)); const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : ""; - zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type) : NULL; + zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type, NULL) : NULL; const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, ZSTR_VAL(name)); @@ -674,7 +674,7 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_ smart_str_append_printf(str, " "); } if (ZEND_TYPE_IS_SET(arg_info->type)) { - zend_string *type_str = zend_type_to_string(arg_info->type); + zend_string *type_str = zend_type_to_string(arg_info->type, fptr->common.scope); smart_str_append(str, type_str); smart_str_appendc(str, ' '); zend_string_release(type_str); @@ -795,9 +795,9 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent if (scope && fptr->common.scope) { if (fptr->common.scope != scope) { smart_str_append_printf(str, ", inherits %s", ZSTR_VAL(fptr->common.scope->name)); - } else if (fptr->common.scope->parent) { + } else if (fptr->common.scope->num_parents) { lc_name = zend_string_tolower(fptr->common.function_name); - if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parent->function_table, lc_name)) != NULL) { + if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parents[0]->ce->function_table, lc_name)) != NULL) { if (fptr->common.scope != overwrites->common.scope && !(overwrites->common.fn_flags & ZEND_ACC_PRIVATE)) { smart_str_append_printf(str, ", overwrites %s", ZSTR_VAL(overwrites->common.scope->name)); } @@ -865,7 +865,8 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent if ((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { smart_str_append_printf(str, " %s- %s [ ", indent, ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]) ? "Tentative return" : "Return"); if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) { - zend_string *type_str = zend_type_to_string(fptr->common.arg_info[-1].type); + zend_string *type_str = + zend_type_to_string(fptr->common.arg_info[-1].type, fptr->common.scope); smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str)); zend_string_release(type_str); } @@ -912,7 +913,7 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha smart_str_appends(str, "readonly "); } if (ZEND_TYPE_IS_SET(prop->type)) { - zend_string *type_str = zend_type_to_string(prop->type); + zend_string *type_str = zend_type_to_string(prop->type, prop->ce); smart_str_append(str, type_str); smart_str_appendc(str, ' '); zend_string_release(type_str); @@ -1400,8 +1401,8 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be * do this for the top-level type, as resolutions inside type lists will be * fully visible to us (we'd have to do a fully copy of the type if we wanted * to prevent that). */ - if (ZEND_TYPE_HAS_NAME(type)) { - zend_string_addref(ZEND_TYPE_NAME(type)); + if (ZEND_TYPE_HAS_SIMPLE_PNR(type)) { + zend_string_addref(ZEND_TYPE_PNR_SIMPLE_NAME(type)); } } /* }}} */ @@ -2639,7 +2640,7 @@ ZEND_METHOD(ReflectionParameter, getClass) GET_REFLECTION_OBJECT_PTR(param); // TODO: This is going to return null for union types, which is rather odd. - if (ZEND_TYPE_HAS_NAME(param->arg_info->type)) { + if (ZEND_TYPE_HAS_SIMPLE_PNR(param->arg_info->type)) { /* Class name is stored as a string, we might also get "self" or "parent" * - For "self", simply use the function scope. If scope is NULL then * the function is global and thus self does not make any sense @@ -2654,7 +2655,7 @@ ZEND_METHOD(ReflectionParameter, getClass) */ zend_string *class_name; - class_name = ZEND_TYPE_NAME(param->arg_info->type); + class_name = ZEND_TYPE_PNR_SIMPLE_NAME(param->arg_info->type); if (zend_string_equals_literal_ci(class_name, "self")) { ce = param->fptr->common.scope; if (!ce) { @@ -2669,12 +2670,12 @@ ZEND_METHOD(ReflectionParameter, getClass) "Parameter uses \"parent\" as type but function is not a class member"); RETURN_THROWS(); } - if (!ce->parent) { + if (!ce->num_parents) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Parameter uses \"parent\" as type although class does not have a parent"); RETURN_THROWS(); } - ce = ce->parent; + ce = ce->parents[0]->ce; } else { ce = zend_lookup_class(class_name); if (!ce) { @@ -2961,7 +2962,9 @@ ZEND_METHOD(ReflectionParameter, getDefaultValueConstantName) } else if (ast->kind == ZEND_AST_CONSTANT_CLASS) { RETVAL_STRINGL("__CLASS__", sizeof("__CLASS__")-1); } else if (ast->kind == ZEND_AST_CLASS_CONST) { - zend_string *class_name = zend_ast_get_str(ast->child[0]); + zend_ast *class_ast = ast->child[0]; + ZEND_ASSERT(class_ast->child[1] == NULL && "Generic params not supported yet"); + zend_string *class_name = zend_ast_get_str(class_ast->child[0]); zend_string *const_name = zend_ast_get_str(ast->child[1]); RETVAL_NEW_STR(zend_string_concat3( ZSTR_VAL(class_name), ZSTR_LEN(class_name), @@ -3019,7 +3022,7 @@ ZEND_METHOD(ReflectionType, allowsNull) /* }}} */ /* For BC with iterable for named types */ -static zend_string *zend_named_reflection_type_to_string(zend_type type) { +static zend_string *zend_named_reflection_type_to_string(zend_type type, const zend_class_entry *scope) { if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) { zend_string *iterable = ZSTR_KNOWN(ZEND_STR_ITERABLE); if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_NULL) { @@ -3027,12 +3030,12 @@ static zend_string *zend_named_reflection_type_to_string(zend_type type) { } return iterable; } - return zend_type_to_string(type); + return zend_type_to_string(type, scope); } static zend_string *zend_type_to_string_without_null(zend_type type) { ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; - return zend_named_reflection_type_to_string(type); + return zend_named_reflection_type_to_string(type, NULL); } /* {{{ Return the text of the type hint */ @@ -3046,7 +3049,7 @@ ZEND_METHOD(ReflectionType, __toString) } GET_REFLECTION_OBJECT_PTR(param); - RETURN_STR(zend_named_reflection_type_to_string(param->type)); + RETURN_STR(zend_named_reflection_type_to_string(param->type, NULL)); } /* }}} */ @@ -3064,7 +3067,7 @@ ZEND_METHOD(ReflectionNamedType, getName) if (param->legacy_behavior) { RETURN_STR(zend_type_to_string_without_null(param->type)); } - RETURN_STR(zend_named_reflection_type_to_string(param->type)); + RETURN_STR(zend_named_reflection_type_to_string(param->type, NULL)); } /* }}} */ @@ -3117,14 +3120,17 @@ ZEND_METHOD(ReflectionUnionType, getTypes) GET_REFLECTION_OBJECT_PTR(param); array_init(return_value); - if (ZEND_TYPE_HAS_LIST(param->type)) { - zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { - append_type(return_value, *list_type); - } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(param->type)) { - zend_string *name = ZEND_TYPE_NAME(param->type); - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0)); + if (ZEND_TYPE_IS_COMPLEX(param->type)) { + if (ZEND_TYPE_HAS_LIST(param->type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { + append_type(return_value, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + } else { + zend_type type = param->type; + ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_MAY_BE_MASK; + append_type(return_value, type); + } } type_mask = ZEND_TYPE_PURE_MASK(param->type); @@ -4232,7 +4238,8 @@ ZEND_METHOD(ReflectionClass, setStaticPropertyValue) } } - if (ZEND_TYPE_IS_SET(prop_info->type) && !zend_verify_property_type(prop_info, value, 0)) { + if (ZEND_TYPE_IS_SET(prop_info->type) + && !zend_verify_property_type(NULL, prop_info, value, 0)) { return; } @@ -5284,8 +5291,8 @@ ZEND_METHOD(ReflectionClass, getParentClass) } GET_REFLECTION_OBJECT_PTR(ce); - if (ce->parent) { - zend_reflection_class_factory(ce->parent, return_value); + if (ce->num_parents) { + zend_reflection_class_factory(ce->parents[0]->ce, return_value); } else { RETURN_FALSE; } diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index 020b294052df3..deab0531f1603 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -489,7 +489,7 @@ static zend_class_entry *register_class_SoapServer(void) ZVAL_NULL(&property___soap_fault_default_value); zend_string *property___soap_fault_name = zend_string_init("__soap_fault", sizeof("__soap_fault") - 1, 1); zend_string *property___soap_fault_class_SoapFault = zend_string_init("SoapFault", sizeof("SoapFault")-1, 1); - zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); zend_string_release(property___soap_fault_name); return class_entry; @@ -692,7 +692,7 @@ static zend_class_entry *register_class_SoapClient(void) ZVAL_NULL(&property___soap_fault_default_value); zend_string *property___soap_fault_name = zend_string_init("__soap_fault", sizeof("__soap_fault") - 1, 1); zend_string *property___soap_fault_class_SoapFault = zend_string_init("SoapFault", sizeof("SoapFault")-1, 1); - zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property___soap_fault_name, &property___soap_fault_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property___soap_fault_class_SoapFault, 0, MAY_BE_NULL)); zend_string_release(property___soap_fault_name); zval property___last_request_default_value; diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index f3f2c7c50a41e..da60564ad6687 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -71,7 +71,7 @@ static zend_class_entry * spl_find_ce_by_name(zend_string *name, bool autoload) PHP_FUNCTION(class_parents) { zval *obj; - zend_class_entry *parent_class, *ce; + zend_class_entry *ce; bool autoload = 1; /* We do not use Z_PARAM_OBJ_OR_STR here to be able to exclude int, float, and bool which are bogus class names */ @@ -93,10 +93,8 @@ PHP_FUNCTION(class_parents) } array_init(return_value); - parent_class = ce->parent; - while (parent_class) { - spl_add_class_name(return_value, parent_class, 0, 0); - parent_class = parent_class->parent; + for (uint32_t i = 0; i < ce->num_parents; i++) { + spl_add_class_name(return_value, ce->parents[i]->ce, 0, 0); } } /* }}} */ diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 4a9a5f59f5c13..3de8df9d11968 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -144,10 +144,10 @@ static void spl_array_object_free_storage(zend_object *object) static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) { spl_array_object *intern; - zend_class_entry *parent = class_type; + zend_class_entry *parent; int inherited = 0; - intern = zend_object_alloc(sizeof(spl_array_object), parent); + intern = zend_object_alloc(sizeof(spl_array_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -181,13 +181,11 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o array_init(&intern->array); } - while (parent) { - if (parent == spl_ce_ArrayIterator || parent == spl_ce_RecursiveArrayIterator || parent == spl_ce_ArrayObject) { - break; - } - parent = parent->parent; - inherited = 1; - } + parent = zend_class_entry_get_root(class_type); + + inherited = class_type != spl_ce_ArrayIterator + && class_type != spl_ce_ArrayObject + && class_type != spl_ce_RecursiveArrayIterator; ZEND_ASSERT(parent); diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 74dc3b8c99fa1..3cbb5704efaeb 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -309,10 +309,9 @@ static void spl_dllist_object_free_storage(zend_object *object) /* {{{ */ static zend_object *spl_dllist_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) /* {{{ */ { spl_dllist_object *intern; - zend_class_entry *parent = class_type; int inherited = 0; - intern = zend_object_alloc(sizeof(spl_dllist_object), parent); + intern = zend_object_alloc(sizeof(spl_dllist_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -342,43 +341,34 @@ static zend_object *spl_dllist_object_new_ex(zend_class_entry *class_type, zend_ SPL_LLIST_CHECK_ADDREF(intern->traverse_pointer); } - while (parent) { - if (parent == spl_ce_SplStack) { - intern->flags |= (SPL_DLLIST_IT_FIX | SPL_DLLIST_IT_LIFO); - } else if (parent == spl_ce_SplQueue) { - intern->flags |= SPL_DLLIST_IT_FIX; - } - - if (parent == spl_ce_SplDoublyLinkedList) { - break; - } - - parent = parent->parent; - inherited = 1; + inherited = class_type != spl_ce_SplDoublyLinkedList + && class_type != spl_ce_SplStack && class_type != spl_ce_SplQueue; + if (instanceof_function(class_type, spl_ce_SplStack)) { + intern->flags |= (SPL_DLLIST_IT_FIX | SPL_DLLIST_IT_LIFO); + } else if (instanceof_function(class_type, spl_ce_SplQueue)) { + intern->flags |= SPL_DLLIST_IT_FIX; } - ZEND_ASSERT(parent); - if (inherited) { intern->fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1); - if (intern->fptr_offset_get->common.scope == parent) { + if (intern->fptr_offset_get->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_get = NULL; } intern->fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1); - if (intern->fptr_offset_set->common.scope == parent) { + if (intern->fptr_offset_set->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_set = NULL; } intern->fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1); - if (intern->fptr_offset_has->common.scope == parent) { + if (intern->fptr_offset_has->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_has = NULL; } intern->fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1); - if (intern->fptr_offset_del->common.scope == parent) { + if (intern->fptr_offset_del->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_offset_del = NULL; } /* Find count() method */ intern->fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); - if (intern->fptr_count->common.scope == parent) { + if (intern->fptr_count->common.scope == spl_ce_SplDoublyLinkedList) { intern->fptr_count = NULL; } } diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index dbdc08c5a87ca..d59033d420227 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -277,10 +277,9 @@ static void spl_fixedarray_object_free_storage(zend_object *object) static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig) { spl_fixedarray_object *intern; - zend_class_entry *parent = class_type; bool inherited = false; - intern = zend_object_alloc(sizeof(spl_fixedarray_object), parent); + intern = zend_object_alloc(sizeof(spl_fixedarray_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -290,21 +289,12 @@ static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, z spl_fixedarray_copy_ctor(&intern->array, &other->array); } - while (parent) { - if (parent == spl_ce_SplFixedArray) { - break; - } - - parent = parent->parent; - inherited = true; - } - - ZEND_ASSERT(parent); + inherited = class_type != spl_ce_SplFixedArray; if (UNEXPECTED(inherited)) { /* Find count() method */ zend_function *fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); - if (fptr_count->common.scope == parent) { + if (fptr_count->common.scope == spl_ce_SplFixedArray) { fptr_count = NULL; } intern->fptr_count = fptr_count; diff --git a/ext/spl/spl_functions.c b/ext/spl/spl_functions.c index 0fcf389667355..25d81083584d0 100644 --- a/ext/spl/spl_functions.c +++ b/ext/spl/spl_functions.c @@ -70,9 +70,8 @@ void spl_add_classes(zend_class_entry *pce, zval *list, bool sub, int allow, int spl_add_class_name(list, pce, allow, ce_flags); if (sub) { spl_add_interfaces(list, pce, allow, ce_flags); - while (pce->parent) { - pce = pce->parent; - spl_add_classes(pce, list, sub, allow, ce_flags); + for (uint32_t i = 0; i < pce->num_parents; i++) { + spl_add_classes(pce->parents[i]->ce, list, sub, allow, ce_flags); } } } diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 5d61741425185..0780961c49fa9 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -406,10 +406,10 @@ static void spl_heap_object_free_storage(zend_object *object) /* {{{ */ static zend_object *spl_heap_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) /* {{{ */ { spl_heap_object *intern; - zend_class_entry *parent = class_type; + zend_class_entry *parent; int inherited = 0; - intern = zend_object_alloc(sizeof(spl_heap_object), parent); + intern = zend_object_alloc(sizeof(spl_heap_object), class_type); zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); @@ -430,23 +430,23 @@ static zend_object *spl_heap_object_new_ex(zend_class_entry *class_type, zend_ob return &intern->std; } - while (parent) { - if (parent == spl_ce_SplPriorityQueue) { - intern->heap = spl_ptr_heap_init(spl_ptr_pqueue_elem_cmp, spl_ptr_heap_pqueue_elem_ctor, spl_ptr_heap_pqueue_elem_dtor, sizeof(spl_pqueue_elem)); - intern->flags = SPL_PQUEUE_EXTR_DATA; - break; - } - - if (parent == spl_ce_SplMinHeap || parent == spl_ce_SplMaxHeap - || parent == spl_ce_SplHeap) { - intern->heap = spl_ptr_heap_init( - parent == spl_ce_SplMinHeap ? spl_ptr_heap_zval_min_cmp : spl_ptr_heap_zval_max_cmp, - spl_ptr_heap_zval_ctor, spl_ptr_heap_zval_dtor, sizeof(zval)); - break; + parent = zend_class_entry_get_root(class_type); + inherited = class_type != spl_ce_SplPriorityQueue + && class_type != spl_ce_SplMinHeap && spl_ce_SplMaxHeap; + if (parent == spl_ce_SplPriorityQueue) { + intern->heap = spl_ptr_heap_init(spl_ptr_pqueue_elem_cmp, spl_ptr_heap_pqueue_elem_ctor, spl_ptr_heap_pqueue_elem_dtor, sizeof(spl_pqueue_elem)); + intern->flags = SPL_PQUEUE_EXTR_DATA; + } else { + ZEND_ASSERT(parent == spl_ce_SplHeap); + if (instanceof_function(class_type, spl_ce_SplMinHeap)) { + parent = spl_ce_SplMinHeap; + } else if (instanceof_function(class_type, spl_ce_SplMaxHeap)) { + parent = spl_ce_SplMaxHeap; } - parent = parent->parent; - inherited = 1; + intern->heap = spl_ptr_heap_init( + parent == spl_ce_SplMinHeap ? spl_ptr_heap_zval_min_cmp : spl_ptr_heap_zval_max_cmp, + spl_ptr_heap_zval_ctor, spl_ptr_heap_zval_dtor, sizeof(zval)); } ZEND_ASSERT(parent); diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index b996764c1aca1..e9ccb36677794 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -254,9 +254,8 @@ static void spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjec static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zend_object *orig) /* {{{ */ { spl_SplObjectStorage *intern; - zend_class_entry *parent = class_type; - intern = emalloc(sizeof(spl_SplObjectStorage) + zend_object_properties_size(parent)); + intern = emalloc(sizeof(spl_SplObjectStorage) + zend_object_properties_size(class_type)); memset(intern, 0, sizeof(spl_SplObjectStorage) - sizeof(zval)); intern->pos = 0; @@ -265,35 +264,29 @@ static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zend zend_hash_init(&intern->storage, 0, NULL, spl_object_storage_dtor, 0); - while (parent) { - if (parent == spl_ce_SplObjectStorage) { - /* Possible optimization: Cache these results with a map from class entry to IS_NULL/IS_PTR. - * Or maybe just a single item with the result for the most recently loaded subclass. */ - if (class_type != spl_ce_SplObjectStorage) { - zend_function *get_hash = zend_hash_str_find_ptr(&class_type->function_table, "gethash", sizeof("gethash") - 1); - if (get_hash->common.scope != spl_ce_SplObjectStorage) { - intern->fptr_get_hash = get_hash; - } - if (intern->fptr_get_hash != NULL || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetget) || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetexists)) { - intern->flags |= SOS_OVERRIDDEN_READ_DIMENSION; - } - - if (intern->fptr_get_hash != NULL || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetset)) { - intern->flags |= SOS_OVERRIDDEN_WRITE_DIMENSION; - } - - if (intern->fptr_get_hash != NULL || - SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetunset)) { - intern->flags |= SOS_OVERRIDDEN_UNSET_DIMENSION; - } - } - break; + if (class_type != spl_ce_SplObjectStorage + && zend_class_entry_get_root(class_type) == spl_ce_SplObjectStorage) { + /* Possible optimization: Cache these results with a map from class entry to IS_NULL/IS_PTR. + * Or maybe just a single item with the result for the most recently loaded subclass. */ + zend_function *get_hash = zend_hash_str_find_ptr(&class_type->function_table, "gethash", sizeof("gethash") - 1); + if (get_hash->common.scope != spl_ce_SplObjectStorage) { + intern->fptr_get_hash = get_hash; + } + if (intern->fptr_get_hash != NULL || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetget) || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetexists)) { + intern->flags |= SOS_OVERRIDDEN_READ_DIMENSION; } - parent = parent->parent; + if (intern->fptr_get_hash != NULL || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetset)) { + intern->flags |= SOS_OVERRIDDEN_WRITE_DIMENSION; + } + + if (intern->fptr_get_hash != NULL || + SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zf_offsetunset)) { + intern->flags |= SOS_OVERRIDDEN_UNSET_DIMENSION; + } } if (orig) { diff --git a/ext/standard/var.c b/ext/standard/var.c index 2975f165789e5..686ecd9ee7521 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -76,7 +76,7 @@ static void php_object_property_dump(zend_property_info *prop_info, zval *zv, ze if (Z_TYPE_P(zv) == IS_UNDEF) { ZEND_ASSERT(ZEND_TYPE_IS_SET(prop_info->type)); - zend_string *type_str = zend_type_to_string(prop_info->type); + zend_string *type_str = zend_type_to_string(prop_info->type, prop_info->ce); php_printf("%*cuninitialized(%s)\n", level + 1, ' ', ZSTR_VAL(type_str)); zend_string_release(type_str); @@ -266,7 +266,7 @@ static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, z ZEND_PUTS("]=>\n"); } if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) { - zend_string *type_str = zend_type_to_string(prop_info->type); + zend_string *type_str = zend_type_to_string(prop_info->type, prop_info->ce); php_printf("%*cuninitialized(%s)\n", level + 1, ' ', ZSTR_VAL(type_str)); zend_string_release(type_str); diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index a050fb5f74a70..3f3d7b4064bc1 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -689,7 +689,7 @@ second_try: } if (UNEXPECTED(info)) { - if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) { + if (!zend_verify_prop_assignable_by_ref(obj, info, data, /* strict */ 1)) { zval_ptr_dtor(data); ZVAL_UNDEF(data); goto failure; diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index b6d27d7a506f2..e3f7bc16f569c 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -576,7 +576,7 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e ZVAL_NULL(&property_classProp_default_value); zend_string *property_classProp_name = zend_string_init("classProp", sizeof("classProp") - 1, 1); zend_string *property_classProp_class_stdClass = zend_string_init("stdClass", sizeof("stdClass")-1, 1); - zend_declare_typed_property(class_entry, property_classProp_name, &property_classProp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_classProp_class_stdClass, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_classProp_name, &property_classProp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classProp_class_stdClass, 0, MAY_BE_NULL)); zend_string_release(property_classProp_name); zval property_classUnionProp_default_value; @@ -586,8 +586,8 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e zend_string *property_classUnionProp_class_Iterator = zend_string_init("Iterator", sizeof("Iterator") - 1, 1); zend_type_list *property_classUnionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); property_classUnionProp_type_list->num_types = 2; - property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_stdClass, 0, 0); - property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Iterator, 0, 0); + property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_stdClass, 0, 0); + property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_Iterator, 0, 0); zend_type property_classUnionProp_type = ZEND_TYPE_INIT_UNION(property_classUnionProp_type_list, MAY_BE_NULL); zend_declare_typed_property(class_entry, property_classUnionProp_name, &property_classUnionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classUnionProp_type); zend_string_release(property_classUnionProp_name); @@ -599,8 +599,8 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e zend_string *property_classIntersectionProp_class_Countable = zend_string_init("Countable", sizeof("Countable") - 1, 1); zend_type_list *property_classIntersectionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); property_classIntersectionProp_type_list->num_types = 2; - property_classIntersectionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classIntersectionProp_class_Traversable, 0, 0); - property_classIntersectionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classIntersectionProp_class_Countable, 0, 0); + property_classIntersectionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classIntersectionProp_class_Traversable, 0, 0); + property_classIntersectionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classIntersectionProp_class_Countable, 0, 0); zend_type property_classIntersectionProp_type = ZEND_TYPE_INIT_INTERSECTION(property_classIntersectionProp_type_list, 0); zend_declare_typed_property(class_entry, property_classIntersectionProp_name, &property_classIntersectionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classIntersectionProp_type); zend_string_release(property_classIntersectionProp_name); @@ -708,8 +708,8 @@ static zend_class_entry *register_class__ZendTestTrait(void) zend_string *property_classUnionProp_class_Countable = zend_string_init("Countable", sizeof("Countable") - 1, 1); zend_type_list *property_classUnionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); property_classUnionProp_type_list->num_types = 2; - property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Traversable, 0, 0); - property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Countable, 0, 0); + property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_Traversable, 0, 0); + property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_classUnionProp_class_Countable, 0, 0); zend_type property_classUnionProp_type = ZEND_TYPE_INIT_UNION(property_classUnionProp_type_list, 0); zend_declare_typed_property(class_entry, property_classUnionProp_name, &property_classUnionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classUnionProp_type); zend_string_release(property_classUnionProp_name); @@ -983,7 +983,7 @@ static zend_class_entry *register_class_ZendTestNS2_Foo(void) ZVAL_UNDEF(&property_foo_default_value); zend_string *property_foo_name = zend_string_init("foo", sizeof("foo") - 1, 1); zend_string *property_foo_class_ZendTestNS2_ZendSubNS_Foo = zend_string_init("ZendTestNS2\\ZendSubNS\\Foo", sizeof("ZendTestNS2\\ZendSubNS\\Foo")-1, 1); - zend_declare_typed_property(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_foo_class_ZendTestNS2_ZendSubNS_Foo, 0, 0)); + zend_declare_typed_property(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS_CONST(property_foo_class_ZendTestNS2_ZendSubNS_Foo, 0, 0)); zend_string_release(property_foo_name); return class_entry; diff --git a/sapi/phpdbg/phpdbg_info.c b/sapi/phpdbg/phpdbg_info.c index 0a1e7570a493c..f98585b231623 100644 --- a/sapi/phpdbg/phpdbg_info.c +++ b/sapi/phpdbg/phpdbg_info.c @@ -402,13 +402,11 @@ PHPDBG_INFO(classes) /* {{{ */ ZEND_HASH_PACKED_FOREACH_PTR(&classes, ce) { phpdbg_print_class_name(ce); - if (ce->parent) { - zend_class_entry *pce; - pce = ce->parent; - do { + if (ce->num_parents) { + for (uint32_t i = 0; i < ce->num_parents; i++) { phpdbg_out("|-------- "); - phpdbg_print_class_name(pce); - } while ((pce = pce->parent)); + phpdbg_print_class_name(ce->parents[i]->ce); + } } if (ce->info.user.filename) { From a2865296a693cc007d063c454423d307b1b38222 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 8 Jan 2024 13:49:02 +0100 Subject: [PATCH 02/10] Support instantiation of concrete generic classes --- .../generic_param_with_over_types.phpt | 66 +++ Zend/tests/generics/type_params_in_types.phpt | 54 +++ Zend/tests/generics/unbound_params.phpt | 119 +++++ Zend/zend.c | 7 +- Zend/zend_API.c | 167 ++++++-- Zend/zend_API.h | 11 +- Zend/zend_ast.c | 2 +- Zend/zend_builtin_functions.c | 11 +- Zend/zend_closures.c | 13 +- Zend/zend_compile.c | 39 +- Zend/zend_constants.c | 6 +- Zend/zend_enum.h | 6 +- Zend/zend_exceptions.c | 17 +- Zend/zend_execute.c | 104 +++-- Zend/zend_execute.h | 3 +- Zend/zend_execute_API.c | 20 +- Zend/zend_gc.c | 4 +- Zend/zend_interfaces.c | 4 +- Zend/zend_object_handlers.c | 136 +++--- Zend/zend_objects.c | 67 +-- Zend/zend_objects.h | 2 + Zend/zend_objects_API.c | 4 +- Zend/zend_objects_API.h | 4 +- Zend/zend_operators.c | 2 +- Zend/zend_types.h | 24 +- Zend/zend_vm_def.h | 56 ++- Zend/zend_vm_execute.h | 405 ++++++++++-------- Zend/zend_weakrefs.c | 2 +- ext/date/php_date.c | 12 +- ext/hash/hash.c | 2 +- ext/json/json_encoder.c | 2 +- ext/random/engine_mt19937.c | 10 +- ext/random/random.c | 2 +- ext/random/randomizer.c | 6 +- ext/reflection/php_reflection.c | 20 +- ext/spl/spl_array.c | 24 +- ext/spl/spl_directory.c | 4 +- ext/spl/spl_dllist.c | 4 +- ext/spl/spl_fixedarray.c | 14 +- ext/spl/spl_heap.c | 6 +- ext/spl/spl_iterators.c | 6 +- ext/spl/spl_observer.c | 24 +- ext/standard/array.c | 4 +- ext/standard/basic_functions.c | 13 +- ext/standard/browscap.c | 2 +- ext/standard/incomplete_class.c | 2 +- ext/standard/type.c | 6 +- ext/standard/var.c | 4 +- ext/standard/var_unserializer.re | 16 +- sapi/phpdbg/phpdbg_prompt.c | 10 +- sapi/phpdbg/phpdbg_utils.c | 2 +- 51 files changed, 1009 insertions(+), 541 deletions(-) create mode 100644 Zend/tests/generics/unbound_params.phpt diff --git a/Zend/tests/generics/generic_param_with_over_types.phpt b/Zend/tests/generics/generic_param_with_over_types.phpt index d37945d3324f3..32eb3772da6ab 100644 --- a/Zend/tests/generics/generic_param_with_over_types.phpt +++ b/Zend/tests/generics/generic_param_with_over_types.phpt @@ -54,6 +54,56 @@ try { echo $e->getMessage(), "\n"; } +echo "\n"; + +class ConcreteTest4 { + public ?T $prop; + public function method(?T $param) { + var_dump($param); + } +} + +$obj = new ConcreteTest4; +$obj->method([]); +$obj->method(null); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +class ConcreteTest5 { + public T|int $prop; + public function method(T|int $param) { + var_dump($param); + } +} + +$obj = new ConcreteTest5; +$obj->method([]); +$obj->method(42); +$obj->method("42"); + +echo "\n"; + +class ConcreteTest6 { + public T|stdClass $prop; + public function method(T|stdClass $param) { + var_dump($param); + } +} + +$obj = new ConcreteTest6; +$obj->method([]); +$obj->method(new stdClass); +try { + $obj->method("string"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + ?> --EXPECTF-- array(0) { @@ -71,3 +121,19 @@ array(0) { object(stdClass)#3 (0) { } AbstractTest3::method(): Argument #1 ($param) must be of type T|stdClass (where T = array), string given, called in %s on line %d + +array(0) { +} +NULL +ConcreteTest4::method(): Argument #1 ($param) must be of type ?T (where T = array), string given, called in %s on line %d + +array(0) { +} +int(42) +int(42) + +array(0) { +} +object(stdClass)#3 (0) { +} +ConcreteTest6::method(): Argument #1 ($param) must be of type T|stdClass (where T = array), string given, called in %s on line %d diff --git a/Zend/tests/generics/type_params_in_types.phpt b/Zend/tests/generics/type_params_in_types.phpt index 01de96896d075..38f40b2af84b3 100644 --- a/Zend/tests/generics/type_params_in_types.phpt +++ b/Zend/tests/generics/type_params_in_types.phpt @@ -29,6 +29,18 @@ class ConcreteIntDefaulted extends AbstractDefaulted { class ConcreteStringDefaulted extends AbstractDefaulted { } +class ConcretePassthru { + public function method(T $param) { + var_dump($param); + } +} + +class ConcreteDefaulted { + public function method(T $param) { + var_dump($param); + } +} + function test(AbstractTest $test) { $test->method(42); } @@ -63,6 +75,40 @@ try { echo $e->getMessage(), "\n"; } +function test4(ConcretePassthru $test) { + $test->method(42); +} + +test4(new ConcretePassthru); +try { + test4(new ConcretePassthru); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +function test5(ConcreteDefaulted $test) { + $test->method(42); +} + +function test6(ConcreteDefaulted $test) { + $test->method(42); +} + +test5(new ConcreteDefaulted); +test5(new ConcreteDefaulted); +test6(new ConcreteDefaulted); +test6(new ConcreteDefaulted); +try { + test5(new ConcreteDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + test6(new ConcreteDefaulted); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + ?> --EXPECTF-- int(42) @@ -73,3 +119,11 @@ int(42) int(42) test2(): Argument #1 ($test) must be of type AbstractDefaulted, ConcreteStringDefaulted given, called in %s on line %d test3(): Argument #1 ($test) must be of type AbstractDefaulted, ConcreteStringDefaulted given, called in %s on line %d +int(42) +test4(): Argument #1 ($test) must be of type ConcretePassthru, ConcretePassthru given, called in %s on line %d +int(42) +int(42) +int(42) +int(42) +test5(): Argument #1 ($test) must be of type ConcreteDefaulted, ConcreteDefaulted given, called in %s on line %d +test6(): Argument #1 ($test) must be of type ConcreteDefaulted, ConcreteDefaulted given, called in %s on line %d diff --git a/Zend/tests/generics/unbound_params.phpt b/Zend/tests/generics/unbound_params.phpt new file mode 100644 index 0000000000000..680023986c729 --- /dev/null +++ b/Zend/tests/generics/unbound_params.phpt @@ -0,0 +1,119 @@ +--TEST-- +Test instantiation of class with unbound params +--FILE-- + { + public function method(T $param) { + var_dump($param); + } +} + +class Test2 { + public function method(T $t, U $u) { + var_dump($t, $u); + } +} + +class Test3 { + public function method(T $param) { + var_dump($param); + } +} + +class AbstractTest4 { + public function method1(T $t, U $u) { + var_dump($t, $u); + } +} + +class Test4 extends AbstractTest4 { + public function method2(T $param) { + var_dump($param); + } +} + +$test = new Test1(); +$test->method(42); +try { + $test->method([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test2(); +$test->method(42,'hello'); +try { + $test->method([],'hello'); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $test->method(42,[]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test3(); +$test->method(42); +try { + $test->method([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test3(); +$test->method('hello'); +try { + $test->method([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\n"; + +$test = new Test4(); +$test->method1(42, 'hello'); +$test->method2(42); +try { + $test->method1([], 'hello'); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $test->method1(42,[]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $test->method2([]); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +--EXPECTF-- +int(42) +Test1::method(): Argument #1 ($param) must be of type T (where T = int), array given, called in %s on line %d + +int(42) +string(5) "hello" +Test2::method(): Argument #1 ($t) must be of type T (where T = int), array given, called in %s on line %d +Test2::method(): Argument #2 ($u) must be of type U (where U = string), array given, called in %s on line %d + +int(42) +Test3::method(): Argument #1 ($param) must be of type T (where T = int), array given, called in %s on line %d + +string(5) "hello" +Test3::method(): Argument #1 ($param) must be of type T (where T = string), array given, called in %s on line %d + +int(42) +string(5) "hello" +int(42) +AbstractTest4::method1(): Argument #1 ($t) must be of type T (where T = int), array given, called in %s on line %d +AbstractTest4::method1(): Argument #2 ($u) must be of type U (where U = string), array given, called in %s on line %d +Test4::method2(): Argument #1 ($param) must be of type T (where T = int), array given, called in %s on line %d diff --git a/Zend/zend.c b/Zend/zend.c index 3277aa55f0ae7..7578ce5af5280 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -26,6 +26,7 @@ #include "zend_exceptions.h" #include "zend_builtin_functions.h" #include "zend_ini.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_dtrace.h" #include "zend_virtual_cwd.h" @@ -556,13 +557,13 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* smart_str_appends(buf, ZSTR_VAL(class_name)); zend_string_release_ex(class_name, 0); - if (!(zobj->ce->ce_flags & ZEND_ACC_ENUM)) { + if (!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM)) { smart_str_appends(buf, " Object\n"); } else { smart_str_appends(buf, " Enum"); - if (zobj->ce->enum_backing_type != IS_UNDEF) { + if (OBJ_CE(zobj)->enum_backing_type != IS_UNDEF) { smart_str_appendc(buf, ':'); - smart_str_appends(buf, zend_get_type_by_const(zobj->ce->enum_backing_type)); + smart_str_appends(buf, zend_get_type_by_const(OBJ_CE(zobj)->enum_backing_type)); } smart_str_appendc(buf, '\n'); } diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 0a597753a1baa..5c9ce7ce19928 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -32,6 +32,7 @@ #include "zend_ini.h" #include "zend_enum.h" #include "zend_observer.h" +#include "zend_types.h" #include @@ -1640,13 +1641,13 @@ ZEND_API void object_properties_init(zend_object *object, zend_class_entry *clas ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties) /* {{{ */ { object->properties = properties; - if (object->ce->default_properties_count) { + if (OBJ_CE(object)->default_properties_count) { zval *prop; zend_string *key; zend_property_info *property_info; ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(properties, key, prop) { - property_info = zend_get_property_info(object->ce, key, 1); + property_info = zend_get_property_info(OBJ_CE(object), key, 1); if (property_info != ZEND_WRONG_PROPERTY_INFO && property_info && (property_info->flags & ZEND_ACC_STATIC) == 0) { @@ -1690,14 +1691,14 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) EG(fake_scope) = zend_lookup_class(cname); zend_string_release_ex(cname, 0); } - property_info = zend_get_property_info(object->ce, pname, 1); + property_info = zend_get_property_info(OBJ_CE(object), pname, 1); zend_string_release_ex(pname, 0); EG(fake_scope) = prev_scope; } else { property_info = ZEND_WRONG_PROPERTY_INFO; } } else { - property_info = zend_get_property_info(object->ce, key, 1); + property_info = zend_get_property_info(OBJ_CE(object), key, 1); } if (property_info != ZEND_WRONG_PROPERTY_INFO && property_info && @@ -1711,13 +1712,13 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) zend_hash_update(object->properties, key, &tmp); } } else { - if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + if (UNEXPECTED(OBJ_CE(object)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", - ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); + ZSTR_VAL(OBJ_NAME(object)), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); return; - } else if (!(object->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + } else if (!(OBJ_CE(object)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", - ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); + ZSTR_VAL(OBJ_NAME(object)), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); } if (!object->properties) { @@ -1727,12 +1728,12 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) zval_add_ref(prop); } } else { - if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { - zend_throw_error(NULL, "Cannot create dynamic property %s::$" ZEND_LONG_FMT, ZSTR_VAL(object->ce->name), h); + if (UNEXPECTED(OBJ_CE(object)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_throw_error(NULL, "Cannot create dynamic property %s::$" ZEND_LONG_FMT, ZSTR_VAL(OBJ_CE(object)->name), h); return; - } else if (!(object->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + } else if (!(OBJ_CE(object)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { zend_error(E_DEPRECATED, "Creation of dynamic property %s::$" ZEND_LONG_FMT " is deprecated", - ZSTR_VAL(object->ce->name), h); + ZSTR_VAL(OBJ_CE(object)->name), h); } if (!object->properties) { @@ -1745,60 +1746,132 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) } /* }}} */ +ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, const zend_type_list *type_args) +{ + if (UNEXPECTED(type_args->num_types > ce->num_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at most", + ce->num_generic_params, ce->num_generic_params == 1 ? "" : "s", + type_args->num_types); + return NULL; + } + + if (UNEXPECTED(type_args->num_types < ce->num_required_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at least", + ce->num_required_generic_params, ce->num_required_generic_params == 1 ? "" : "s", + type_args->num_types); + return NULL; + } + + // TODO: re-use class references + zend_class_reference *class_ref = emalloc(ZEND_CLASS_REF_SIZE(ce->num_generic_params)); + class_ref->ce = ce; + + uint8_t param_id = 0; + + for (; param_id < type_args->num_types; param_id++) { + // TODO: check ce->generic_params[param_id].bound_type + class_ref->args.types[param_id] = type_args->types[param_id]; + } + + for (; param_id < ce->num_generic_params; param_id++) { + class_ref->args.types[param_id] = ce->generic_params[param_id].default_type; + } + + class_ref->args.num_types = param_id; + + return class_ref; +} + /* This function requires 'properties' to contain all props declared in the * class and all props being public. If only a subset is given or the class * has protected members then you need to merge the properties separately by * calling zend_merge_properties(). */ -static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */ -{ - if (UNEXPECTED(class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_ENUM))) { - if (class_type->ce_flags & ZEND_ACC_INTERFACE) { - zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(class_type->name)); - } else if (class_type->ce_flags & ZEND_ACC_TRAIT) { - zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(class_type->name)); - } else if (class_type->ce_flags & ZEND_ACC_ENUM) { - zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(class_type->name)); +static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_reference *class_ref, HashTable *properties) /* {{{ */ +{ + zend_class_entry *ce = class_ref->ce; + + if (UNEXPECTED(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_ENUM))) { + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(ce->name)); + } else if (ce->ce_flags & ZEND_ACC_TRAIT) { + zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(ce->name)); + } else if (ce->ce_flags & ZEND_ACC_ENUM) { + zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(ce->name)); } else { - zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(class_type->name)); + zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(ce->name)); } ZVAL_NULL(arg); Z_OBJ_P(arg) = NULL; return FAILURE; } - if (UNEXPECTED(!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(class_type) != SUCCESS)) { + if (UNEXPECTED(class_ref->args.num_types > ce->num_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + ce->num_required_generic_params == ce->num_generic_params ? "exactly" : "at most", + ce->num_generic_params, ce->num_generic_params == 1 ? "" : "s", + class_ref->args.num_types); + ZVAL_NULL(arg); + Z_OBJ_P(arg) = NULL; + return FAILURE; + } else if (UNEXPECTED(class_ref->args.num_types < ce->num_required_generic_params)) { + zend_throw_error(zend_ce_argument_count_error, + "Class %s expects %s %d generic argument%s, but %d provided", + ZSTR_VAL(ce->name), + class_ref->args.num_types == ce->num_generic_params ? "exactly" : "at least", + ce->num_required_generic_params, ce->num_required_generic_params == 1 ? "" : "s", + class_ref->args.num_types); + ZVAL_NULL(arg); + Z_OBJ_P(arg) = NULL; + return FAILURE; + } + + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { ZVAL_NULL(arg); Z_OBJ_P(arg) = NULL; return FAILURE; } } - if (class_type->create_object == NULL) { - zend_object *obj = zend_objects_new(class_type); + if (ce->create_object == NULL) { + zend_object *obj = zend_objects_new_ref(class_ref); ZVAL_OBJ(arg, obj); if (properties) { object_properties_init_ex(obj, properties); } else { - _object_properties_init(obj, class_type); + _object_properties_init(obj, ce); } } else { - ZVAL_OBJ(arg, class_type->create_object(class_type)); + ZVAL_OBJ(arg, ce->create_object(ce)); } return SUCCESS; } /* }}} */ -ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */ +ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_reference *class_ref, HashTable *properties) /* {{{ */ +{ + return _object_and_properties_init(arg, class_ref, properties); +} +/* }}} */ + +ZEND_API zend_result object_init_ref(zval *arg, zend_class_reference *class_ref) /* {{{ */ { - return _object_and_properties_init(arg, class_type, properties); + return _object_and_properties_init(arg, class_ref, NULL); } /* }}} */ ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type) /* {{{ */ { - return _object_and_properties_init(arg, class_type, NULL); + return object_init_ref(arg, ZEND_CE_TO_REF(class_type)); } /* }}} */ @@ -3573,7 +3646,8 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"self\" in callables is deprecated"); } - fcc->called_scope = zend_get_called_scope(frame); + zend_class_reference *class_ref = zend_get_called_scope(frame); + fcc->called_scope = class_ref ? class_ref->ce : NULL; if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope)) { fcc->called_scope = scope; } @@ -3592,7 +3666,8 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"parent\" in callables is deprecated"); } - fcc->called_scope = zend_get_called_scope(frame); + zend_class_reference *class_ref = zend_get_called_scope(frame); + fcc->called_scope = class_ref ? class_ref->ce : NULL; if (!fcc->called_scope || !instanceof_function(fcc->called_scope, scope->parents[0]->ce)) { fcc->called_scope = scope->parents[0]->ce; } @@ -3604,7 +3679,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc ret = 1; } } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_STATIC))) { - zend_class_entry *called_scope = zend_get_called_scope(frame); + zend_class_reference *called_scope = zend_get_called_scope(frame); if (!called_scope) { if (error) *error = estrdup("cannot access \"static\" when no class scope is active"); @@ -3612,8 +3687,8 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc if (!suppress_deprecation) { zend_error(E_DEPRECATED, "Use of \"static\" in callables is deprecated"); } - fcc->called_scope = called_scope; - fcc->calling_scope = called_scope; + fcc->called_scope = called_scope->ce; + fcc->calling_scope = called_scope->ce; if (!fcc->object) { fcc->object = zend_get_this_object(frame); } @@ -3627,15 +3702,15 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc zend_object *object = zend_get_this_object(frame); if (object && - instanceof_function(object->ce, scope) && + instanceof_function(OBJ_CE(object), scope) && instanceof_function(scope, ce)) { fcc->object = object; - fcc->called_scope = object->ce; + fcc->called_scope = OBJ_CE(object); } else { fcc->called_scope = ce; } } else { - fcc->called_scope = fcc->object ? fcc->object->ce : ce; + fcc->called_scope = fcc->object ? OBJ_CE(fcc->object) : ce; } *strict_class = 1; ret = 1; @@ -3733,15 +3808,15 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ zend_object *object = zend_get_this_object(frame); if (object && - instanceof_function(object->ce, scope) && + instanceof_function(OBJ_CE(object), scope) && instanceof_function(scope, fcc->calling_scope)) { fcc->object = object; - fcc->called_scope = object->ce; + fcc->called_scope = OBJ_CE(object); } else { fcc->called_scope = fcc->calling_scope; } } else { - fcc->called_scope = fcc->object ? fcc->object->ce : fcc->calling_scope; + fcc->called_scope = fcc->object ? OBJ_CE(fcc->object) : fcc->calling_scope; } strict_class = 1; } else if (!zend_is_callable_check_class(cname, scope, frame, fcc, &strict_class, error, suppress_deprecation || ce_org != NULL)) { @@ -3849,7 +3924,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ if (call_via_handler && !fcc->object) { zend_object *object = zend_get_this_object(frame); if (object && - instanceof_function(object->ce, fcc->calling_scope)) { + instanceof_function(OBJ_CE(object), fcc->calling_scope)) { fcc->object = object; } } @@ -3898,7 +3973,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ zend_string_release_ex(mname, 0); if (fcc->object) { - fcc->called_scope = fcc->object->ce; + fcc->called_scope = OBJ_CE(fcc->object); if (fcc->function_handler && (fcc->function_handler->common.fn_flags & ZEND_ACC_STATIC)) { fcc->object = NULL; @@ -3914,7 +3989,7 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj switch (Z_TYPE_P(callable)) { case IS_STRING: if (object) { - return zend_create_member_string(object->ce->name, Z_STR_P(callable)); + return zend_create_member_string(OBJ_NAME(object), Z_STR_P(callable)); } return zend_string_copy(Z_STR_P(callable)); @@ -3988,7 +4063,7 @@ ZEND_API bool zend_is_callable_at_frame( case IS_STRING: if (object) { fcc->object = object; - fcc->calling_scope = object->ce; + fcc->calling_scope = OBJ_CE(object); } if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index a9a5759443f05..7486962833b47 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -25,6 +25,7 @@ #include "zend_modules.h" #include "zend_list.h" #include "zend_operators.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_execute.h" #include "zend_type_info.h" @@ -531,11 +532,13 @@ ZEND_API const char *zend_get_type_by_const(int type); #define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) #define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) ZEND_API void object_init(zval *arg); -ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce); -ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties); +ZEND_API zend_result object_init_ref(zval *arg, zend_class_reference *class_ref); +ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type); +ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_reference *class_ref, HashTable *properties); ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type); ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties); ZEND_API void object_properties_load(zend_object *object, HashTable *properties); +ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, const zend_type_list *type_args); ZEND_API void zend_merge_properties(zval *obj, HashTable *properties); @@ -850,7 +853,7 @@ static zend_always_inline void zend_call_known_instance_method( zend_function *fn, zend_object *object, zval *retval_ptr, uint32_t param_count, zval *params) { - zend_call_known_function(fn, object, object->ce, retval_ptr, param_count, params, NULL); + zend_call_known_function(fn, object, OBJ_CE(object), retval_ptr, param_count, params, NULL); } static zend_always_inline void zend_call_known_instance_method_with_0_params( @@ -2514,7 +2517,7 @@ static zend_always_inline bool zend_parse_arg_obj_or_class_name( return *destination != NULL; } else if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { - *destination = Z_OBJ_P(arg)->ce; + *destination = Z_OBJCE_P(arg); } else if (allow_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) { *destination = NULL; } else { diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index f6307dc71e7f8..1c731f655c860 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1007,7 +1007,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( } zend_object *zobj = Z_OBJ(op1); - if (!(zobj->ce->ce_flags & ZEND_ACC_ENUM)) { + if (!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM)) { zend_throw_error(NULL, "Fetching properties on non-enums in constant expressions is not allowed"); zval_ptr_dtor_nogc(&op1); zval_ptr_dtor_nogc(&op2); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 3fc5de03fb6e7..75be4ef1667f3 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -31,6 +31,7 @@ #include "zend_generators.h" #include "zend_builtin_functions_arginfo.h" #include "zend_smart_str.h" +#include "zend_types.h" /* }}} */ @@ -579,7 +580,7 @@ ZEND_FUNCTION(get_class) /* {{{ Retrieves the "Late Static Binding" class name */ ZEND_FUNCTION(get_called_class) { - zend_class_entry *called_scope; + zend_class_reference *called_scope; ZEND_PARSE_PARAMETERS_NONE(); @@ -589,7 +590,7 @@ ZEND_FUNCTION(get_called_class) RETURN_THROWS(); } - RETURN_STR_COPY(called_scope->name); + RETURN_STR_COPY(called_scope->ce->name); } /* }}} */ @@ -773,7 +774,7 @@ ZEND_FUNCTION(get_object_vars) RETURN_EMPTY_ARRAY(); } - if (!zobj->ce->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) { + if (!OBJ_CE(zobj)->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) { /* fast copy */ if (EXPECTED(zobj->handlers == &std_object_handlers)) { RETURN_ARR(zend_proptable_to_symtable(properties, 0)); @@ -840,7 +841,7 @@ ZEND_FUNCTION(get_mangled_object_vars) } properties = zend_proptable_to_symtable(properties, - (obj->ce->default_properties_count || + (OBJ_CE(obj)->default_properties_count || obj->handlers != &std_object_handlers || GC_IS_RECURSIVE(properties))); RETURN_ARR(properties); @@ -1842,7 +1843,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int 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); + ZVAL_STR_COPY(&tmp, OBJ_CE(object)->name); } else { ZVAL_STR(&tmp, object->handlers->get_class_name(object)); } diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index f1b29af7967c9..c702f03d9c8bd 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -22,11 +22,13 @@ #include "zend_API.h" #include "zend_closures.h" #include "zend_exceptions.h" +#include "zend_execute.h" #include "zend_interfaces.h" #include "zend_objects.h" #include "zend_objects_API.h" #include "zend_globals.h" #include "zend_closures_arginfo.h" +#include "zend_types.h" typedef struct _zend_closure { zend_object std; @@ -140,7 +142,7 @@ ZEND_METHOD(Closure, call) closure = (zend_closure *) Z_OBJ_P(ZEND_THIS); newobj = Z_OBJ_P(newthis); - newclass = newobj->ce; + newclass = OBJ_CE(newobj); if (!zend_valid_closure_binding(closure, newthis, newclass)) { return; @@ -224,7 +226,7 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure); if (scope_obj) { - ce = scope_obj->ce; + ce = OBJ_CE(scope_obj); } else if (scope_str) { if (zend_string_equals(scope_str, ZSTR_KNOWN(ZEND_STR_STATIC))) { ce = closure->func.common.scope; @@ -318,7 +320,8 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ { } fcc.object = fci.object = Z_OBJ_P(ZEND_THIS); - fcc.called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *class_ref = zend_get_called_scope(EG(current_execute_data)); + fcc.called_scope = class_ref ? class_ref->ce : NULL; zend_call_function(&fci, &fcc); @@ -339,7 +342,7 @@ static zend_result zend_create_closure_from_callable(zval *return_value, zval *c mptr = fcc.function_handler; if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { /* For Closure::fromCallable([$closure, "__invoke"]) return $closure. */ - if (fcc.object && fcc.object->ce == zend_ce_closure + if (fcc.object && OBJ_CE(fcc.object) == zend_ce_closure && zend_string_equals(mptr->common.function_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) { RETVAL_OBJ_COPY(fcc.object); zend_free_trampoline(mptr); @@ -793,7 +796,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) { /* avoid infinity recursion, by taking handler from nested closure */ zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func)); - ZEND_ASSERT(nested->std.ce == zend_ce_closure); + ZEND_ASSERT(OBJ_CE(&nested->std) == zend_ce_closure); closure->orig_internal_handler = nested->orig_internal_handler; } else { closure->orig_internal_handler = closure->func.internal_function.handler; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index fc970e3c7eea6..c961766bae92d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -28,6 +28,7 @@ #include "zend_API.h" #include "zend_exceptions.h" #include "zend_interfaces.h" +#include "zend_types.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h" @@ -1455,9 +1456,9 @@ static zend_string *zend_type_to_string_impl( zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC); // During compilation of eval'd code the called scope refers to the scope calling the eval if (scope && !zend_is_compiling()) { - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *called_scope = zend_get_called_scope(EG(current_execute_data)); if (called_scope) { - name = called_scope->name; + name = called_scope->ce->name; } } str = add_type_string(str, name, /* is_intersection */ false); @@ -2877,7 +2878,7 @@ static inline void zend_set_class_name_op1(zend_op *opline, znode *class_node) / } /* }}} */ -static void zend_compile_class_ref(znode *result, zend_ast *class_ast, uint32_t fetch_flags) /* {{{ */ +static void zend_compile_class_ref_ex(znode *result, zend_ast *class_ast, uint32_t fetch_flags) /* {{{ */ { uint32_t fetch_type; @@ -2913,27 +2914,44 @@ static void zend_compile_class_ref(znode *result, zend_ast *class_ast, uint32_t return; } - zend_ast *name_ast = unwrap_non_generic_class_ref(class_ast); + zend_ast *name_ast = class_ast->child[0]; /* Fully qualified names are always default refs */ if (name_ast->attr == ZEND_NAME_FQ) { result->op_type = IS_CONST; - ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast)); + zend_string *name = zend_new_interned_string(zend_resolve_class_name_ast(name_ast)); + ZVAL_PNR(&result->u.constant, zend_compile_pnr(name, class_ast->child[1])); return; } fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) { result->op_type = IS_CONST; - ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast)); + zend_string *name = zend_new_interned_string(zend_resolve_class_name_ast(name_ast)); + ZVAL_PNR(&result->u.constant, zend_compile_pnr(name, class_ast->child[1])); } else { zend_ensure_valid_class_fetch_type(fetch_type); + if (class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } result->op_type = IS_UNUSED; result->u.op.num = fetch_type | fetch_flags; } } /* }}} */ +static void zend_compile_class_ref(znode *result, zend_ast *class_ast, uint32_t fetch_flags) /* {{{ */ +{ + if (class_ast->kind == ZEND_AST_CLASS_REF && class_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Generic type arguments are currently not supported here yet"); + } + + zend_compile_class_ref_ex(result, class_ast, fetch_flags); +} +/* }}} */ + static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *name_ast = ast->child[0]; @@ -4978,18 +4996,13 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ /* anon class declaration */ zend_compile_class_decl(&class_node, class_ast, 0); } else { - zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); + zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); } - opline = zend_emit_op(result, ZEND_NEW, NULL, NULL); + opline = zend_emit_op(result, ZEND_NEW, &class_node, NULL); if (class_node.op_type == IS_CONST) { - opline->op1_type = IS_CONST; - opline->op1.constant = zend_add_class_name_literal( - Z_STR(class_node.u.constant)); opline->op2.num = zend_alloc_cache_slot(); - } else { - SET_NODE(opline->op1, &class_node); } zend_compile_call_common(&ctor_result, args_ast, NULL, ast->lineno); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index f30ddb1f07522..3936b7b26f568 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -21,6 +21,7 @@ #include "zend_constants.h" #include "zend_exceptions.h" #include "zend_execute.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_operators.h" #include "zend_globals.h" @@ -327,11 +328,12 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * ce = scope->parents[0]->ce; } } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_STATIC))) { - ce = zend_get_called_scope(EG(current_execute_data)); - if (UNEXPECTED(!ce)) { + zend_class_reference *class_ref = zend_get_called_scope(EG(current_execute_data)); + if (UNEXPECTED(!class_ref)) { zend_throw_error(NULL, "Cannot access \"static\" when no class scope is active"); goto failure; } + ce = class_ref->ce; } else { ce = zend_fetch_class(class_name, flags); } diff --git a/Zend/zend_enum.h b/Zend/zend_enum.h index 7b58592d42bd5..f8b6f6da125d9 100644 --- a/Zend/zend_enum.h +++ b/Zend/zend_enum.h @@ -48,14 +48,14 @@ ZEND_API zend_result zend_enum_get_case_by_value(zend_object **result, zend_clas static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj) { - ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM); + ZEND_ASSERT(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM); return OBJ_PROP_NUM(zobj, 0); } static zend_always_inline zval *zend_enum_fetch_case_value(zend_object *zobj) { - ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM); - ZEND_ASSERT(zobj->ce->enum_backing_type != IS_UNDEF); + ZEND_ASSERT(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ENUM); + ZEND_ASSERT(OBJ_CE(zobj)->enum_backing_type != IS_UNDEF); return OBJ_PROP_NUM(zobj, 1); } diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index d015fed13674e..aea36e8461eac 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -24,6 +24,7 @@ #include "zend_builtin_functions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_dtrace.h" #include "zend_smart_str.h" @@ -82,7 +83,7 @@ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entr static inline zend_class_entry *i_get_exception_base(zend_object *object) /* {{{ */ { - return instanceof_function(object->ce, zend_ce_exception) ? zend_ce_exception : zend_ce_error; + return instanceof_function(OBJ_CE(object), zend_ce_exception) ? zend_ce_exception : zend_ce_error; } /* }}} */ @@ -107,7 +108,7 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo return; } - ZEND_ASSERT(instanceof_function(add_previous->ce, zend_ce_throwable) + ZEND_ASSERT(instanceof_function(OBJ_CE(add_previous), zend_ce_throwable) && "Previous exception must implement Throwable"); ZVAL_OBJ(&pv, add_previous); @@ -172,7 +173,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* #ifdef HAVE_DTRACE if (DTRACE_EXCEPTION_THROWN_ENABLED()) { if (exception != NULL) { - DTRACE_EXCEPTION_THROWN(ZSTR_VAL(exception->ce->name)); + DTRACE_EXCEPTION_THROWN(ZSTR_VAL(OBJ_NAME(exception))); } else { DTRACE_EXCEPTION_THROWN(NULL); } @@ -195,7 +196,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* } } if (!EG(current_execute_data)) { - if (exception && (exception->ce == zend_ce_parse_error || exception->ce == zend_ce_compile_error)) { + if (exception && (OBJ_CE(exception) == zend_ce_parse_error || OBJ_CE(exception) == zend_ce_compile_error)) { return; } if (EG(exception)) { @@ -900,7 +901,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_result result = FAILURE; ZVAL_OBJ(&exception, ex); - ce_exception = ex->ce; + ce_exception = OBJ_CE(ex); EG(exception) = NULL; if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) { zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE)); @@ -918,7 +919,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_string *str, *file = NULL; zend_long line = 0; - zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp); + zend_call_known_instance_method_with_0_params(OBJ_CE(ex)->__tostring, ex, &tmp); if (!EG(exception)) { if (Z_TYPE(tmp) != IS_STRING) { zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name)); @@ -1031,10 +1032,10 @@ ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) ZEND_API bool zend_is_unwind_exit(const zend_object *ex) { - return ex->ce == &zend_ce_unwind_exit; + return OBJ_CE(ex) == &zend_ce_unwind_exit; } ZEND_API bool zend_is_graceful_exit(const zend_object *ex) { - return ex->ce == &zend_ce_graceful_exit; + return OBJ_CE(ex) == &zend_ce_graceful_exit; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 870b660cc58de..36c8d03675941 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -673,14 +673,24 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_erro static void zend_verify_type_generic_params_explainer(smart_str *dest, zend_class_entry *scope, zend_type type) { // TODO: It's all a hack... - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *called_scope = zend_get_called_scope(EG(current_execute_data)); if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); - generic_param_id -= scope->num_bound_generic_args; - zend_generic_param *param = &scope->generic_params[generic_param_id]; - zend_type real_type = called_scope->bound_generic_args[generic_param_id]; - zend_string *real_type_string = zend_type_to_string(real_type, called_scope); + zend_generic_param *param = &scope->generic_params[generic_param_id - scope->num_bound_generic_args]; + for (;;) { + if (generic_param_id >= called_scope->ce->num_bound_generic_args) { + ZEND_ASSERT(generic_param_id - called_scope->ce->num_bound_generic_args < called_scope->args.num_types); + type = called_scope->args.types[generic_param_id - called_scope->ce->num_bound_generic_args]; + } else { + type = called_scope->ce->bound_generic_args[generic_param_id]; + } + if (!ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + break; + } + generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + } + zend_string *real_type_string = zend_type_to_string(type, called_scope->ce); smart_str_append(dest, param->name); smart_str_appends(dest, " = "); smart_str_append(dest, real_type_string); @@ -1085,6 +1095,15 @@ static zend_never_inline bool check_property_type_generic( } #endif +static zend_type zend_resolve_generic_type(zend_class_reference *scope, uint32_t param_id) { + if (param_id >= scope->ce->num_bound_generic_args) { + ZEND_ASSERT(param_id - scope->ce->num_bound_generic_args < scope->args.num_types); + return scope->args.types[param_id - scope->ce->num_bound_generic_args]; + } else { + return scope->ce->bound_generic_args[param_id]; + } +} + static zend_always_inline bool i_zend_check_property_type(const zend_object *obj, const zend_property_info *info, zval *property, bool strict) { ZEND_ASSERT(!Z_ISREF_P(property)); @@ -1098,9 +1117,7 @@ static zend_always_inline bool i_zend_check_property_type(const zend_object *obj } if (ZEND_TYPE_HAS_GENERIC_PARAM(info->type)) { - uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(info->type); - ZEND_ASSERT(param_id < obj->ce->num_bound_generic_args); - zend_type real_type = obj->ce->bound_generic_args[param_id]; + zend_type real_type = zend_resolve_generic_type(obj->cr, ZEND_TYPE_GENERIC_PARAM_ID(info->type)); if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(property))) { return 1; } @@ -1156,11 +1173,11 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) { return 0; } - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_class_reference *called_scope = zend_get_called_scope(EG(current_execute_data)); if (!called_scope) { return 0; } - return instanceof_function(Z_OBJCE_P(zv), called_scope); + return instanceof_function(Z_OBJCE_P(zv), called_scope->ce); } static ZEND_COLD void zend_validate_generic_args_error( @@ -1263,10 +1280,7 @@ static bool zend_check_intersection_type_from_cache_slot(zend_type_list *interse } else { ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); // TODO: Deduplicate this code. - uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); - ZEND_ASSERT(param_id < called_scope->num_bound_generic_args); - zend_type real_type = called_scope->bound_generic_args[param_id]; + zend_type real_type = zend_resolve_generic_type(zend_get_called_scope(EG(current_execute_data)), ZEND_TYPE_GENERIC_PARAM_ID(*list_type)); if (!zend_check_type_slow( &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { status = false; @@ -1318,11 +1332,7 @@ static zend_always_inline bool zend_check_type_slow( PROGRESS_CACHE_SLOT(); } else { ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); - // TODO: Deduplicate this code. - uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); - ZEND_ASSERT(param_id < called_scope->num_bound_generic_args); - zend_type real_type = called_scope->bound_generic_args[param_id]; + zend_type real_type = zend_resolve_generic_type(zend_get_called_scope(EG(current_execute_data)), ZEND_TYPE_GENERIC_PARAM_ID(*list_type)); if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { return true; } @@ -1343,20 +1353,17 @@ static zend_always_inline bool zend_check_type_slow( /* If we have a CE we check if it satisfies the type constraint, * otherwise it will check if a standard type satisfies it. */ if (ce && zend_validate_generic_args(ce, args) - && instanceof_unpacked(ZEND_CE_TO_REF(Z_OBJCE_P(arg)), ce, args)) { + && instanceof_unpacked(Z_OBJCR_P(arg), ce, args)) { return true; } } else if (UNEXPECTED(ZEND_TYPE_HAS_GENERIC_PARAM(*type))) { - // TODO: This doesn't handle free generic parameters. - uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(*type); - zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); - ZEND_ASSERT(param_id < called_scope->num_bound_generic_args); - zend_type *real_type = &called_scope->bound_generic_args[param_id]; - if (ZEND_TYPE_CONTAINS_CODE(*real_type, Z_TYPE_P(arg))) { + // TODO: Deduplicate this code. + zend_type real_type = zend_resolve_generic_type(zend_get_called_scope(EG(current_execute_data)), ZEND_TYPE_GENERIC_PARAM_ID(*type)); + if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { return true; } if (zend_check_type_slow( - real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { + &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { return true; } } @@ -1596,6 +1603,31 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data * } } +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_type_arg_error(zend_execute_data *execute_data) +{ + zend_execute_data *ptr = EX(prev_execute_data); + + if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { + zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected", + EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "", + EX(func)->common.scope ? "::" : "", + ZSTR_VAL(EX(func)->common.function_name), + EX_NUM_ARGS(), + ZSTR_VAL(ptr->func->op_array.filename), + ptr->opline->lineno, + EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least", + EX(func)->common.required_num_args); + } else { + zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed and %s %d expected", + EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "", + EX(func)->common.scope ? "::" : "", + ZSTR_VAL(EX(func)->common.function_name), + EX_NUM_ARGS(), + EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least", + EX(func)->common.required_num_args); + } +} + ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, zval *value) { const zend_arg_info *arg_info = &zf->common.arg_info[-1]; @@ -1711,7 +1743,7 @@ ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_const static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(const zend_object *object) { - zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(object->ce->name)); + zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(OBJ_NAME(object))); } static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_array_offset_access(const zval *offset) @@ -2847,7 +2879,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * retval = obj->handlers->read_dimension(obj, dim, type, result); if (UNEXPECTED(retval == &EG(uninitialized_zval))) { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); ZVAL_NULL(result); zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); @@ -2858,7 +2890,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * retval = result; } if (Z_TYPE_P(retval) != IS_OBJECT) { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); } } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) { @@ -3305,13 +3337,13 @@ ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref) { static zend_property_info *zend_object_fetch_property_type_info( zend_object *obj, zval *slot) { - if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) { + if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(OBJ_CE(obj)))) { return NULL; } /* Not a declared property */ if (UNEXPECTED(slot < obj->properties_table || - slot >= obj->properties_table + obj->ce->default_properties_count)) { + slot >= obj->properties_table + OBJ_CE(obj)->default_properties_count)) { return NULL; } @@ -3396,7 +3428,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c zobj = Z_OBJ_P(container); if (prop_op_type == IS_CONST && - EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -4963,7 +4995,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zend_o } } } else { - zend_throw_error(NULL, "Object of type %s is not callable", ZSTR_VAL(function->ce->name)); + zend_throw_error(NULL, "Object of type %s is not callable", ZSTR_VAL(OBJ_NAME(function))); return NULL; } @@ -5038,13 +5070,13 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(object->ce, Z_STR_P(method)); + zend_undefined_method(OBJ_CE(object), Z_STR_P(method)); } return NULL; } if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - object_or_called_scope = object->ce; + object_or_called_scope = OBJ_CE(object); } else { call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS; GC_ADDREF(object); /* For $this pointer */ diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 6cf39aac01a72..7b1c511d3c473 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -24,6 +24,7 @@ #include "zend_compile.h" #include "zend_hash.h" #include "zend_operators.h" +#include "zend_types.h" #include "zend_variables.h" #include @@ -50,7 +51,7 @@ ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_val ZEND_API bool zend_is_valid_class_name(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags); -ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex); +ZEND_API zend_class_reference *zend_get_called_scope(zend_execute_data *ex); ZEND_API zend_object *zend_get_this_object(zend_execute_data *ex); ZEND_API zend_result zend_eval_string(const char *str, zval *retval_ptr, const char *string_name); ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *retval_ptr, const char *string_name); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index ec229b492e684..b967ba86de0f1 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -31,6 +31,7 @@ #include "zend_exceptions.h" #include "zend_closures.h" #include "zend_generators.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_float.h" #include "zend_fibers.h" @@ -1239,13 +1240,13 @@ ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ } /* }}} */ -ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ +ZEND_API zend_class_reference *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ { while (ex) { if (Z_TYPE(ex->This) == IS_OBJECT) { - return Z_OBJCE(ex->This); + return Z_OBJCR(ex->This); } else if (Z_CE(ex->This)) { - return Z_CE(ex->This); + return ZEND_CE_TO_REF(Z_CE(ex->This)); } else if (ex->func) { if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) { return NULL; @@ -1665,13 +1666,14 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type) return NULL; } return scope->parents[0]->ce; - case ZEND_FETCH_CLASS_STATIC: - ce = zend_get_called_scope(EG(current_execute_data)); - if (UNEXPECTED(!ce)) { - zend_throw_or_error(fetch_type, NULL, "Cannot access \"static\" when no class scope is active"); - return NULL; + case ZEND_FETCH_CLASS_STATIC: { + zend_class_reference *class_ref = zend_get_called_scope(EG(current_execute_data)); + if (UNEXPECTED(!class_ref)) { + zend_throw_or_error(fetch_type, NULL, "Cannot access \"static\" when no class scope is active"); + return NULL; + } + return class_ref->ce; } - return ce; case ZEND_FETCH_CLASS_AUTO: { fetch_sub_type = zend_get_class_fetch_type(class_name); if (UNEXPECTED(fetch_sub_type != ZEND_FETCH_CLASS_DEFAULT)) { diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index b92ead02eb4a0..bc7ed05fda114 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -1442,7 +1442,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta } if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED) && (obj->handlers->dtor_obj != zend_objects_destroy_object - || obj->ce->destructor != NULL)) { + || OBJ_CE(obj)->destructor != NULL)) { *flags |= GC_HAS_DESTRUCTORS; } @@ -1846,7 +1846,7 @@ ZEND_API int zend_gc_collect_cycles(void) if (GC_TYPE(p) == IS_OBJECT && !(OBJ_FLAGS(p) & IS_OBJ_DESTRUCTOR_CALLED)) { zend_object *obj = (zend_object *) p; if (obj->handlers->dtor_obj != zend_objects_destroy_object - || obj->ce->destructor) { + || OBJ_CE(obj)->destructor) { current->ref = GC_MAKE_DTOR_GARBAGE(obj); GC_REF_SET_COLOR(obj, GC_PURPLE); } else { diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 73069872e7f2a..e21f280cf188a 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -49,7 +49,7 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z } if (!obj_ce) { - obj_ce = object ? object->ce : NULL; + obj_ce = object ? OBJ_CE(object) : NULL; } if (!fn_proxy || !*fn_proxy) { if (EXPECTED(obj_ce)) { @@ -74,7 +74,7 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z } if (object) { - called_scope = object->ce; + called_scope = OBJ_CE(object); } else { called_scope = obj_ce; } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index b6aef6d8bdc1c..6f9fc770b205d 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -62,7 +62,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ { if (!zobj->properties) { zend_property_info *prop_info; - zend_class_entry *ce = zobj->ce; + zend_class_entry *ce = OBJ_CE(zobj); int i; zobj->properties = zend_new_array(ce->default_properties_count); @@ -90,7 +90,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj) /* {{{ */ { zend_property_info *prop_info; - zend_class_entry *ce = zobj->ce; + zend_class_entry *ce = OBJ_CE(zobj); HashTable *ht; zval* prop; int i; @@ -145,7 +145,7 @@ ZEND_API HashTable *zend_std_get_gc(zend_object *zobj, zval **table, int *n) /* return zobj->properties; } else { *table = zobj->properties_table; - *n = zobj->ce->default_properties_count; + *n = OBJ_CE(zobj)->default_properties_count; return NULL; } } @@ -154,7 +154,7 @@ ZEND_API HashTable *zend_std_get_gc(zend_object *zobj, zval **table, int *n) /* ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval retval; HashTable *ht; @@ -193,7 +193,7 @@ static void zend_std_call_getter(zend_object *zobj, zend_string *prop_name, zval { zval member; ZVAL_STR(&member, prop_name); - zend_call_known_instance_method_with_1_params(zobj->ce->__get, zobj, retval, &member); + zend_call_known_instance_method_with_1_params(OBJ_CE(zobj)->__get, zobj, retval, &member); } /* }}} */ @@ -202,7 +202,7 @@ static void zend_std_call_setter(zend_object *zobj, zend_string *prop_name, zval zval args[2]; ZVAL_STR(&args[0], prop_name); ZVAL_COPY_VALUE(&args[1], value); - zend_call_known_instance_method(zobj->ce->__set, zobj, NULL, 2, args); + zend_call_known_instance_method(OBJ_CE(zobj)->__set, zobj, NULL, 2, args); } /* }}} */ @@ -210,7 +210,7 @@ static void zend_std_call_unsetter(zend_object *zobj, zend_string *prop_name) /* { zval member; ZVAL_STR(&member, prop_name); - zend_call_known_instance_method_with_1_params(zobj->ce->__unset, zobj, NULL, &member); + zend_call_known_instance_method_with_1_params(OBJ_CE(zobj)->__unset, zobj, NULL, &member); } /* }}} */ @@ -218,7 +218,7 @@ static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zv { zval member; ZVAL_STR(&member, prop_name); - zend_call_known_instance_method_with_1_params(zobj->ce->__isset, zobj, retval, &member); + zend_call_known_instance_method_with_1_params(OBJ_CE(zobj)->__isset, zobj, retval, &member); } /* }}} */ @@ -282,9 +282,9 @@ static ZEND_COLD zend_never_inline bool zend_deprecated_dynamic_property( zend_object *obj, const zend_string *member) { GC_ADDREF(obj); zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", - ZSTR_VAL(obj->ce->name), ZSTR_VAL(member)); + ZSTR_VAL(OBJ_NAME(obj)), ZSTR_VAL(member)); if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_objects_store_del(obj); if (!EG(exception)) { /* We cannot continue execution and have to throw an exception */ @@ -500,7 +500,7 @@ ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_st zend_unmangle_property_name_ex(prop_info_name, &class_name, &prop_name, &prop_name_len); member = zend_string_init(prop_name, prop_name_len, 0); - property_info = zend_get_property_info(zobj->ce, member, 1); + property_info = zend_get_property_info(OBJ_CE(zobj), member, 1); zend_string_release_ex(member, 0); if (property_info == NULL || property_info == ZEND_WRONG_PROPERTY_INFO) { return FAILURE; @@ -519,7 +519,7 @@ ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_st } return SUCCESS; } else { - property_info = zend_get_property_info(zobj->ce, prop_info_name, 1); + property_info = zend_get_property_info(OBJ_CE(zobj), prop_info_name, 1); if (property_info == NULL) { ZEND_ASSERT(is_dynamic); return SUCCESS; @@ -541,7 +541,7 @@ static void zend_property_guard_dtor(zval *el) /* {{{ */ { static zend_always_inline zval *zend_get_guard_value(zend_object *zobj) { - return zobj->properties_table + zobj->ce->default_properties_count; + return zobj->properties_table + OBJ_CE(zobj)->default_properties_count; } ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */ @@ -551,7 +551,7 @@ ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *membe uint32_t *ptr; - ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS); + ZEND_ASSERT(OBJ_CE(zobj)->ce_flags & ZEND_ACC_USE_GUARDS); zv = zend_get_guard_value(zobj); if (EXPECTED(Z_TYPE_P(zv) == IS_STRING)) { zend_string *str = Z_STR_P(zv); @@ -594,7 +594,7 @@ ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *membe ZEND_API uint32_t *zend_get_recursion_guard(zend_object *zobj) { - if (!(zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { + if (!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_USE_GUARDS)) { return NULL; } zval *zv = zend_get_guard_value(zobj); @@ -614,7 +614,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int #endif /* make zend_get_property_info silent if we have getter - we may want to use it */ - property_offset = zend_get_property_offset(zobj->ce, name, (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (type == BP_VAR_IS) || (OBJ_CE(zobj)->__get != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { retval = OBJ_PROP(zobj, property_offset); @@ -684,7 +684,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } /* magic isset */ - if ((type == BP_VAR_IS) && zobj->ce->__isset) { + if ((type == BP_VAR_IS) && OBJ_CE(zobj)->__isset) { zval tmp_result; guard = zend_get_property_guard(zobj, name); @@ -707,14 +707,14 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } zval_ptr_dtor(&tmp_result); - if (zobj->ce->__get && !((*guard) & IN_GET)) { + if (OBJ_CE(zobj)->__get && !((*guard) & IN_GET)) { goto call_getter; } OBJ_RELEASE(zobj); - } else if (zobj->ce->__get && !((*guard) & IN_GET)) { + } else if (OBJ_CE(zobj)->__get && !((*guard) & IN_GET)) { goto call_getter_addref; } - } else if (zobj->ce->__get) { + } else if (OBJ_CE(zobj)->__get) { /* magic get */ guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_GET)) { @@ -731,7 +731,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int if (!Z_ISREF_P(rv) && (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) { if (UNEXPECTED(Z_TYPE_P(rv) != IS_OBJECT)) { - zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); } } } else { @@ -739,14 +739,14 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int } if (UNEXPECTED(prop_info)) { - zend_verify_prop_assignable_by_ref_ex(zobj, prop_info, retval, (zobj->ce->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET); + zend_verify_prop_assignable_by_ref_ex(zobj, prop_info, retval, (OBJ_CE(zobj)->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0, ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET); } OBJ_RELEASE(zobj); goto exit; } else if (UNEXPECTED(IS_WRONG_PROPERTY_OFFSET(property_offset))) { /* Trigger the correct error */ - zend_get_property_offset(zobj->ce, name, 0, NULL, &prop_info); + zend_get_property_offset(OBJ_CE(zobj), name, 0, NULL, &prop_info); ZEND_ASSERT(EG(exception)); retval = &EG(uninitialized_zval); goto exit; @@ -760,7 +760,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int ZSTR_VAL(prop_info->ce->name), ZSTR_VAL(name)); } else { - zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); } } retval = &EG(uninitialized_zval); @@ -816,7 +816,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva const zend_property_info *prop_info = NULL; ZEND_ASSERT(!Z_ISREF_P(value)); - property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__set != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (OBJ_CE(zobj)->__set != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { variable_ptr = OBJ_PROP(zobj, property_offset); @@ -900,7 +900,7 @@ found:; } /* magic set */ - if (zobj->ce->__set) { + if (OBJ_CE(zobj)->__set) { uint32_t *guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_SET)) { @@ -914,7 +914,7 @@ found:; goto write_std_property; } else { /* Trigger the correct error */ - zend_wrong_offset(zobj->ce, name); + zend_wrong_offset(OBJ_CE(zobj), name); ZEND_ASSERT(EG(exception)); variable_ptr = &EG(error_zval); goto exit; @@ -928,7 +928,7 @@ found:; Z_TRY_ADDREF_P(value); if (UNEXPECTED(prop_info)) { if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) - && !verify_readonly_initialization_access(prop_info, zobj->ce, name, "initialize"))) { + && !verify_readonly_initialization_access(prop_info, OBJ_CE(zobj), name, "initialize"))) { Z_TRY_DELREF_P(value); variable_ptr = &EG(error_zval); goto exit; @@ -956,12 +956,12 @@ found:; ZVAL_COPY_VALUE(variable_ptr, value); } else { - if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { - zend_forbidden_dynamic_property(zobj->ce, name); + if (UNEXPECTED(OBJ_CE(zobj)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_forbidden_dynamic_property(OBJ_CE(zobj), name); variable_ptr = &EG(error_zval); goto exit; } - if (UNEXPECTED(!(zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { + if (UNEXPECTED(!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { if (UNEXPECTED(!zend_deprecated_dynamic_property(zobj, name))) { variable_ptr = &EG(error_zval); goto exit; @@ -989,7 +989,7 @@ static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry * ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval tmp_offset; /* arrayaccess_funcs_ptr is set if (and only if) the class implements zend_ce_arrayaccess */ @@ -1040,7 +1040,7 @@ ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int ty ZEND_API void zend_std_write_dimension(zend_object *object, zval *offset, zval *value) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval tmp_offset; zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr; @@ -1062,7 +1062,7 @@ ZEND_API void zend_std_write_dimension(zend_object *object, zval *offset, zval * ZEND_API int zend_std_has_dimension(zend_object *object, zval *offset, int check_empty) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval retval, tmp_offset; int result; @@ -1098,12 +1098,12 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam fprintf(stderr, "Ptr object #%d property: %s\n", zobj->handle, ZSTR_VAL(name)); #endif - property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__get != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (OBJ_CE(zobj)->__get != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { retval = OBJ_PROP(zobj, property_offset); if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { - if (EXPECTED(!zobj->ce->__get) || + if (EXPECTED(!OBJ_CE(zobj)->__get) || UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET) || UNEXPECTED(prop_info && (Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT))) { if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) { @@ -1114,7 +1114,7 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam ZSTR_VAL(name)); retval = &EG(error_zval); } else { - zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); /* An error handler may set the property */ if (EXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { ZVAL_NULL(retval); @@ -1146,13 +1146,13 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam return retval; } } - if (EXPECTED(!zobj->ce->__get) || + if (EXPECTED(!OBJ_CE(zobj)->__get) || UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) { - if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { - zend_forbidden_dynamic_property(zobj->ce, name); + if (UNEXPECTED(OBJ_CE(zobj)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_forbidden_dynamic_property(OBJ_CE(zobj), name); return &EG(error_zval); } - if (UNEXPECTED(!(zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { + if (UNEXPECTED(!(OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { if (UNEXPECTED(!zend_deprecated_dynamic_property(zobj, name))) { return &EG(error_zval); } @@ -1161,11 +1161,11 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam rebuild_object_properties(zobj); } if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) { - zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name)); + zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(OBJ_CE(zobj)->name), ZSTR_VAL(name)); } retval = zend_hash_add(zobj->properties, name, &EG(uninitialized_zval)); } - } else if (zobj->ce->__get == NULL) { + } else if (OBJ_CE(zobj)->__get == NULL) { retval = &EG(error_zval); } @@ -1178,7 +1178,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void uintptr_t property_offset; const zend_property_info *prop_info = NULL; - property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__unset != NULL), cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, (OBJ_CE(zobj)->__unset != NULL), cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { zval *slot = OBJ_PROP(zobj, property_offset); @@ -1209,7 +1209,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void } if (UNEXPECTED(Z_PROP_FLAG_P(slot) & IS_PROP_UNINIT)) { if (UNEXPECTED(prop_info && (prop_info->flags & ZEND_ACC_READONLY) - && !verify_readonly_initialization_access(prop_info, zobj->ce, name, "unset"))) { + && !verify_readonly_initialization_access(prop_info, OBJ_CE(zobj), name, "unset"))) { return; } @@ -1233,7 +1233,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void } /* magic unset */ - if (zobj->ce->__unset) { + if (OBJ_CE(zobj)->__unset) { uint32_t *guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_UNSET)) { /* have unsetter - try with it! */ @@ -1242,7 +1242,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void (*guard) &= ~IN_UNSET; } else if (UNEXPECTED(IS_WRONG_PROPERTY_OFFSET(property_offset))) { /* Trigger the correct error */ - zend_wrong_offset(zobj->ce, name); + zend_wrong_offset(OBJ_CE(zobj), name); ZEND_ASSERT(EG(exception)); return; } else { @@ -1254,7 +1254,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void ZEND_API void zend_std_unset_dimension(zend_object *object, zval *offset) /* {{{ */ { - zend_class_entry *ce = object->ce; + zend_class_entry *ce = OBJ_CE(object); zval tmp_offset; zend_class_arrayaccess_funcs *funcs = ce->arrayaccess_funcs_ptr; @@ -1424,12 +1424,12 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name)); } - if (UNEXPECTED((func = zend_hash_find(&zobj->ce->function_table, lc_method_name)) == NULL)) { + if (UNEXPECTED((func = zend_hash_find(&OBJ_CE(zobj)->function_table, lc_method_name)) == NULL)) { if (UNEXPECTED(!key)) { ZSTR_ALLOCA_FREE(lc_method_name, use_heap); } - if (zobj->ce->__call) { - return zend_get_user_call_function(zobj->ce, method_name); + if (OBJ_CE(zobj)->__call) { + return zend_get_user_call_function(OBJ_CE(zobj), method_name); } else { return NULL; } @@ -1443,7 +1443,7 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * if (fbc->common.scope != scope) { if (fbc->op_array.fn_flags & ZEND_ACC_CHANGED) { - zend_function *updated_fbc = zend_get_parent_private_method(scope, zobj->ce, lc_method_name); + zend_function *updated_fbc = zend_get_parent_private_method(scope, OBJ_CE(zobj), lc_method_name); if (EXPECTED(updated_fbc != NULL)) { fbc = updated_fbc; @@ -1454,8 +1454,8 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * } if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) { - if (zobj->ce->__call) { - fbc = zend_get_user_call_function(zobj->ce, method_name); + if (OBJ_CE(zobj)->__call) { + fbc = zend_get_user_call_function(OBJ_CE(zobj), method_name); } else { zend_bad_method_call(fbc, method_name, scope); fbc = NULL; @@ -1488,12 +1488,12 @@ static zend_always_inline zend_function *get_static_method_fallback( zend_object *object; if (ce->__call && (object = zend_get_this_object(EG(current_execute_data))) != NULL && - instanceof_function(object->ce, ce)) { + instanceof_function(OBJ_CE(object), ce)) { /* Call the top-level defined __call(). * see: tests/classes/__call_004.phpt */ - ZEND_ASSERT(object->ce->__call); - return zend_get_user_call_function(object->ce, function_name); + ZEND_ASSERT(OBJ_CE(object)->__call); + return zend_get_user_call_function(OBJ_CE(object), function_name); } else if (ce->__callstatic) { return zend_get_user_callstatic_function(ce, function_name); } else { @@ -1674,7 +1674,7 @@ static ZEND_COLD zend_never_inline void zend_bad_constructor_call(zend_function ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */ { - zend_function *constructor = zobj->ce->constructor; + zend_function *constructor = OBJ_CE(zobj)->constructor; zend_class_entry *scope; if (constructor) { @@ -1745,14 +1745,14 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ if (zobj1 == zobj2) { return 0; /* the same object */ } - if (zobj1->ce != zobj2->ce) { + if (OBJ_CE(zobj1) != OBJ_CE(zobj2)) { return ZEND_UNCOMPARABLE; /* different classes */ } if (!zobj1->properties && !zobj2->properties) { zend_property_info *info; int i; - if (!zobj1->ce->default_properties_count) { + if (!OBJ_CE(zobj1)->default_properties_count) { return 0; } @@ -1766,10 +1766,10 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ } Z_PROTECT_RECURSION_P(o1); - for (i = 0; i < zobj1->ce->default_properties_count; i++) { + for (i = 0; i < OBJ_CE(zobj1)->default_properties_count; i++) { zval *p1, *p2; - info = zobj1->ce->properties_info_table[i]; + info = OBJ_CE(zobj1)->properties_info_table[i]; if (!info) { continue; @@ -1826,7 +1826,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has const zend_property_info *prop_info = NULL; zend_string *tmp_name = NULL; - property_offset = zend_get_property_offset(zobj->ce, name, 1, cache_slot, &prop_info); + property_offset = zend_get_property_offset(OBJ_CE(zobj), name, 1, cache_slot, &prop_info); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) { value = OBJ_PROP(zobj, property_offset); @@ -1882,7 +1882,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has } result = 0; - if ((has_set_exists != ZEND_PROPERTY_EXISTS) && zobj->ce->__isset) { + if ((has_set_exists != ZEND_PROPERTY_EXISTS) && OBJ_CE(zobj)->__isset) { uint32_t *guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_ISSET)) { @@ -1898,7 +1898,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has result = zend_is_true(&rv); zval_ptr_dtor(&rv); if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY && result) { - if (EXPECTED(!EG(exception)) && zobj->ce->__get && !((*guard) & IN_GET)) { + if (EXPECTED(!EG(exception)) && OBJ_CE(zobj)->__get && !((*guard) & IN_GET)) { (*guard) |= IN_GET; zend_std_call_getter(zobj, name, &rv); (*guard) &= ~IN_GET; @@ -1921,7 +1921,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has ZEND_API zend_string *zend_std_get_class_name(const zend_object *zobj) /* {{{ */ { - return zend_string_copy(zobj->ce->name); + return zend_string_copy(OBJ_CE(zobj)->name); } /* }}} */ @@ -1929,7 +1929,7 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w { switch (type) { case IS_STRING: { - zend_class_entry *ce = readobj->ce; + zend_class_entry *ce = OBJ_CE(readobj); if (ce->__tostring) { zval retval; GC_ADDREF(readobj); @@ -1957,7 +1957,7 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */ { - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zval *func = zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE)); if (func == NULL) { diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 4c4b3cf30c13d..ceef78705466b 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -20,30 +20,31 @@ #include "zend.h" #include "zend_globals.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_API.h" #include "zend_interfaces.h" #include "zend_exceptions.h" #include "zend_weakrefs.h" -static zend_always_inline void _zend_object_std_init(zend_object *object, zend_class_entry *ce) +static zend_always_inline void _zend_object_std_init(zend_object *object, zend_class_reference *class_ref) { GC_SET_REFCOUNT(object, 1); GC_TYPE_INFO(object) = GC_OBJECT; - object->ce = ce; - object->handlers = ce->default_object_handlers; + object->cr = class_ref; + object->handlers = class_ref->ce->default_object_handlers; object->properties = NULL; zend_objects_store_put(object); - if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) { - zval *guard_value = object->properties_table + object->ce->default_properties_count; + if (UNEXPECTED(class_ref->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { + zval *guard_value = object->properties_table + class_ref->ce->default_properties_count; ZVAL_UNDEF(guard_value); Z_GUARD_P(guard_value) = 0; } } -ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce) +ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *class_type) { - _zend_object_std_init(object, ce); + _zend_object_std_init(object, ZEND_CE_TO_REF(class_type)); } ZEND_API void zend_object_std_dtor(zend_object *object) @@ -59,8 +60,8 @@ ZEND_API void zend_object_std_dtor(zend_object *object) } } p = object->properties_table; - if (EXPECTED(object->ce->default_properties_count)) { - end = p + object->ce->default_properties_count; + if (EXPECTED(OBJ_CE(object)->default_properties_count)) { + end = p + OBJ_CE(object)->default_properties_count; do { if (Z_REFCOUNTED_P(p)) { if (UNEXPECTED(Z_ISREF_P(p)) && @@ -76,7 +77,7 @@ ZEND_API void zend_object_std_dtor(zend_object *object) } while (p != end); } - if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { + if (UNEXPECTED(OBJ_CE(object)->ce_flags & ZEND_ACC_USE_GUARDS)) { if (EXPECTED(Z_TYPE_P(p) == IS_STRING)) { zval_ptr_dtor_str(p); } else if (Z_TYPE_P(p) == IS_ARRAY) { @@ -92,11 +93,16 @@ ZEND_API void zend_object_std_dtor(zend_object *object) if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { zend_weakrefs_notify(object); } + + if (UNEXPECTED(!ZEND_REF_IS_TRIVIAL(object->cr))) { + // TODO + efree(object->cr); + } } ZEND_API void zend_objects_destroy_object(zend_object *object) { - zend_function *destructor = object->ce->destructor; + zend_function *destructor = OBJ_CE(object)->destructor; if (destructor) { zend_object *old_exception; @@ -109,10 +115,10 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (EG(current_execute_data)) { zend_class_entry *scope = zend_get_executed_scope(); - if (object->ce != scope) { + if (OBJ_CE(object) != scope) { zend_throw_error(NULL, "Call to private %s::__destruct() from %s%s", - ZSTR_VAL(object->ce->name), + ZSTR_VAL(OBJ_NAME(object)), scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "" ); @@ -121,7 +127,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } else { zend_error(E_WARNING, "Call to private %s::__destruct() from global scope during shutdown ignored", - ZSTR_VAL(object->ce->name)); + ZSTR_VAL(OBJ_NAME(object))); return; } } else { @@ -133,7 +139,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (!zend_check_protected(zend_get_function_root_class(destructor), scope)) { zend_throw_error(NULL, "Call to protected %s::__destruct() from %s%s", - ZSTR_VAL(object->ce->name), + ZSTR_VAL(OBJ_NAME(object)), scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "" ); @@ -142,7 +148,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } else { zend_error(E_WARNING, "Call to protected %s::__destruct() from global scope during shutdown ignored", - ZSTR_VAL(object->ce->name)); + ZSTR_VAL(OBJ_NAME(object))); return; } } @@ -184,22 +190,27 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) } } -ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce) +ZEND_API zend_object* ZEND_FASTCALL zend_objects_new_ref(zend_class_reference *class_ref) { - zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce)); + zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(class_ref->ce)); - _zend_object_std_init(object, ce); + _zend_object_std_init(object, class_ref); return object; } +ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce) +{ + return zend_objects_new_ref(ZEND_CE_TO_REF(ce)); +} + ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object) { - bool has_clone_method = old_object->ce->clone != NULL; + bool has_clone_method = OBJ_CE(old_object)->clone != NULL; - if (old_object->ce->default_properties_count) { + if (OBJ_CE(old_object)->default_properties_count) { zval *src = old_object->properties_table; zval *dst = new_object->properties_table; - zval *end = src + old_object->ce->default_properties_count; + zval *end = src + OBJ_CE(old_object)->default_properties_count; do { i_zval_ptr_dtor(dst); @@ -268,10 +279,10 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, if (has_clone_method) { GC_ADDREF(new_object); - zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL); + zend_call_known_instance_method_with_0_params(OBJ_CE(new_object)->clone, new_object, NULL); - if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { - for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { + if (ZEND_CLASS_HAS_READONLY_PROPS(OBJ_CE(new_object))) { + for (uint32_t i = 0; i < OBJ_CE(new_object)->default_properties_count; i++) { zval* prop = OBJ_PROP_NUM(new_object, i); /* Unconditionally remove the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */ Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE; @@ -288,12 +299,12 @@ ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object) /* assume that create isn't overwritten, so when clone depends on the * overwritten one then it must itself be overwritten */ - new_object = zend_objects_new(old_object->ce); + new_object = zend_objects_new_ref(old_object->cr); /* zend_objects_clone_members() expect the properties to be initialized. */ - if (new_object->ce->default_properties_count) { + if (OBJ_CE(new_object)->default_properties_count) { zval *p = new_object->properties_table; - zval *end = p + new_object->ce->default_properties_count; + zval *end = p + OBJ_CE(new_object)->default_properties_count; do { ZVAL_UNDEF(p); p++; diff --git a/Zend/zend_objects.h b/Zend/zend_objects.h index 91d388154dd13..970ff92fb60a9 100644 --- a/Zend/zend_objects.h +++ b/Zend/zend_objects.h @@ -21,9 +21,11 @@ #define ZEND_OBJECTS_H #include "zend.h" +#include "zend_types.h" BEGIN_EXTERN_C() ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce); +ZEND_API zend_object* ZEND_FASTCALL zend_objects_new_ref(zend_class_reference *class_ref); ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce); ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object); diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 80f5b747db710..72fd04233d1c2 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -54,7 +54,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_sto GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); if (obj->handlers->dtor_obj != zend_objects_destroy_object - || obj->ce->destructor) { + || OBJ_CE(obj)->destructor) { GC_ADDREF(obj); obj->handlers->dtor_obj(obj); GC_DELREF(obj); @@ -178,7 +178,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ * GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); if (object->handlers->dtor_obj != zend_objects_destroy_object - || object->ce->destructor) { + || OBJ_CE(object)->destructor) { zend_fiber_switch_block(); GC_SET_REFCOUNT(object, 1); object->handlers->dtor_obj(object); diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 95fa5acf62fb7..24f7b427e63b2 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -98,9 +98,9 @@ static zend_always_inline void *zend_object_alloc(size_t obj_size, zend_class_en static inline zend_property_info *zend_get_property_info_for_slot(zend_object *obj, zval *slot) { - zend_property_info **table = obj->ce->properties_info_table; + zend_property_info **table = OBJ_CE(obj)->properties_info_table; intptr_t prop_num = slot - obj->properties_table; - ZEND_ASSERT(prop_num >= 0 && prop_num < obj->ce->default_properties_count); + ZEND_ASSERT(prop_num >= 0 && prop_num < OBJ_CE(obj)->default_properties_count); return table[prop_num]; } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 50503d21f9a64..f5c4a8530fdad 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2859,7 +2859,7 @@ ZEND_API bool ZEND_FASTCALL zend_object_is_true(const zval *op) /* {{{ */ if (zobj->handlers->cast_object(zobj, &tmp, _IS_BOOL) == SUCCESS) { return Z_TYPE(tmp) == IS_TRUE; } - zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(zobj->ce->name)); + zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(OBJ_NAME(zobj))); return false; } /* }}} */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index e55c01b48d3e7..c4e7a36132f1b 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -242,7 +242,7 @@ typedef struct { ((uint32_t) (uintptr_t) (t).ptr) #define ZEND_TYPE_LIST_SIZE(num_types) \ - (sizeof(zend_type_list) + ((num_types) - 1) * sizeof(zend_type)) + (sizeof(zend_type_list) + ((ssize_t)(num_types) - 1) * sizeof(zend_type)) /* This iterates over a zend_type_list. */ #define ZEND_TYPE_LIST_FOREACH(list, type_ptr) do { \ @@ -417,6 +417,9 @@ static const zend_type_list zend_empty_type_list = {0}; #define ZEND_PNR_GET_NAME(pnr) \ (ZEND_PNR_IS_COMPLEX(pnr) ? ZEND_PNR_COMPLEX_GET_NAME(pnr) : ZEND_PNR_SIMPLE_GET_NAME(pnr)) +#define ZEND_PNR_GET_ARGS(pnr) \ + (ZEND_PNR_IS_COMPLEX(pnr) ? &ZEND_PNR_COMPLEX_GET_REF(pnr)->args : &zend_empty_type_list) + #define ZEND_PNR_UNPACK(pnr, name_var, args_var) do { \ if (ZEND_PNR_IS_COMPLEX(pnr)) { \ zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ @@ -441,6 +444,7 @@ typedef union _zend_value { zval *zv; void *ptr; zend_class_entry *ce; + zend_packed_name_reference pnr; zend_function *func; struct { uint32_t w1; @@ -672,7 +676,7 @@ typedef struct _HashTableIterator { struct _zend_object { zend_refcounted_h gc; uint32_t handle; // TODO: may be removed ??? - zend_class_entry *ce; + zend_class_reference *cr; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; @@ -949,6 +953,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define OBJ_FLAGS(obj) GC_FLAGS(obj) +#define OBJ_CE(obj) (obj)->cr->ce +#define OBJ_NAME(obj) OBJ_CE(obj)->name + /* Fast class cache */ #define ZSTR_HAS_CE_CACHE(s) (GC_FLAGS(s) & IS_STR_CLASS_NAME_MAP_PTR) #define ZSTR_GET_CE_CACHE(s) ZSTR_GET_CE_CACHE_EX(s, 1) @@ -1110,9 +1117,12 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_OBJ_HANDLE(zval) (Z_OBJ((zval)))->handle #define Z_OBJ_HANDLE_P(zval_p) Z_OBJ_HANDLE(*(zval_p)) -#define Z_OBJCE(zval) (Z_OBJ(zval)->ce) +#define Z_OBJCE(zval) (Z_OBJ(zval)->cr->ce) #define Z_OBJCE_P(zval_p) Z_OBJCE(*(zval_p)) +#define Z_OBJCR(zval) (Z_OBJ(zval)->cr) +#define Z_OBJCR_P(zval_p) Z_OBJCR(*(zval_p)) + #define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(Z_OBJ(zval)) #define Z_OBJPROP_P(zval_p) Z_OBJPROP(*(zval_p)) @@ -1148,6 +1158,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_CE(zval) (zval).value.ce #define Z_CE_P(zval_p) Z_CE(*(zval_p)) +#define Z_PNR(zval) (zval).value.pnr +#define Z_PNR_P(zval_p) Z_PNR(*(zval_p)) + #define Z_FUNC(zval) (zval).value.func #define Z_FUNC_P(zval_p) Z_FUNC(*(zval_p)) @@ -1365,6 +1378,11 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { Z_TYPE_INFO_P(z) = IS_PTR; \ } while (0) +#define ZVAL_PNR(z, c) do { \ + Z_PNR_P(z) = (c); \ + Z_TYPE_INFO_P(z) = IS_PTR; \ + } while (0) + #define ZVAL_ALIAS_PTR(z, p) do { \ Z_PTR_P(z) = (p); \ Z_TYPE_INFO_P(z) = IS_ALIAS_PTR; \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 40f3e7c98171e..df33b6482926f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -23,6 +23,7 @@ * php zend_vm_gen.php */ +#include "zend_types.h" ZEND_VM_HELPER(zend_add_helper, ANY, ANY, zval *op_1, zval *op_2) { USE_OPLINE @@ -2073,7 +2074,7 @@ ZEND_VM_HOT_OBJ_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMPVAR|UNUSED|THIS|CV, CONST if (OP2_TYPE == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -2135,7 +2136,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy): * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -2238,7 +2239,7 @@ ZEND_VM_COLD_CONST_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|THIS|CV, C if (OP2_TYPE == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -2409,7 +2410,7 @@ ZEND_VM_HANDLER(24, ZEND_ASSIGN_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_ ZEND_VM_C_LABEL(assign_object): zobj = Z_OBJ_P(object); if (OP2_TYPE == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -2446,7 +2447,7 @@ ZEND_VM_C_LABEL(fast_assign_obj): } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -3553,7 +3554,7 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (OP2_TYPE == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -3569,7 +3570,7 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((OP2_TYPE == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } FREE_OP2(); if ((OP1_TYPE & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -3717,7 +3718,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -4657,7 +4658,7 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT) CACHE_PTR(opline->extended_value & ~ZEND_LAST_CATCH, catch_ce); } - ce = EG(exception)->ce; + ce = OBJ_CE(EG(exception)); #ifdef HAVE_DTRACE if (DTRACE_EXCEPTION_CAUGHT_ENABLED()) { @@ -5768,32 +5769,49 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (OP1_TYPE == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); + zend_class_entry *ce = CACHED_PTR(opline->op2.num); + zend_string *name; + const zend_type_list *type_args; + ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + // TODO: pre-compute lower case name + ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } CACHE_PTR(opline->op2.num, ce); } + + if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { + class_ref = ZEND_CE_TO_REF(ce); + } else { + // TODO: re-use class references + class_ref = zend_build_class_reference(ce, type_args); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } } else if (OP1_TYPE == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -5867,7 +5885,7 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY) } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -6732,7 +6750,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR) ZEND_VM_NEXT_OPCODE(); } else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -9326,10 +9344,10 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED) } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 60b7534c7710a..65ce71f596266 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4750,7 +4750,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_ CACHE_PTR(opline->extended_value & ~ZEND_LAST_CATCH, catch_ce); } - ce = EG(exception)->ce; + ce = OBJ_CE(EG(exception)); #ifdef HAVE_DTRACE if (DTRACE_EXCEPTION_CAUGHT_ENABLED()) { @@ -4864,7 +4864,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_ } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -5170,7 +5170,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER( ZEND_VM_NEXT_OPCODE(); } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -6426,7 +6426,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -6488,7 +6488,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -6553,7 +6553,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -6868,7 +6868,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -6884,7 +6884,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CONST & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -7032,7 +7032,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -8946,7 +8946,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -9008,7 +9008,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -9073,7 +9073,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -9388,7 +9388,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -9404,7 +9404,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if ((IS_CONST & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -9552,7 +9552,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -10305,7 +10305,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -10520,32 +10520,49 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (IS_CONST == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); + zend_class_entry *ce = CACHED_PTR(opline->op2.num); + zend_string *name; + const zend_type_list *type_args; + ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + // TODO: pre-compute lower case name + ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } CACHE_PTR(opline->op2.num, ce); } + + if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { + class_ref = ZEND_CE_TO_REF(ce); + } else { + // TODO: re-use class references + class_ref = zend_build_class_reference(ce, type_args); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } } else if (IS_CONST == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -10946,10 +10963,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_ } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); @@ -11333,7 +11350,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -11395,7 +11412,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -11460,7 +11477,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -11775,7 +11792,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -11791,7 +11808,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CONST & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -11939,7 +11956,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -14772,7 +14789,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -15715,7 +15732,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -15777,7 +15794,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -15842,7 +15859,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -16128,7 +16145,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -16144,7 +16161,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -17160,7 +17177,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -17222,7 +17239,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -17287,7 +17304,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -17573,7 +17590,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -17589,7 +17606,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -18223,10 +18240,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); @@ -18520,7 +18537,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -18582,7 +18599,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -18647,7 +18664,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HA if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -18933,7 +18950,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -18949,7 +18966,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -19537,7 +19554,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE ZEND_VM_NEXT_OPCODE(); } else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -22207,7 +22224,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE ZEND_VM_NEXT_OPCODE(); } else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -23403,7 +23420,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23440,7 +23457,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -23537,7 +23554,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23574,7 +23591,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -23671,7 +23688,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23708,7 +23725,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -23805,7 +23822,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -23842,7 +23859,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -24749,7 +24766,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -26330,7 +26347,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26367,7 +26384,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -26464,7 +26481,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26501,7 +26518,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -26598,7 +26615,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26635,7 +26652,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -26732,7 +26749,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -26769,7 +26786,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -27618,7 +27635,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -29132,7 +29149,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -29672,32 +29689,49 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (IS_VAR == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); + zend_class_entry *ce = CACHED_PTR(opline->op2.num); + zend_string *name; + const zend_type_list *type_args; + ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + // TODO: pre-compute lower case name + ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } CACHE_PTR(opline->op2.num, ce); } + + if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { + class_ref = ZEND_CE_TO_REF(ce); + } else { + // TODO: re-use class references + class_ref = zend_build_class_reference(ce, type_args); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } } else if (IS_VAR == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -30623,7 +30657,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -30660,7 +30694,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -30757,7 +30791,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -30794,7 +30828,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -30891,7 +30925,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -30928,7 +30962,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -31025,7 +31059,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -31062,7 +31096,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -32007,7 +32041,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -32594,7 +32628,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -32951,7 +32985,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -33013,7 +33047,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -33121,7 +33155,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -33257,7 +33291,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33294,7 +33328,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33391,7 +33425,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33428,7 +33462,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33525,7 +33559,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33562,7 +33596,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -33659,7 +33693,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -33696,7 +33730,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -34001,7 +34035,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -34017,7 +34051,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_UNUSED & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -34165,7 +34199,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -35011,7 +35045,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -35073,7 +35107,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -35176,7 +35210,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -35312,7 +35346,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35349,7 +35383,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -35446,7 +35480,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35483,7 +35517,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -35580,7 +35614,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35617,7 +35651,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -35714,7 +35748,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -35751,7 +35785,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -36057,7 +36091,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -36073,7 +36107,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if ((IS_UNUSED & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -36221,7 +36255,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -36636,7 +36670,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -36834,32 +36868,49 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER USE_OPLINE zval *result; zend_function *constructor; - zend_class_entry *ce; + zend_class_reference *class_ref; zend_execute_data *call; SAVE_OPLINE(); if (IS_UNUSED == IS_CONST) { - ce = CACHED_PTR(opline->op2.num); + zend_class_entry *ce = CACHED_PTR(opline->op2.num); + zend_string *name; + const zend_type_list *type_args; + ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); if (UNEXPECTED(ce == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + // TODO: pre-compute lower case name + ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } CACHE_PTR(opline->op2.num, ce); } + + if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { + class_ref = ZEND_CE_TO_REF(ce); + } else { + // TODO: re-use class references + class_ref = zend_build_class_reference(ce, type_args); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } } else if (IS_UNUSED == IS_UNUSED) { - ce = zend_fetch_class(NULL, opline->op1.num); + zend_class_entry *ce = zend_fetch_class(NULL, opline->op1.num); if (UNEXPECTED(ce == NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + class_ref = ZEND_CE_TO_REF(ce); } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); + zend_class_entry *ce = Z_CE_P(EX_VAR(opline->op1.var)); + class_ref = ZEND_CE_TO_REF(ce); } result = EX_VAR(opline->result.var); - if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { + if (UNEXPECTED(object_init_ref(result, class_ref) != SUCCESS)) { ZVAL_UNDEF(result); HANDLE_EXCEPTION(); } @@ -37508,7 +37559,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -37570,7 +37621,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -37673,7 +37724,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HA if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -37809,7 +37860,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -37846,7 +37897,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -37943,7 +37994,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -37980,7 +38031,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -38077,7 +38128,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -38114,7 +38165,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -38211,7 +38262,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -38248,7 +38299,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -38553,7 +38604,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -38569,7 +38620,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_UNUSED & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -38717,7 +38768,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U zend_throw_error(NULL, "Cannot call constructor"); HANDLE_EXCEPTION(); } - if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { + if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJCE(EX(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) { zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name)); HANDLE_EXCEPTION(); } @@ -39721,7 +39772,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC } while (0); zobj = Z_OBJ_P(obj); - ce = zobj->ce; + ce = OBJ_CE(zobj); clone = ce->clone; clone_call = zobj->handlers->clone_obj; if (UNEXPECTED(clone_call == NULL)) { @@ -39944,7 +39995,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN ZEND_VM_NEXT_OPCODE(); } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(array_ptr); - if (!zobj->ce->get_iterator) { + if (!OBJ_CE(zobj)->get_iterator) { HashTable *properties = zobj->properties; if (properties) { if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) { @@ -41779,7 +41830,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -41841,7 +41892,7 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -41949,7 +42000,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HAN if (IS_CONST == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -42085,7 +42136,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42122,7 +42173,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -42219,7 +42270,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42256,7 +42307,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -42353,7 +42404,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42390,7 +42441,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -42487,7 +42538,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA assign_object: zobj = Z_OBJ_P(object); if (IS_CONST == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -42524,7 +42575,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -43545,7 +43596,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -43561,7 +43612,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -45624,7 +45675,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -45686,7 +45737,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -45789,7 +45840,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HA if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -45925,7 +45976,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -45962,7 +46013,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -46059,7 +46110,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -46096,7 +46147,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -46193,7 +46244,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -46230,7 +46281,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -46327,7 +46378,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D assign_object: zobj = Z_OBJ_P(object); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -46364,7 +46415,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -47327,7 +47378,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -47343,7 +47394,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { @@ -49871,10 +49922,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z } /* if not and the object implements Countable we call its count() method */ - if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) { + if (zend_class_implements_interface(OBJ_CE(zobj), zend_ce_countable)) { zval retval; - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); count = zval_get_long(&retval); zval_ptr_dtor(&retval); @@ -51016,7 +51067,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -51078,7 +51129,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER * Fetch prop_info before calling read_property(), as it may deallocate the object. */ zend_property_info *prop_info = NULL; if (zobj->handlers->read_property != zend_std_read_property) { - prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true); + prop_info = zend_get_property_info(OBJ_CE(zobj), name, /* silent */ true); } #endif retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var)); @@ -51181,7 +51232,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLE if (IS_CV == IS_CONST) { cache_slot = CACHE_ADDR(opline->extended_value); - if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { @@ -51317,7 +51368,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51354,7 +51405,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -51451,7 +51502,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51488,7 +51539,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -51585,7 +51636,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51622,7 +51673,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -51719,7 +51770,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ assign_object: zobj = Z_OBJ_P(object); if (IS_CV == IS_CONST) { - if (EXPECTED(zobj->ce == CACHED_PTR(opline->extended_value))) { + if (EXPECTED(OBJ_CE(zobj) == CACHED_PTR(opline->extended_value))) { void **cache_slot = CACHE_ADDR(opline->extended_value); uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zval *property_val; @@ -51756,7 +51807,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ } } - if (!zobj->ce->__set && (zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + if (!OBJ_CE(zobj)->__set && (OBJ_CE(zobj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { if (EXPECTED(zobj->properties == NULL)) { rebuild_object_properties(zobj); } @@ -52815,7 +52866,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA } while (0); } - called_scope = obj->ce; + called_scope = OBJ_CE(obj); if (IS_CV == IS_CONST && EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { @@ -52831,7 +52882,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_undefined_method(obj->ce, Z_STR_P(function_name)); + zend_undefined_method(OBJ_CE(obj), Z_STR_P(function_name)); } if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && GC_DELREF(orig_obj) == 0) { diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 442b19fb62adb..26e9d201d79f7 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -341,7 +341,7 @@ static zval *zend_weakmap_read_dimension(zend_object *object, zval *offset, int if (zv == NULL) { if (type != BP_VAR_IS) { zend_throw_error(NULL, - "Object %s#%d not contained in WeakMap", ZSTR_VAL(obj_addr->ce->name), obj_addr->handle); + "Object %s#%d not contained in WeakMap", ZSTR_VAL(OBJ_NAME(obj_addr)), obj_addr->handle); return NULL; } return NULL; diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 52e6e98260ec5..602abd6d78719 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -600,14 +600,14 @@ static void update_property(zend_object *object, zend_string *key, zval *prop_va zend_string_release_ex(cname, 0); } else { // protected - zend_update_property(object->ce, object, prop_name, prop_name_len, prop_val); + zend_update_property(OBJ_CE(object), object, prop_name, prop_name_len, prop_val); } } return; } // public - zend_update_property(object->ce, object, ZSTR_VAL(key), ZSTR_LEN(key), prop_val); + zend_update_property(OBJ_CE(object), object, ZSTR_VAL(key), ZSTR_LEN(key), prop_val); } /* }}} */ @@ -1858,7 +1858,7 @@ static zend_object *date_object_new_date(zend_class_entry *class_type) /* {{{ */ static zend_object *date_object_clone_date(zend_object *this_ptr) /* {{{ */ { php_date_obj *old_obj = php_date_obj_from_obj(this_ptr); - php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(old_obj->std.ce)); + php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); if (!old_obj->time) { @@ -1998,7 +1998,7 @@ static zend_object *date_object_new_timezone(zend_class_entry *class_type) /* {{ static zend_object *date_object_clone_timezone(zend_object *this_ptr) /* {{{ */ { php_timezone_obj *old_obj = php_timezone_obj_from_obj(this_ptr); - php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(old_obj->std.ce)); + php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); if (!old_obj->initialized) { @@ -2162,7 +2162,7 @@ static zend_object *date_object_new_interval(zend_class_entry *class_type) /* {{ static zend_object *date_object_clone_interval(zend_object *this_ptr) /* {{{ */ { php_interval_obj *old_obj = php_interval_obj_from_obj(this_ptr); - php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(old_obj->std.ce)); + php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); new_obj->civil_or_wall = old_obj->civil_or_wall; @@ -2253,7 +2253,7 @@ static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ static zend_object *date_object_clone_period(zend_object *this_ptr) /* {{{ */ { php_period_obj *old_obj = php_period_obj_from_obj(this_ptr); - php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(old_obj->std.ce)); + php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(OBJ_CE(&old_obj->std))); zend_objects_clone_members(&new_obj->std, &old_obj->std); new_obj->initialized = old_obj->initialized; diff --git a/ext/hash/hash.c b/ext/hash/hash.c index e0024daf90d77..d5ff4f215a4df 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -1396,7 +1396,7 @@ static void php_hashcontext_free(zend_object *obj) { /* {{{ php_hashcontext_clone */ static zend_object *php_hashcontext_clone(zend_object *zobj) { php_hashcontext_object *oldobj = php_hashcontext_from_object(zobj); - zend_object *znew = php_hashcontext_create(zobj->ce); + zend_object *znew = php_hashcontext_create(OBJ_CE(zobj)); php_hashcontext_object *newobj = php_hashcontext_from_object(znew); if (!oldobj->context) { diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 4709c0e2be4a7..8db6984767382 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -124,7 +124,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, && Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) { /* Optimized version without rebuilding properties HashTable */ zend_object *obj = Z_OBJ_P(val); - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_property_info *prop_info; zval *prop; int i; diff --git a/ext/random/engine_mt19937.c b/ext/random/engine_mt19937.c index e16c9f6722fc3..043c4ac6232fd 100644 --- a/ext/random/engine_mt19937.c +++ b/ext/random/engine_mt19937.c @@ -350,30 +350,30 @@ PHP_METHOD(Random_Engine_Mt19937, __unserialize) /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */ if (zend_hash_num_elements(d) != 2) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } /* members */ t = zend_hash_index_find(d, 0); if (!t || Z_TYPE_P(t) != IS_ARRAY) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } object_properties_load(&engine->std, Z_ARRVAL_P(t)); if (EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } /* state */ t = zend_hash_index_find(d, 1); if (!t || Z_TYPE_P(t) != IS_ARRAY) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } if (!engine->algo->unserialize(engine->status, Z_ARRVAL_P(t))) { - zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(OBJ_CE(&engine->std)->name)); RETURN_THROWS(); } } diff --git a/ext/random/random.c b/ext/random/random.c index f34d55f340727..d90b94216eca8 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -277,7 +277,7 @@ PHPAPI void php_random_engine_common_free_object(zend_object *object) PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object) { php_random_engine *old_engine = php_random_engine_from_obj(object); - php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce)); + php_random_engine *new_engine = php_random_engine_from_obj(OBJ_CE(&old_engine->std)->create_object(OBJ_CE(&old_engine->std))); new_engine->algo = old_engine->algo; if (old_engine->status) { diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index fe9ad5fc35a9c..9dc6ec61848a9 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -28,7 +28,7 @@ #include "Zend/zend_exceptions.h" static inline void randomizer_common_init(php_random_randomizer *randomizer, zend_object *engine_object) { - if (engine_object->ce->type == ZEND_INTERNAL_CLASS) { + if (OBJ_CE(engine_object)->type == ZEND_INTERNAL_CLASS) { /* Internal classes always php_random_engine struct */ php_random_engine *engine = php_random_engine_from_obj(engine_object); @@ -43,7 +43,7 @@ static inline void randomizer_common_init(php_random_randomizer *randomizer, zen zend_function *generate_method; mname = ZSTR_INIT_LITERAL("generate", 0); - generate_method = zend_hash_find_ptr(&engine_object->ce->function_table, mname); + generate_method = zend_hash_find_ptr(&OBJ_CE(engine_object)->function_table, mname); zend_string_release(mname); /* Create compatible state */ @@ -503,7 +503,7 @@ PHP_METHOD(Random_Randomizer, __unserialize) RETURN_THROWS(); } - zengine = zend_read_property(randomizer->std.ce, &randomizer->std, "engine", strlen("engine"), 1, NULL); + zengine = zend_read_property(OBJ_CE(&randomizer->std), &randomizer->std, "engine", strlen("engine"), 1, NULL); if (Z_TYPE_P(zengine) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(zengine), random_ce_Random_Engine)) { zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); RETURN_THROWS(); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 09cbf2bd2065b..85c2349976503 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -105,7 +105,7 @@ PHPAPI zend_class_entry *reflection_fiber_ptr; #define GET_REFLECTION_OBJECT() do { \ intern = Z_REFLECTION_P(ZEND_THIS); \ if (intern->ptr == NULL) { \ - if (EG(exception) && EG(exception)->ce == reflection_exception_ptr) { \ + if (EG(exception) && OBJ_CE(EG(exception)) == reflection_exception_ptr) { \ RETURN_THROWS(); \ } \ zend_throw_error(NULL, "Internal error: Failed to retrieve the reflection object"); \ @@ -645,7 +645,7 @@ static int format_default_value(smart_str *str, zval *value) { } else if (Z_TYPE_P(value) == IS_OBJECT) { /* This branch may only be reached for default properties, which don't support arbitrary objects. */ zend_object *obj = Z_OBJ_P(value); - zend_class_entry *class = obj->ce; + zend_class_entry *class = OBJ_CE(obj); ZEND_ASSERT(class->ce_flags & ZEND_ACC_ENUM); smart_str_append(str, class->name); smart_str_appends(str, "::"); @@ -3236,7 +3236,7 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_ } orig_obj = arg1_obj; - ce = arg1_obj->ce; + ce = OBJ_CE(arg1_obj); method_name = ZSTR_VAL(arg2_str); method_name_len = ZSTR_LEN(arg2_str); } else if (arg2_str) { @@ -3779,7 +3779,7 @@ ZEND_METHOD(ReflectionClassConstant, __construct) ZEND_PARSE_PARAMETERS_END(); if (classname_obj) { - ce = classname_obj->ce; + ce = OBJ_CE(classname_obj); } else { if ((ce = zend_lookup_class(classname_str)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(classname_str)); @@ -4047,8 +4047,8 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob intern = Z_REFLECTION_P(object); if (arg_obj) { - ZVAL_STR_COPY(reflection_prop_name(object), arg_obj->ce->name); - intern->ptr = arg_obj->ce; + ZVAL_STR_COPY(reflection_prop_name(object), OBJ_CE(arg_obj)->name); + intern->ptr = OBJ_CE(arg_obj); if (is_object) { ZVAL_OBJ_COPY(&intern->obj, arg_obj); } @@ -5518,7 +5518,7 @@ ZEND_METHOD(ReflectionProperty, __construct) intern = Z_REFLECTION_P(object); if (classname_obj) { - ce = classname_obj->ce; + ce = OBJ_CE(classname_obj); } else { if ((ce = zend_lookup_class(classname_str)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(classname_str)); @@ -6719,7 +6719,7 @@ static int call_attribute_constructor( EG(current_execute_data) = call; } - zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params); + zend_call_known_function(ctor, obj, OBJ_CE(obj), NULL, argc, args, named_params); if (filename) { EG(current_execute_data) = call->prev_execute_data; @@ -7183,11 +7183,11 @@ ZEND_METHOD(ReflectionFiber, getCallable) /* {{{ _reflection_write_property */ static zval *_reflection_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { - if (zend_hash_exists(&object->ce->properties_info, name) + if (zend_hash_exists(&OBJ_CE(object)->properties_info, name) && (zend_string_equals(name, ZSTR_KNOWN(ZEND_STR_NAME)) || zend_string_equals(name, ZSTR_KNOWN(ZEND_STR_CLASS)))) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Cannot set read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); + "Cannot set read-only property %s::$%s", ZSTR_VAL(OBJ_CE(object)->name), ZSTR_VAL(name)); return &EG(uninitialized_zval); } else diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 3de8df9d11968..97fbcd3df4be8 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -230,7 +230,7 @@ static zend_object *spl_array_object_clone(zend_object *old_object) { zend_object *new_object; - new_object = spl_array_object_new_ex(old_object->ce, old_object, 1); + new_object = spl_array_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -412,7 +412,7 @@ static zval *spl_array_read_dimension_ex(int check_inherited, zend_object *objec ZVAL_UNDEF(&tmp); offset = &tmp; } - zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_get, "offsetGet", rv, offset); + zend_call_method_with_1_params(object, OBJ_CE(object), &intern->fptr_offset_get, "offsetGet", rv, offset); if (!Z_ISUNDEF_P(rv)) { return rv; @@ -421,7 +421,7 @@ static zval *spl_array_read_dimension_ex(int check_inherited, zend_object *objec } } - ret = spl_array_get_dimension_ptr(check_inherited, intern, object->ce->name, offset, type); + ret = spl_array_get_dimension_ptr(check_inherited, intern, OBJ_CE(object)->name, offset, type); /* When in a write context, * ZE has to be fooled into thinking this is in a reference set @@ -471,7 +471,7 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec ZVAL_NULL(&tmp); offset = &tmp; } - zend_call_method_with_2_params(object, object->ce, &intern->fptr_offset_set, "offsetSet", NULL, offset, value); + zend_call_method_with_2_params(object, OBJ_CE(object), &intern->fptr_offset_set, "offsetSet", NULL, offset, value); return; } @@ -495,7 +495,7 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec } if (get_hash_key(&key, intern, offset) == FAILURE) { - zend_illegal_container_offset(object->ce->name, offset, BP_VAR_W); + zend_illegal_container_offset(OBJ_NAME(object), offset, BP_VAR_W); zval_ptr_dtor(value); return; } @@ -526,7 +526,7 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec spl_hash_key key; if (check_inherited && intern->fptr_offset_del) { - zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_del, "offsetUnset", NULL, offset); + zend_call_method_with_1_params(object, OBJ_CE(object), &intern->fptr_offset_del, "offsetUnset", NULL, offset); return; } @@ -536,7 +536,7 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec } if (get_hash_key(&key, intern, offset) == FAILURE) { - zend_illegal_container_offset(object->ce->name, offset, BP_VAR_UNSET); + zend_illegal_container_offset(OBJ_NAME(object), offset, BP_VAR_UNSET); return; } @@ -585,7 +585,7 @@ static bool spl_array_has_dimension_ex(bool check_inherited, zend_object *object zval rv, *value = NULL, *tmp; if (check_inherited && intern->fptr_offset_has) { - zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_has, "offsetExists", &rv, offset); + zend_call_method_with_1_params(object, OBJ_CE(object), &intern->fptr_offset_has, "offsetExists", &rv, offset); if (!zend_is_true(&rv)) { zval_ptr_dtor(&rv); @@ -606,7 +606,7 @@ static bool spl_array_has_dimension_ex(bool check_inherited, zend_object *object spl_hash_key key; if (get_hash_key(&key, intern, offset) == FAILURE) { - zend_illegal_container_offset(object->ce->name, offset, BP_VAR_IS); + zend_illegal_container_offset(OBJ_CE(object)->name, offset, BP_VAR_IS); return 0; } @@ -844,7 +844,7 @@ static zval *spl_array_get_property_ptr_ptr(zend_object *object, zend_string *na return NULL; } ZVAL_STR(&member, name); - return spl_array_get_dimension_ptr(1, intern, object->ce->name, &member, type); + return spl_array_get_dimension_ptr(1, intern, OBJ_CE(object)->name, &member, type); } return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); } /* }}} */ @@ -970,7 +970,7 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar if (handler != zend_std_get_properties) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Overloaded object of type %s is not compatible with %s", - ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(intern->std.ce->name)); + ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(OBJ_CE(&intern->std)->name)); return; } zval_ptr_dtor(&intern->array); @@ -1136,7 +1136,7 @@ static zend_result spl_array_object_count_elements(zend_object *object, zend_lon if (intern->fptr_count) { zval rv; - zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv); + zend_call_method_with_0_params(object, OBJ_CE(&intern->std), &intern->fptr_count, "count", &rv); if (Z_TYPE(rv) != IS_UNDEF) { *count = zval_get_long(&rv); zval_ptr_dtor(&rv); diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 528dc5ecc4720..330f3ac4fae70 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -379,7 +379,7 @@ static zend_result spl_filesystem_file_open(spl_filesystem_object *intern, bool intern->u.file.enclosure = '"'; intern->u.file.escape = (unsigned char) '\\'; - intern->u.file.func_getCurr = zend_hash_str_find_ptr(&intern->std.ce->function_table, "getcurrentline", sizeof("getcurrentline") - 1); + intern->u.file.func_getCurr = zend_hash_str_find_ptr(&OBJ_CE(&intern->std)->function_table, "getcurrentline", sizeof("getcurrentline") - 1); return SUCCESS; } /* }}} */ @@ -398,7 +398,7 @@ static zend_object *spl_filesystem_object_clone(zend_object *old_object) spl_filesystem_object *source; source = spl_filesystem_from_obj(old_object); - new_object = spl_filesystem_object_new_ex(old_object->ce); + new_object = spl_filesystem_object_new_ex(OBJ_CE(old_object)); intern = spl_filesystem_from_obj(new_object); intern->flags = source->flags; diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 3cbb5704efaeb..976c2fbf6340c 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -385,7 +385,7 @@ static zend_object *spl_dllist_object_new(zend_class_entry *class_type) /* {{{ * static zend_object *spl_dllist_object_clone(zend_object *old_object) /* {{{ */ { - zend_object *new_object = spl_dllist_object_new_ex(old_object->ce, old_object, 1); + zend_object *new_object = spl_dllist_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -399,7 +399,7 @@ static zend_result spl_dllist_object_count_elements(zend_object *object, zend_lo if (intern->fptr_count) { zval rv; - zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv); + zend_call_method_with_0_params(object, OBJ_CE(&intern->std), &intern->fptr_count, "count", &rv); if (!Z_ISUNDEF(rv)) { *count = zval_get_long(&rv); zval_ptr_dtor(&rv); diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index d59033d420227..6a2ee7ce50b40 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -42,7 +42,7 @@ ZEND_GET_MODULE(spl_fixedarray) /* Check if the object is an instance of a subclass of SplFixedArray that overrides method's implementation. * Expect subclassing SplFixedArray to be rare and check that first. */ -#define HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, method) UNEXPECTED((object)->ce != spl_ce_SplFixedArray && (object)->ce->arrayaccess_funcs_ptr->method->common.scope != spl_ce_SplFixedArray) +#define HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, method) UNEXPECTED(OBJ_CE(object) != spl_ce_SplFixedArray && OBJ_CE(object)->arrayaccess_funcs_ptr->method->common.scope != spl_ce_SplFixedArray) typedef struct _spl_fixedarray { zend_long size; @@ -237,7 +237,7 @@ static HashTable* spl_fixedarray_object_get_properties_for(zend_object *obj, zen * SplFixedArray can be subclassed or have dynamic properties (With or without AllowDynamicProperties in subclasses). * Instances of subclasses with declared properties may have properties but not yet have a property table. */ - HashTable *source_properties = obj->properties ? obj->properties : (obj->ce->default_properties_count ? zend_std_get_properties(obj) : NULL); + HashTable *source_properties = obj->properties ? obj->properties : (OBJ_CE(obj)->default_properties_count ? zend_std_get_properties(obj) : NULL); const zend_long size = intern->array.size; if (size == 0 && (!source_properties || !zend_hash_num_elements(source_properties))) { @@ -310,7 +310,7 @@ static zend_object *spl_fixedarray_new(zend_class_entry *class_type) static zend_object *spl_fixedarray_object_clone(zend_object *old_object) { - zend_object *new_object = spl_fixedarray_object_new_ex(old_object->ce, old_object, 1); + zend_object *new_object = spl_fixedarray_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -387,7 +387,7 @@ static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *off ZVAL_NULL(&tmp); offset = &tmp; } - zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetget, object, rv, offset); + zend_call_known_instance_method_with_1_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetget, object, rv, offset); if (!Z_ISUNDEF_P(rv)) { return rv; } @@ -435,7 +435,7 @@ static void spl_fixedarray_object_write_dimension(zend_object *object, zval *off ZVAL_NULL(&tmp); offset = &tmp; } - zend_call_known_instance_method_with_2_params(object->ce->arrayaccess_funcs_ptr->zf_offsetset, object, NULL, offset, value); + zend_call_known_instance_method_with_2_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetset, object, NULL, offset, value); return; } @@ -464,7 +464,7 @@ static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object * static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *offset) { if (UNEXPECTED(HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetunset))) { - zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetunset, object, NULL, offset); + zend_call_known_instance_method_with_1_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetunset, object, NULL, offset); return; } @@ -497,7 +497,7 @@ static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetexists)) { zval rv; - zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetexists, object, &rv, offset); + zend_call_known_instance_method_with_1_params(OBJ_CE(object)->arrayaccess_funcs_ptr->zf_offsetexists, object, &rv, offset); bool result = zend_is_true(&rv); zval_ptr_dtor(&rv); return result; diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 0780961c49fa9..048980257628d 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -124,7 +124,7 @@ static void spl_ptr_heap_pqueue_elem_ctor(void *elem) { /* {{{ */ static zend_result spl_ptr_heap_cmp_cb_helper(zval *object, spl_heap_object *heap_object, zval *a, zval *b, zend_long *result) { /* {{{ */ zval zresult; - zend_call_method_with_2_params(Z_OBJ_P(object), heap_object->std.ce, &heap_object->fptr_cmp, "compare", &zresult, a, b); + zend_call_method_with_2_params(Z_OBJ_P(object), OBJ_CE(&heap_object->std), &heap_object->fptr_cmp, "compare", &zresult, a, b); if (EG(exception)) { return FAILURE; @@ -475,7 +475,7 @@ static zend_object *spl_heap_object_new(zend_class_entry *class_type) /* {{{ */ static zend_object *spl_heap_object_clone(zend_object *old_object) /* {{{ */ { - zend_object *new_object = spl_heap_object_new_ex(old_object->ce, old_object, 1); + zend_object *new_object = spl_heap_object_new_ex(OBJ_CE(old_object), old_object, 1); zend_objects_clone_members(new_object, old_object); @@ -489,7 +489,7 @@ static zend_result spl_heap_object_count_elements(zend_object *object, zend_long if (intern->fptr_count) { zval rv; - zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv); + zend_call_method_with_0_params(object, OBJ_CE(&intern->std), &intern->fptr_count, "count", &rv); if (!Z_ISUNDEF(rv)) { *count = zval_get_long(&rv); zval_ptr_dtor(&rv); diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 482fb80acab8b..9eb57beabd62d 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -929,7 +929,7 @@ static zend_function *spl_recursive_it_get_method(zend_object **zobject, zend_st zval *zobj; if (!object->iterators) { - zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL((*zobject)->ce->name)); + zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL(OBJ_CE(*zobject)->name)); return NULL; } zobj = &object->iterators[level].zobject; @@ -1656,7 +1656,7 @@ static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern) zval retval; while (spl_dual_it_fetch(intern, 1) == SUCCESS) { - zend_call_method_with_0_params(Z_OBJ_P(zthis), intern->std.ce, NULL, "accept", &retval); + zend_call_method_with_0_params(Z_OBJ_P(zthis), OBJ_CE(&intern->std), NULL, "accept", &retval); if (Z_TYPE(retval) != IS_UNDEF) { if (zend_is_true(&retval)) { zval_ptr_dtor(&retval); @@ -1887,7 +1887,7 @@ PHP_METHOD(RegexIterator, accept) break; case REGIT_MODE_REPLACE: { - zval *replacement = zend_read_property(intern->std.ce, Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv); + zval *replacement = zend_read_property(OBJ_CE(&intern->std), Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv); zend_string *replacement_str = zval_try_get_string(replacement); if (UNEXPECTED(!replacement_str)) { diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index e9ccb36677794..75880046258c7 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -88,7 +88,7 @@ static zend_result spl_object_storage_get_hash(zend_hash_key *key, spl_SplObject zval rv; ZVAL_OBJ(¶m, obj); zend_call_method_with_1_params( - &intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, ¶m); + &intern->std, OBJ_CE(&intern->std), &intern->fptr_get_hash, "getHash", &rv, ¶m); if (!Z_ISUNDEF(rv)) { if (Z_TYPE(rv) == IS_STRING) { key->key = Z_STR(rv); @@ -303,7 +303,7 @@ static zend_object *spl_object_storage_clone(zend_object *old_object) { zend_object *new_object; - new_object = spl_object_storage_new_ex(old_object->ce, old_object); + new_object = spl_object_storage_new_ex(OBJ_CE(old_object), old_object); zend_objects_clone_members(new_object, old_object); @@ -375,15 +375,9 @@ static int spl_object_storage_compare_info(zval *e1, zval *e2) /* {{{ */ static int spl_object_storage_compare_objects(zval *o1, zval *o2) /* {{{ */ { - zend_object *zo1; - zend_object *zo2; - ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2); - zo1 = (zend_object *)Z_OBJ_P(o1); - zo2 = (zend_object *)Z_OBJ_P(o2); - - if (zo1->ce != spl_ce_SplObjectStorage || zo2->ce != spl_ce_SplObjectStorage) { + if (Z_OBJCE_P(o1) != spl_ce_SplObjectStorage || Z_OBJCE_P(o2) != spl_ce_SplObjectStorage) { return ZEND_UNCOMPARABLE; } @@ -1144,7 +1138,7 @@ PHP_METHOD(MultipleIterator, rewind) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_rewind, it, NULL); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_rewind, it, NULL); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1165,7 +1159,7 @@ PHP_METHOD(MultipleIterator, next) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_next, it, NULL); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_next, it, NULL); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1194,7 +1188,7 @@ PHP_METHOD(MultipleIterator, valid) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_valid, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_valid, it, &retval); if (!Z_ISUNDEF(retval)) { valid = (Z_TYPE(retval) == IS_TRUE); @@ -1232,7 +1226,7 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_valid, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_valid, it, &retval); if (!Z_ISUNDEF(retval)) { valid = Z_TYPE(retval) == IS_TRUE; @@ -1243,9 +1237,9 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ if (valid) { if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) { - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_current, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_current, it, &retval); } else { - zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_key, it, &retval); + zend_call_known_instance_method_with_0_params(OBJ_CE(it)->iterator_funcs_ptr->zf_key, it, &retval); } if (Z_ISUNDEF(retval)) { zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0); diff --git a/ext/standard/array.c b/ext/standard/array.c index eee260f224319..2f7a2298646ec 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -678,8 +678,8 @@ PHP_FUNCTION(count) } } /* if not and the object implements Countable we call its count() method */ - if (instanceof_function(zobj->ce, zend_ce_countable)) { - zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); + if (instanceof_function(OBJ_CE(zobj), zend_ce_countable)) { + zend_function *count_fn = zend_hash_find_ptr(&OBJ_CE(zobj)->function_table, ZSTR_KNOWN(ZEND_STR_COUNT)); zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval); if (Z_TYPE(retval) != IS_UNDEF) { RETVAL_LONG(zval_get_long(&retval)); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 4bf49c226ca39..360995d491c44 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -39,6 +39,7 @@ #include "ext/standard/php_dns.h" #include "ext/standard/php_uuencode.h" #include "ext/standard/crc32_x86.h" +#include "zend_types.h" #ifdef PHP_WIN32 #include "win32/php_win32_globals.h" @@ -1518,7 +1519,7 @@ PHP_FUNCTION(forward_static_call) zval retval; zend_fcall_info fci; zend_fcall_info_cache fci_cache; - zend_class_entry *called_scope; + zend_class_reference *called_scope; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_FUNC(fci, fci_cache) @@ -1534,8 +1535,8 @@ PHP_FUNCTION(forward_static_call) called_scope = zend_get_called_scope(execute_data); if (called_scope && fci_cache.calling_scope && - instanceof_function(called_scope, fci_cache.calling_scope)) { - fci_cache.called_scope = called_scope; + instanceof_function(called_scope->ce, fci_cache.calling_scope)) { + fci_cache.called_scope = called_scope->ce; } if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { @@ -1554,7 +1555,7 @@ PHP_FUNCTION(forward_static_call_array) HashTable *params; zend_fcall_info fci; zend_fcall_info_cache fci_cache; - zend_class_entry *called_scope; + zend_class_reference *called_scope; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_FUNC(fci, fci_cache) @@ -1567,8 +1568,8 @@ PHP_FUNCTION(forward_static_call_array) called_scope = zend_get_called_scope(execute_data); if (called_scope && fci_cache.calling_scope && - instanceof_function(called_scope, fci_cache.calling_scope)) { - fci_cache.called_scope = called_scope; + instanceof_function(called_scope->ce, fci_cache.calling_scope)) { + fci_cache.called_scope = called_scope->ce; } if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c index 31e5c57435639..e1eb5ab18fe48 100644 --- a/ext/standard/browscap.c +++ b/ext/standard/browscap.c @@ -796,7 +796,7 @@ PHP_FUNCTION(get_browser) if (return_array) { RETVAL_ARR(agent_ht); } else { - object_and_properties_init(return_value, zend_standard_class_def, agent_ht); + object_and_properties_init(return_value, ZEND_CE_TO_REF(zend_standard_class_def), agent_ht); } HashTable *target_ht = return_array ? Z_ARRVAL_P(return_value) : Z_OBJPROP_P(return_value); diff --git a/ext/standard/incomplete_class.c b/ext/standard/incomplete_class.c index 228e03fbe863e..61586c6f08a50 100644 --- a/ext/standard/incomplete_class.c +++ b/ext/standard/incomplete_class.c @@ -100,7 +100,7 @@ static zend_object *php_create_incomplete_object(zend_class_entry *class_type) { zend_object *object; - object = zend_objects_new( class_type); + object = zend_objects_new(class_type); object->handlers = &php_incomplete_object_handlers; object_properties_init(object, class_type); diff --git a/ext/standard/type.c b/ext/standard/type.c index 6cd72fc744ca4..1b561439ea650 100644 --- a/ext/standard/type.c +++ b/ext/standard/type.c @@ -61,11 +61,11 @@ PHP_FUNCTION(get_debug_type) case IS_ARRAY: RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_ARRAY)); case IS_OBJECT: - if (Z_OBJ_P(arg)->ce->ce_flags & ZEND_ACC_ANON_CLASS) { - name = ZSTR_VAL(Z_OBJ_P(arg)->ce->name); + if (Z_OBJCE_P(arg)->ce_flags & ZEND_ACC_ANON_CLASS) { + name = ZSTR_VAL(Z_OBJCE_P(arg)->name); RETURN_NEW_STR(zend_string_init(name, strlen(name), 0)); } else { - RETURN_STR_COPY(Z_OBJ_P(arg)->ce->name); + RETURN_STR_COPY(Z_OBJCE_P(arg)->name); } case IS_RESOURCE: name = zend_rsrc_list_get_rsrc_type(Z_RES_P(arg)); diff --git a/ext/standard/var.c b/ext/standard/var.c index 686ecd9ee7521..79b300aae3dbe 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -780,7 +780,7 @@ static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function * if (Z_TYPE(retval) != IS_ARRAY) { zval_ptr_dtor(&retval); - php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(obj->ce->name)); + php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(OBJ_NAME(obj))); return NULL; } @@ -1171,7 +1171,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ && Z_OBJ_HT_P(struc)->get_properties == zend_std_get_properties) { /* Optimized version without rebulding properties HashTable */ zend_object *obj = Z_OBJ_P(struc); - zend_class_entry *ce = obj->ce; + zend_class_entry *ce = OBJ_CE(obj); zend_property_info *prop_info; zval *prop; int i; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 3f3d7b4064bc1..f333a35d0dcad 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -270,9 +270,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) ZVAL_UNDEF(&fci.function_name); fci_cache.function_handler = zend_hash_find_ptr( - &fci.object->ce->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); + &OBJ_CE(fci.object)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); fci_cache.object = fci.object; - fci_cache.called_scope = fci.object->ce; + fci_cache.called_scope = OBJ_CE(fci.object); BG(serialize_lock)++; if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { @@ -626,7 +626,7 @@ declared_property: ZVAL_NULL(data); } else { /* Unusual override of dynamic property */ - int ret = is_property_visibility_changed(obj->ce, &key); + int ret = is_property_visibility_changed(OBJ_CE(obj), &key); if (ret > 0) { goto second_try; @@ -638,17 +638,17 @@ declared_property: } } } else { - int ret = is_property_visibility_changed(obj->ce, &key); + int ret = is_property_visibility_changed(OBJ_CE(obj), &key); if (EXPECTED(!ret)) { - if (UNEXPECTED(obj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + if (UNEXPECTED(OBJ_CE(obj)->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", - ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key))); + ZSTR_VAL(OBJ_NAME(obj)), zend_get_unmangled_property_name(Z_STR_P(&key))); zval_ptr_dtor_str(&key); goto failure; - } else if (!(obj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + } else if (!(OBJ_CE(obj)->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", - ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key))); + ZSTR_VAL(OBJ_NAME(obj)), zend_get_unmangled_property_name(Z_STR_P(&key))); if (EG(exception)) { zval_ptr_dtor_str(&key); goto failure; diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index b242e5345a180..9558b29158ab5 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -716,7 +716,7 @@ static inline void phpdbg_handle_exception(void) /* {{{ */ EG(exception) = NULL; - zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp); + zend_call_known_instance_method_with_0_params(OBJ_CE(ex)->__tostring, ex, &tmp); file = zval_get_string(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_FILE), /* silent */ true, &rv)); line = zval_get_long(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_LINE), /* silent */ true, &rv)); @@ -729,7 +729,7 @@ static inline void phpdbg_handle_exception(void) /* {{{ */ msg = zval_get_string(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_STRING), /* silent */ true, &rv)); } - phpdbg_error("Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(ex->ce->name), ZSTR_VAL(file), line); + phpdbg_error("Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(OBJ_CE(ex)->name), ZSTR_VAL(file), line); zend_string_release(file); phpdbg_writeln("%s", ZSTR_VAL(msg)); zend_string_release(msg); @@ -1032,7 +1032,7 @@ PHPDBG_COMMAND(generator) /* {{{ */ if (param) { i = param->num; zend_object **obj = EG(objects_store).object_buckets + i; - if (i < EG(objects_store).top && *obj && IS_OBJ_VALID(*obj) && (*obj)->ce == zend_ce_generator) { + if (i < EG(objects_store).top && *obj && IS_OBJ_VALID(*obj) && OBJ_CE(*obj) == zend_ce_generator) { zend_generator *gen = (zend_generator *) *obj; if (gen->execute_data) { if (zend_generator_get_current(gen)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) { @@ -1049,7 +1049,7 @@ PHPDBG_COMMAND(generator) /* {{{ */ } else { for (i = 0; i < EG(objects_store).top; i++) { zend_object *obj = EG(objects_store).object_buckets[i]; - if (obj && IS_OBJ_VALID(obj) && obj->ce == zend_ce_generator) { + if (obj && IS_OBJ_VALID(obj) && OBJ_CE(obj) == zend_ce_generator) { zend_generator *gen = (zend_generator *) obj, *current = zend_generator_get_current(gen); if (gen->execute_data) { zend_string *s = phpdbg_compile_stackframe(gen->execute_data); @@ -1700,7 +1700,7 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ zend_string *msg = zval_get_string(zend_read_property_ex(zend_get_exception_base(exception), exception, ZSTR_KNOWN(ZEND_STR_MESSAGE), /* silent */ true, &rv)); phpdbg_error("Uncaught %s in %s on line " ZEND_LONG_FMT ": %.*s", - ZSTR_VAL(exception->ce->name), ZSTR_VAL(file), line, + ZSTR_VAL(OBJ_NAME(exception)), ZSTR_VAL(file), line, ZSTR_LEN(msg) < 80 ? (int) ZSTR_LEN(msg) : 80, ZSTR_VAL(msg)); zend_string_release(msg); zend_string_release(file); diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index 964d82ef6c8d5..439e3beda84eb 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -638,7 +638,7 @@ PHPDBG_API bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_obj CACHE_PTR(cur->extended_value & ~ZEND_LAST_CATCH, ce); } - if (ce == exception->ce || (ce && instanceof_function(exception->ce, ce))) { + if (ce == OBJ_CE(exception) || (ce && instanceof_function(OBJ_CE(exception), ce))) { return 1; } From fcaa55475d7fe82f644f47faad1afc313cdb303e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 9 Jan 2024 15:11:45 +0100 Subject: [PATCH 03/10] Ensure that statically stored class entries have an header --- Zend/zend.h | 4 ++++ Zend/zend_API.c | 2 +- Zend/zend_compile.c | 4 ++-- Zend/zend_compile.h | 2 +- Zend/zend_exceptions.c | 22 +++++++++++++--------- Zend/zend_iterators.c | 10 ++++++---- 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Zend/zend.h b/Zend/zend.h index 05188b39672fd..026094feddb8d 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -253,6 +253,10 @@ struct _zend_class_entry { } info; }; +typedef char zend_class_entry_storage[ZEND_CLASS_ENTRY_HEADER_SIZE + sizeof(zend_class_entry)]; + +#define ZEND_CES_TO_CE(ces) ((zend_class_entry*)((char*) (ces) + ZEND_CLASS_ENTRY_HEADER_SIZE)) + typedef struct _zend_utility_functions { void (*error_function)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); size_t (*printf_function)(const char *format, ...) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 1, 2); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5c9ce7ce19928..cc7fc4cb8659d 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3359,7 +3359,7 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */ { - void *ref = malloc(sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); + zend_class_entry_storage *ref = (zend_class_entry_storage*) malloc(sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); zend_class_entry *class_entry = zend_init_class_entry_header(ref); zend_string *lowercase_name; *class_entry = *orig_class_entry; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c961766bae92d..48ccc0a54ec21 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8270,8 +8270,8 @@ static void zend_compile_generic_params(zend_ast *params_ast) } } -zend_class_entry *zend_init_class_entry_header(void *ptr) { - zend_class_reference *ref = ptr; +zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr) { + zend_class_reference *ref = (zend_class_reference*) ptr; zend_class_entry *ce = (zend_class_entry *) ((char *) ptr + ZEND_CLASS_ENTRY_HEADER_SIZE); ref->ce = ce; ref->args.num_types = 0; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d2f9998a5ffc0..d612d80f770da 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -909,7 +909,7 @@ ZEND_API void pass_two(zend_op_array *op_array); ZEND_API bool zend_is_compiling(void); ZEND_API char *zend_make_compiled_string_description(const char *name); ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers); -zend_class_entry *zend_init_class_entry_header(void *ptr); +zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr); uint32_t zend_get_class_fetch_type(const zend_string *name); ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc); ZEND_API bool zend_is_smart_branch(const zend_op *opline); diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index aea36e8461eac..bf5783f687bd6 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -45,10 +45,12 @@ ZEND_API zend_class_entry *zend_ce_division_by_zero_error; ZEND_API zend_class_entry *zend_ce_unhandled_match_error; /* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */ -static zend_class_entry zend_ce_unwind_exit; +static zend_class_entry_storage zend_ces_unwind_exit; +#define zend_ce_unwind_exit ZEND_CES_TO_CE(zend_ces_unwind_exit) /* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does* execute finally blocks. */ -static zend_class_entry zend_ce_graceful_exit; +static zend_class_entry_storage zend_ces_graceful_exit; +#define zend_ce_graceful_exit ZEND_CES_TO_CE(zend_ces_graceful_exit) ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); @@ -795,9 +797,11 @@ void zend_register_default_exception(void) /* {{{ */ zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error); zend_init_exception_class_entry(zend_ce_unhandled_match_error); - INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); + INIT_CLASS_ENTRY((*zend_ce_unwind_exit), "UnwindExit", NULL); + zend_init_class_entry_header(&zend_ces_unwind_exit); - INIT_CLASS_ENTRY(zend_ce_graceful_exit, "GracefulExit", NULL); + INIT_CLASS_ENTRY((*zend_ce_graceful_exit), "GracefulExit", NULL); + zend_init_class_entry_header(&zend_ces_graceful_exit); } /* }}} */ @@ -958,7 +962,7 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_string_release_ex(str, 0); zend_string_release_ex(file, 0); - } else if (ce_exception == &zend_ce_unwind_exit || ce_exception == &zend_ce_graceful_exit) { + } else if (ce_exception == zend_ce_unwind_exit || ce_exception == zend_ce_graceful_exit) { /* We successfully unwound, nothing more to do. * We still return FAILURE in this case, as further execution should still be aborted. */ } else { @@ -1006,12 +1010,12 @@ ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */ ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void) { - return zend_objects_new(&zend_ce_unwind_exit); + return zend_objects_new(zend_ce_unwind_exit); } ZEND_API ZEND_COLD zend_object *zend_create_graceful_exit(void) { - return zend_objects_new(&zend_ce_graceful_exit); + return zend_objects_new(zend_ce_graceful_exit); } ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) @@ -1032,10 +1036,10 @@ ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) ZEND_API bool zend_is_unwind_exit(const zend_object *ex) { - return OBJ_CE(ex) == &zend_ce_unwind_exit; + return ex->cr == (zend_class_reference*) &zend_ces_unwind_exit; } ZEND_API bool zend_is_graceful_exit(const zend_object *ex) { - return OBJ_CE(ex) == &zend_ce_graceful_exit; + return ex->cr == (zend_class_reference*) &zend_ces_graceful_exit; } diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index f67033b11161c..9f0a3be471561 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -20,7 +20,8 @@ #include "zend.h" #include "zend_API.h" -static zend_class_entry zend_iterator_class_entry; +static zend_class_entry_storage zend_iterator_class_entry_storage; +#define zend_iterator_class_entry ZEND_CES_TO_CE(&zend_iterator_class_entry_storage) static void iter_wrapper_free(zend_object *object); static void iter_wrapper_dtor(zend_object *object); @@ -56,8 +57,9 @@ static const zend_object_handlers iterator_object_handlers = { ZEND_API void zend_register_iterator_wrapper(void) { - INIT_CLASS_ENTRY(zend_iterator_class_entry, "__iterator_wrapper", NULL); - zend_iterator_class_entry.default_object_handlers = &iterator_object_handlers; + INIT_CLASS_ENTRY((*zend_iterator_class_entry), "__iterator_wrapper", NULL); + zend_iterator_class_entry->default_object_handlers = &iterator_object_handlers; + zend_init_class_entry_header(&zend_iterator_class_entry_storage); } static void iter_wrapper_free(zend_object *object) @@ -83,7 +85,7 @@ static HashTable *iter_wrapper_get_gc(zend_object *object, zval **table, int *n) ZEND_API void zend_iterator_init(zend_object_iterator *iter) { - zend_object_std_init(&iter->std, &zend_iterator_class_entry); + zend_object_std_init(&iter->std, zend_iterator_class_entry); } ZEND_API void zend_iterator_dtor(zend_object_iterator *iter) From f384d8a1f719f81234a1ede0257dabb8ac0753ef Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 10 Jan 2024 19:09:35 +0100 Subject: [PATCH 04/10] Re-use class references --- Zend/zend_API.c | 25 ++-- Zend/zend_API.h | 2 +- Zend/zend_builtin_functions.c | 5 +- Zend/zend_compile.c | 152 +++++++++++++++++++++- Zend/zend_compile.h | 4 +- Zend/zend_constants.c | 6 +- Zend/zend_exceptions.c | 8 +- Zend/zend_execute.c | 14 +- Zend/zend_execute.h | 2 + Zend/zend_execute_API.c | 77 ++++++++++- Zend/zend_globals.h | 1 + Zend/zend_inheritance.c | 5 +- Zend/zend_iterators.c | 3 +- Zend/zend_objects.c | 5 - Zend/zend_opcode.c | 12 ++ Zend/zend_types.h | 27 +++- Zend/zend_vm_def.h | 50 ++++--- Zend/zend_vm_execute.h | 150 +++++++++++++-------- ext/opcache/ZendAccelerator.c | 3 +- ext/opcache/zend_accelerator_util_funcs.c | 3 +- ext/spl/php_spl.c | 4 +- ext/standard/var_unserializer.re | 9 +- 22 files changed, 443 insertions(+), 124 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index cc7fc4cb8659d..e93eec27279de 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1746,8 +1746,11 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) } /* }}} */ -ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, const zend_type_list *type_args) +ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *name_ref, zend_class_entry *ce) { + const zend_type_list *type_args = &name_ref->args; + zend_string *key = name_ref->key; + if (UNEXPECTED(type_args->num_types > ce->num_generic_params)) { zend_throw_error(zend_ce_argument_count_error, "Class %s expects %s %d generic argument%s, but %d provided", @@ -1768,9 +1771,9 @@ ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, return NULL; } - // TODO: re-use class references zend_class_reference *class_ref = emalloc(ZEND_CLASS_REF_SIZE(ce->num_generic_params)); class_ref->ce = ce; + class_ref->key = key; uint8_t param_id = 0; @@ -1785,6 +1788,11 @@ ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, class_ref->args.num_types = param_id; + if (ZSTR_HAS_CE_CACHE(key) && ZSTR_VALID_CE_CACHE(key) && + (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { + ZSTR_SET_CE_CACHE(key, class_ref); + } + return class_ref; } @@ -3360,8 +3368,11 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */ { zend_class_entry_storage *ref = (zend_class_entry_storage*) malloc(sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); - zend_class_entry *class_entry = zend_init_class_entry_header(ref); - zend_string *lowercase_name; + + zend_string *lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT); + lowercase_name = zend_new_interned_string(lowercase_name); + + zend_class_entry *class_entry = zend_init_class_entry_header(ref, lowercase_name); *class_entry = *orig_class_entry; class_entry->type = ZEND_INTERNAL_CLASS; @@ -3374,10 +3385,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); } - lowercase_name = zend_string_tolower_ex(orig_class_entry->name, EG(current_module)->type == MODULE_PERSISTENT); - lowercase_name = zend_new_interned_string(lowercase_name); zend_hash_update_ptr(CG(class_table), lowercase_name, class_entry); - zend_string_release_ex(lowercase_name, 1); if (class_entry->__tostring && !zend_string_equals_literal(class_entry->name, "Stringable") && !(class_entry->ce_flags & ZEND_ACC_TRAIT)) { @@ -3803,7 +3811,8 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_ cname = zend_string_init_interned(Z_STRVAL_P(callable), clen, 0); if (ZSTR_HAS_CE_CACHE(cname) && ZSTR_GET_CE_CACHE(cname)) { - fcc->calling_scope = ZSTR_GET_CE_CACHE(cname); + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(cname); + fcc->calling_scope = class_ref->ce; if (scope && !fcc->object) { zend_object *object = zend_get_this_object(frame); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 7486962833b47..1b46867b2cbda 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -538,7 +538,7 @@ ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_reference ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type); ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties); ZEND_API void object_properties_load(zend_object *object, HashTable *properties); -ZEND_API zend_class_reference *zend_build_class_reference(zend_class_entry *ce, const zend_type_list *type_args); +ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *name_ref, zend_class_entry *ce); ZEND_API void zend_merge_properties(zval *obj, HashTable *properties); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 75be4ef1667f3..1a062a4e83146 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -996,8 +996,9 @@ static inline void class_exists_impl(INTERNAL_FUNCTION_PARAMETERS, int flags, in ZEND_PARSE_PARAMETERS_END(); if (ZSTR_HAS_CE_CACHE(name)) { - ce = ZSTR_GET_CE_CACHE(name); - if (ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name); + if (class_ref) { + ce = class_ref->ce; RETURN_BOOL(((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags)); } } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 48ccc0a54ec21..24219ffd566d2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -633,6 +633,24 @@ static int zend_add_class_name_literal(zend_string *name) /* {{{ */ } /* }}} */ +static int zend_add_pnr_literal(zend_packed_name_reference pnr) /* {{{ */ +{ + zend_string *name = ZEND_PNR_GET_NAME(pnr); + + zval zpnr; + ZVAL_PNR(&zpnr, pnr); + + /* Original PNR */ + int ret = zend_add_literal(&zpnr); + + /* Lowercased name */ + zend_string *lc_name = zend_string_tolower(name); + zend_add_literal_string(&lc_name); + + return ret; +} +/* }}} */ + static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {{{ */ { zend_string *tmp_name; @@ -1517,6 +1535,105 @@ zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry return zend_type_to_string_impl(type, scope, 1); } +#define zend_type_to_key_append_separator() \ + if (smart_str_get_len(key) != orig_len) { \ + smart_str_appendc(key, '|'); \ + } + +static void zend_type_to_key_impl(smart_str *key, zend_type type) { + size_t orig_len = smart_str_get_len(key); + + if (ZEND_TYPE_IS_INTERSECTION(type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + smart_str_appendc(key, '&'); + zend_type_to_key_impl(key, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + return; + } else if (ZEND_TYPE_HAS_LIST(type)) { + /* A union type might not be a list */ + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + smart_str_appendc(key, '|'); + zend_type_to_key_impl(key, *list_type); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_PNR(type)) { + zend_packed_name_reference pnr = ZEND_TYPE_PNR(type); + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_string *lcname = zend_string_tolower(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + smart_str_append(key, lcname); + zend_string_release(lcname); + } else { + smart_str_append(key, ZEND_PNR_COMPLEX_GET_KEY(pnr)); + } + } else if (ZEND_TYPE_HAS_CLASS_REF(type)) { + zend_class_reference *ce_ref = ZEND_TYPE_CLASS_REF(type); + smart_str_append(key, ce_ref->key); + } else if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); + smart_str_appendc(key, 0); + smart_str_appendl(key, (char*)&generic_param_id, sizeof(generic_param_id)); + } + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + + if (type_mask == MAY_BE_ANY) { + smart_str_setl(key, ZSTR_VAL(ZSTR_KNOWN(ZEND_STR_MIXED)), ZSTR_LEN(ZSTR_KNOWN(ZEND_STR_MIXED))); + return; + } + if (type_mask & MAY_BE_STATIC) { + ZEND_ASSERT(0 && "Unsupported yet"); + } + if (type_mask & MAY_BE_CALLABLE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_CALLABLE)); + } + if (type_mask & MAY_BE_OBJECT) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_OBJECT)); + } + if (type_mask & MAY_BE_ARRAY) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_ARRAY)); + } + if (type_mask & MAY_BE_STRING) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_STRING)); + } + if (type_mask & MAY_BE_LONG) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_INT)); + } + if (type_mask & MAY_BE_DOUBLE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_FLOAT)); + } + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_BOOL)); + } else if (type_mask & MAY_BE_FALSE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_FALSE)); + } else if (type_mask & MAY_BE_TRUE) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_TRUE)); + } + if (type_mask & MAY_BE_VOID) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_VOID)); + } + if (type_mask & MAY_BE_NEVER) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_NEVER)); + } + + if (type_mask & MAY_BE_NULL) { + zend_type_to_key_append_separator(); + smart_str_append(key, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE)); + } +} + static bool is_generator_compatible_class_type(zend_string *name) { return zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_TRAVERSABLE)) || zend_string_equals_literal_ci(name, "Iterator") @@ -1780,13 +1897,27 @@ static zend_name_reference *zend_compile_name_reference( size_t alloc_size = ZEND_CLASS_REF_SIZE(num_types); zend_name_reference *ref = zend_arena_alloc(&CG(arena), alloc_size); ref->name = name; - ref->args.num_types = num_types; - if (list) { + if (num_types == 0) { + ref->key = zend_new_interned_string(zend_string_tolower(name)); + } else { + ZEND_ASSERT(list); + smart_str key = {0}; + zend_string *lcname = zend_string_tolower(name); + smart_str_append(&key, lcname); + zend_string_release(lcname); + smart_str_appendc(&key, '<'); for (uint32_t i = 0; i < num_types; i++) { + if (i != 0) { + smart_str_appendc(&key, ','); + } zend_ast *type_ast = list->child[i]; ref->args.types[i] = zend_compile_typename(type_ast); + zend_type_to_key_impl(&key, ref->args.types[i]); } + smart_str_appendc(&key, '>'); + ref->key = zend_new_interned_string(smart_str_extract(&key)); } + ref->args.num_types = num_types; return ref; } @@ -4999,10 +5130,14 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); } - opline = zend_emit_op(result, ZEND_NEW, &class_node, NULL); + opline = zend_emit_op(result, ZEND_NEW, NULL, NULL); if (class_node.op_type == IS_CONST) { + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_pnr_literal(Z_PNR(class_node.u.constant)); opline->op2.num = zend_alloc_cache_slot(); + } else { + SET_NODE(opline->op1, &class_node); } zend_compile_call_common(&ctor_result, args_ast, NULL, ast->lineno); @@ -8270,10 +8405,11 @@ static void zend_compile_generic_params(zend_ast *params_ast) } } -zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr) { +zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, zend_string *key) { zend_class_reference *ref = (zend_class_reference*) ptr; zend_class_entry *ce = (zend_class_entry *) ((char *) ptr + ZEND_CLASS_ENTRY_HEADER_SIZE); ref->ce = ce; + ref->key = key; ref->args.num_types = 0; return ce; } @@ -8287,7 +8423,6 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) zend_string *name, *lcname; void *ce_ref = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry) + ZEND_CLASS_ENTRY_HEADER_SIZE); - zend_class_entry *ce = zend_init_class_entry_header(ce_ref); zend_op *opline; zend_class_entry *original_ce = CG(active_class_entry); @@ -8327,6 +8462,8 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) } lcname = zend_new_interned_string(lcname); + zend_class_entry *ce = zend_init_class_entry_header(ce_ref, lcname); + ce->type = ZEND_USER_CLASS; ce->name = name; zend_initialize_class_data(ce, 1); @@ -8361,6 +8498,11 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) if (generic_params_ast) { zend_compile_generic_params(generic_params_ast); } + + if (!(decl->flags & ZEND_ACC_ANON_CLASS) && + ce->num_generic_params > 0 && ce->num_required_generic_params == 0) { + zend_alloc_ce_cache(ZEND_CE_TO_REF(ce)->key); + } } if (extends_ast) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d612d80f770da..881d75f54beaf 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -885,6 +885,7 @@ void zend_free_internal_arg_info(zend_internal_function *function); ZEND_API void destroy_zend_function(zend_function *function); ZEND_API void zend_function_dtor(zval *zv); ZEND_API void destroy_zend_class(zval *zv); +ZEND_API void destroy_zend_class_reference(zval *zv); void zend_class_add_ref(zval *zv); ZEND_API zend_string *zend_mangle_property_name(const char *src1, size_t src1_length, const char *src2, size_t src2_length, bool internal); @@ -900,6 +901,7 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen #define ZEND_FUNCTION_DTOR zend_function_dtor #define ZEND_CLASS_DTOR destroy_zend_class +#define ZEND_GENERIC_CLASS_DTOR destroy_zend_class_reference typedef bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline); ZEND_API void zend_recalc_live_ranges( @@ -909,7 +911,7 @@ ZEND_API void pass_two(zend_op_array *op_array); ZEND_API bool zend_is_compiling(void); ZEND_API char *zend_make_compiled_string_description(const char *name); ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers); -zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr); +zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, zend_string *key); uint32_t zend_get_class_fetch_type(const zend_string *name); ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc); ZEND_API bool zend_is_smart_branch(const zend_op *opline); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 3936b7b26f568..998cc98f51e81 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -307,8 +307,10 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * zval *ret_constant = NULL; if (ZSTR_HAS_CE_CACHE(class_name)) { - ce = ZSTR_GET_CE_CACHE(class_name); - if (!ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + if (class_ref) { + ce = class_ref->ce; + } else { ce = zend_fetch_class(class_name, flags); } } else if (zend_string_equals_literal_ci(class_name, "self")) { diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index bf5783f687bd6..14c6baf9943fb 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -24,6 +24,8 @@ #include "zend_builtin_functions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" +#include "zend_operators.h" +#include "zend_string.h" #include "zend_types.h" #include "zend_vm.h" #include "zend_dtrace.h" @@ -798,10 +800,12 @@ void zend_register_default_exception(void) /* {{{ */ zend_init_exception_class_entry(zend_ce_unhandled_match_error); INIT_CLASS_ENTRY((*zend_ce_unwind_exit), "UnwindExit", NULL); - zend_init_class_entry_header(&zend_ces_unwind_exit); + zend_init_class_entry_header(&zend_ces_unwind_exit, + zend_new_interned_string(zend_string_tolower_ex(zend_ce_unwind_exit->name, true))); INIT_CLASS_ENTRY((*zend_ce_graceful_exit), "GracefulExit", NULL); - zend_init_class_entry_header(&zend_ces_graceful_exit); + zend_init_class_entry_header(&zend_ces_graceful_exit, + zend_new_interned_string(zend_string_tolower_ex(zend_ce_graceful_exit->name, true))); } /* }}} */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 36c8d03675941..e87b7af0ecd5f 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -987,8 +987,11 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type( ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*type)); zend_string *name = ZEND_TYPE_PNR_NAME(*type); if (ZSTR_HAS_CE_CACHE(name)) { - zend_class_entry *ce = ZSTR_GET_CE_CACHE(name); - if (!ce) { + zend_class_entry *ce; + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name); + if (class_ref) { + ce = class_ref->ce; + } else { ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); } return ce; @@ -1230,9 +1233,12 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( zend_string *name = ZEND_TYPE_PNR_NAME(*type); zend_class_entry *ce; + if (ZSTR_HAS_CE_CACHE(name)) { - ce = ZSTR_GET_CE_CACHE(name); - if (!ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(name); + if (class_ref) { + ce = class_ref->ce; + } else { ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (UNEXPECTED(!ce)) { /* Cannot resolve */ diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 7b1c511d3c473..2ee0c7a17b4e9 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -390,6 +390,8 @@ ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void); ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type); ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, uint32_t fetch_type, zend_class_entry *scope); ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type); +ZEND_API zend_class_reference *zend_fetch_generic_class_by_ref(zend_name_reference *name_ref, zend_string *base_key, uint32_t fetch_type); +ZEND_API zend_class_reference *zend_register_generic_class(zend_name_reference *name_ref, zend_class_entry *ce); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, size_t len); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index b967ba86de0f1..2086ef6124d0b 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -25,6 +25,7 @@ #include "zend_compile.h" #include "zend_execute.h" #include "zend_API.h" +#include "zend_hash.h" #include "zend_stack.h" #include "zend_constants.h" #include "zend_extensions.h" @@ -146,6 +147,7 @@ void init_executor(void) /* {{{ */ EG(function_table) = CG(function_table); EG(class_table) = CG(class_table); + zend_hash_init(&EG(generic_class_table), 8, NULL, ZEND_GENERIC_CLASS_DTOR, 0); EG(in_autoload) = NULL; EG(error_handling) = EH_NORMAL; @@ -318,6 +320,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) } } } ZEND_HASH_FOREACH_END(); + zend_hash_clean(&EG(generic_class_table)); ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -432,6 +435,7 @@ void shutdown_executor(void) /* {{{ */ * each allocated block separately. */ zend_hash_discard(EG(function_table), EG(persistent_functions_count)); + zend_hash_discard(&EG(generic_class_table), 0); zend_hash_discard(EG(class_table), EG(persistent_classes_count)); } else { zend_vm_stack_destroy(); @@ -458,6 +462,9 @@ void shutdown_executor(void) /* {{{ */ } ZEND_HASH_MAP_FOREACH_END_DEL(); } + // TODO: reverse_clean + zend_hash_graceful_reverse_destroy(&EG(generic_class_table)); + while (EG(symtable_cache_ptr) > EG(symtable_cache)) { EG(symtable_cache_ptr)--; zend_hash_destroy(*EG(symtable_cache_ptr)); @@ -1116,6 +1123,55 @@ ZEND_API bool zend_is_valid_class_name(zend_string *name) { return 1; } +ZEND_API zend_class_reference *zend_register_generic_class(zend_name_reference *name_ref, zend_class_entry *ce) +{ + zend_class_reference *class_ref = zend_build_class_reference(name_ref, ce); + if (!UNEXPECTED(class_ref)) { + return NULL; + } + + void *ptr = zend_hash_add_ptr(&EG(generic_class_table), name_ref->key, class_ref); + ZEND_ASSERT(ptr); + + return class_ref; +} + +ZEND_API zend_class_reference *zend_lookup_generic_class(zend_name_reference *name_ref, zend_string *base_key, uint32_t flags) /* {{{ */ +{ + zend_class_entry *ce = NULL; + zval *zv; + zend_string *name = name_ref->name; + zend_string *key = name_ref->key; + uint32_t ce_cache = 0; + + if (ZSTR_HAS_CE_CACHE(key) && ZSTR_VALID_CE_CACHE(key)) { + ce_cache = GC_REFCOUNT(key); + zend_class_reference *class_ref = GET_CE_CACHE(ce_cache); + if (EXPECTED(class_ref)) { + return class_ref; + } + } + + zv = zend_hash_find(&EG(generic_class_table), key); + if (zv) { + zend_class_reference *class_ref = Z_CR_P(zv); + /* Don't populate CE_CACHE for mutable classes during compilation. + * The class may be freed while persisting. */ + if (ce_cache && + (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { + SET_CE_CACHE(ce_cache, class_ref); + } + return class_ref; + } + + ce = zend_lookup_class_ex(name, base_key, flags); + if (!ce) { + return NULL; + } + + return zend_register_generic_class(name_ref, ce); +} + ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */ { zend_class_entry *ce = NULL; @@ -1126,9 +1182,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if (ZSTR_HAS_CE_CACHE(name) && ZSTR_VALID_CE_CACHE(name)) { ce_cache = GC_REFCOUNT(name); - ce = GET_CE_CACHE(ce_cache); - if (EXPECTED(ce)) { - return ce; + zend_class_reference *class_ref = GET_CE_CACHE(ce_cache); + if (EXPECTED(class_ref)) { + return class_ref->ce; } } @@ -1170,7 +1226,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * * The class may be freed while persisting. */ if (ce_cache && (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { - SET_CE_CACHE(ce_cache, ce); + SET_CE_CACHE(ce_cache, ZEND_CE_TO_REF(ce)); } return ce; } @@ -1227,7 +1283,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if (ce) { ZEND_ASSERT(!CG(in_compilation)); if (ce_cache) { - SET_CE_CACHE(ce_cache, ce); + SET_CE_CACHE(ce_cache, ZEND_CE_TO_REF(ce)); } } return ce; @@ -1737,6 +1793,17 @@ zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string } /* }}} */ +zend_class_reference *zend_fetch_generic_class_by_ref(zend_name_reference *name_ref, zend_string *base_key, uint32_t fetch_type) /* {{{ */ +{ + zend_class_reference *class_ref = zend_lookup_generic_class(name_ref, base_key, fetch_type); + if (!class_ref) { + report_class_fetch_error(name_ref->name, fetch_type); + return NULL; + } + return class_ref; +} +/* }}} */ + ZEND_API zend_result zend_delete_global_variable(zend_string *name) /* {{{ */ { return zend_hash_del_ind(&EG(symbol_table), name); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8900a5f416f53..fab2dc2909da4 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -181,6 +181,7 @@ struct _zend_executor_globals { HashTable *function_table; /* function symbol table */ HashTable *class_table; /* class table */ + HashTable generic_class_table;/* generic class table */ HashTable *zend_constants; /* constants table */ zval *vm_stack_top; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index a294ddbe0fa27..d4562368cd85b 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -30,6 +30,7 @@ #include "zend_attributes.h" #include "zend_constants.h" #include "zend_observer.h" +#include "zend_types.h" ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL; @@ -3422,7 +3423,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string } if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE(ce->name, ce); + ZSTR_SET_CE_CACHE(ce->name, ZEND_CE_TO_REF(ce)); } return ce; @@ -3617,7 +3618,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ } if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE(ce->name, ce); + ZSTR_SET_CE_CACHE(ce->name, ZEND_CE_TO_REF(ce)); } zend_observer_class_linked_notify(ce, lcname); diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index 9f0a3be471561..fae4d98991943 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -59,7 +59,8 @@ ZEND_API void zend_register_iterator_wrapper(void) { INIT_CLASS_ENTRY((*zend_iterator_class_entry), "__iterator_wrapper", NULL); zend_iterator_class_entry->default_object_handlers = &iterator_object_handlers; - zend_init_class_entry_header(&zend_iterator_class_entry_storage); + zend_init_class_entry_header(&zend_iterator_class_entry_storage, + zend_new_interned_string(zend_string_tolower_ex(zend_iterator_class_entry->name, true))); } static void iter_wrapper_free(zend_object *object) diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index ceef78705466b..487380b4d78c0 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -93,11 +93,6 @@ ZEND_API void zend_object_std_dtor(zend_object *object) if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { zend_weakrefs_notify(object); } - - if (UNEXPECTED(!ZEND_REF_IS_TRIVIAL(object->cr))) { - // TODO - efree(object->cr); - } } ZEND_API void zend_objects_destroy_object(zend_object *object) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 572810bc18116..917b26912100b 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -29,6 +29,7 @@ #include "zend_constants.h" #include "zend_observer.h" +#include "zend_types.h" #include "zend_vm.h" static void zend_extension_op_array_ctor_handler(zend_extension *extension, zend_op_array *op_array) @@ -309,6 +310,15 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce) } } +ZEND_API void destroy_zend_class_reference(zval *zv) +{ + zend_class_reference *class_ref = Z_CR_P(zv); + + ZEND_ASSERT(!ZEND_REF_IS_TRIVIAL(class_ref)); + + efree(class_ref); +} + ZEND_API void destroy_zend_class(zval *zv) { zend_property_info *prop_info; @@ -353,6 +363,8 @@ ZEND_API void destroy_zend_class(zval *zv) return; } + zend_string_release(ZEND_CE_TO_REF(ce)->key); + switch (ce->type) { case ZEND_USER_CLASS: if (!(ce->ce_flags & ZEND_ACC_CACHED)) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index c4e7a36132f1b..356f7245ee0f1 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -226,6 +226,9 @@ typedef struct { #define ZEND_TYPE_PNR_NAME(t) \ ZEND_PNR_GET_NAME(ZEND_TYPE_PNR(t)) +#define ZEND_TYPE_PNR_KEY(t) \ + ZEND_PNR_GET_KEY(ZEND_TYPE_PNR(t)) + #define ZEND_TYPE_LITERAL_NAME(t) \ ((const char *) (t).ptr) @@ -377,6 +380,7 @@ typedef struct { * a zero-argument reference to itself. */ typedef struct _zend_class_reference { zend_class_entry *ce; + zend_string *key; zend_type_list args; } zend_class_reference; @@ -384,10 +388,11 @@ typedef struct _zend_class_reference { * This should be structurally the same zend_class_reference to permit in-place resolution. */ typedef struct _zend_name_reference { zend_string *name; + zend_string *key; zend_type_list args; } zend_name_reference; -#define ZEND_CLASS_ENTRY_HEADER_SIZE (2 * sizeof(void*)) +#define ZEND_CLASS_ENTRY_HEADER_SIZE (3 * sizeof(void*)) #define ZEND_CE_TO_REF(ce) \ ((zend_class_reference *) ((char *) (ce) - ZEND_CLASS_ENTRY_HEADER_SIZE)) @@ -410,6 +415,7 @@ static const zend_type_list zend_empty_type_list = {0}; #define ZEND_PNR_SIMPLE_GET_NAME(pnr) ((zend_string *) (pnr)) #define ZEND_PNR_COMPLEX_GET_REF(pnr) ((zend_name_reference *) (pnr - 1)) #define ZEND_PNR_COMPLEX_GET_NAME(pnr) ZEND_PNR_COMPLEX_GET_REF(pnr)->name +#define ZEND_PNR_COMPLEX_GET_KEY(pnr) ZEND_PNR_COMPLEX_GET_REF(pnr)->key #define ZEND_PNR_ENCODE_NAME(name) ((uintptr_t) (name)) #define ZEND_PNR_ENCODE_REF(ref) ((uintptr_t) (ref) + 1) @@ -431,6 +437,17 @@ static const zend_type_list zend_empty_type_list = {0}; } \ } while (0) +#define ZEND_PNR_UNPACK_NAME_KEY(pnr, name_var, key_var) do { \ + if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + (name_var) = __ref->name; \ + (key_var) = __ref->key; \ + } else { \ + (name_var) = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + (key_var) = NULL; \ + } \ +} while (0) + typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ @@ -444,6 +461,7 @@ typedef union _zend_value { zval *zv; void *ptr; zend_class_entry *ce; + zend_class_reference *cr; zend_packed_name_reference pnr; zend_function *func; struct { @@ -974,10 +992,10 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { } while (0) #define GET_CE_CACHE(ce_cache) \ - (*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) + (*(zend_class_reference **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) #define SET_CE_CACHE(ce_cache, ce) do { \ - *((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) = ce; \ + *((zend_class_reference **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) = ce; \ } while (0) /* Recursion protection macros must be used only for arrays and objects */ @@ -1158,6 +1176,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_CE(zval) (zval).value.ce #define Z_CE_P(zval_p) Z_CE(*(zval_p)) +#define Z_CR(zval) (zval).value.cr +#define Z_CR_P(zval_p) Z_CR(*(zval_p)) + #define Z_PNR(zval) (zval).value.pnr #define Z_PNR_P(zval_p) Z_PNR(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index df33b6482926f..f3fedaddf6d6f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5774,28 +5774,40 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N SAVE_OPLINE(); if (OP1_TYPE == IS_CONST) { - zend_class_entry *ce = CACHED_PTR(opline->op2.num); - zend_string *name; - const zend_type_list *type_args; - ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); - if (UNEXPECTED(ce == NULL)) { - // TODO: pre-compute lower case name - ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zend_name_reference ref = { + .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), + .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), + }; + class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); - } - - if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { - class_ref = ZEND_CE_TO_REF(ce); } else { - // TODO: re-use class references - class_ref = zend_build_class_reference(ce, type_args); + class_ref = CACHED_PTR(opline->op2.num); if (UNEXPECTED(class_ref == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } } } else if (OP1_TYPE == IS_UNUSED) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 65ce71f596266..ea81dcc355463 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -10525,28 +10525,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( SAVE_OPLINE(); if (IS_CONST == IS_CONST) { - zend_class_entry *ce = CACHED_PTR(opline->op2.num); - zend_string *name; - const zend_type_list *type_args; - ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); - if (UNEXPECTED(ce == NULL)) { - // TODO: pre-compute lower case name - ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zend_name_reference ref = { + .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), + .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), + }; + class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); - } - - if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { - class_ref = ZEND_CE_TO_REF(ce); } else { - // TODO: re-use class references - class_ref = zend_build_class_reference(ce, type_args); + class_ref = CACHED_PTR(opline->op2.num); if (UNEXPECTED(class_ref == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } } } else if (IS_CONST == IS_UNUSED) { @@ -29694,28 +29706,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE SAVE_OPLINE(); if (IS_VAR == IS_CONST) { - zend_class_entry *ce = CACHED_PTR(opline->op2.num); - zend_string *name; - const zend_type_list *type_args; - ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); - if (UNEXPECTED(ce == NULL)) { - // TODO: pre-compute lower case name - ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zend_name_reference ref = { + .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), + .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), + }; + class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); - } - - if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { - class_ref = ZEND_CE_TO_REF(ce); } else { - // TODO: re-use class references - class_ref = zend_build_class_reference(ce, type_args); + class_ref = CACHED_PTR(opline->op2.num); if (UNEXPECTED(class_ref == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } } } else if (IS_VAR == IS_UNUSED) { @@ -36873,28 +36897,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER SAVE_OPLINE(); if (IS_UNUSED == IS_CONST) { - zend_class_entry *ce = CACHED_PTR(opline->op2.num); - zend_string *name; - const zend_type_list *type_args; - ZEND_PNR_UNPACK(Z_PNR_P(RT_CONSTANT(opline, opline->op1)), name, type_args); - if (UNEXPECTED(ce == NULL)) { - // TODO: pre-compute lower case name - ce = zend_fetch_class_by_name(name, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + zend_packed_name_reference pnr = Z_PNR_P(RT_CONSTANT(opline, opline->op1)); + // TODO: specialize handler? + if (EXPECTED(ZEND_PNR_IS_SIMPLE(pnr))) { + class_ref = CACHED_PTR(opline->op2.num); + if (UNEXPECTED(class_ref == NULL)) { + zend_class_entry *ce = zend_fetch_class_by_name(ZEND_PNR_SIMPLE_GET_NAME(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce->num_generic_params != 0)) { + zend_name_reference ref = { + .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), + .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), + }; + class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + } else { + class_ref = ZEND_CE_TO_REF(ce); + } + CACHE_PTR(opline->op2.num, class_ref); } - CACHE_PTR(opline->op2.num, ce); - } - - if (EXPECTED(ce->num_generic_params == 0 && type_args == &zend_empty_type_list)) { - class_ref = ZEND_CE_TO_REF(ce); } else { - // TODO: re-use class references - class_ref = zend_build_class_reference(ce, type_args); + class_ref = CACHED_PTR(opline->op2.num); if (UNEXPECTED(class_ref == NULL)) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - HANDLE_EXCEPTION(); + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(pnr), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(class_ref == NULL)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + CACHE_PTR(opline->op2.num, class_ref); } } } else if (IS_UNUSED == IS_UNUSED) { diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index b9149fa1fa12a..ac5aa8920153e 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -31,6 +31,7 @@ #include "zend_accelerator_blacklist.h" #include "zend_list.h" #include "zend_execute.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_inheritance.h" #include "zend_exceptions.h" @@ -2290,7 +2291,7 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, } ce = entry->ce; if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); + ZSTR_SET_CE_CACHE_EX(ce->name, ZEND_CE_TO_REF(ce), 0); } return ce; } diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 74161688ea443..a6aa5c2ded674 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -26,6 +26,7 @@ #include "zend_persist.h" #include "zend_shared_alloc.h" #include "zend_observer.h" +#include "zend_types.h" #ifdef __SSE2__ /* For SSE2 adler32 */ @@ -239,7 +240,7 @@ static zend_always_inline void _zend_accel_class_hash_copy(HashTable *target, Ha _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); if ((ce->ce_flags & ZEND_ACC_LINKED) && ZSTR_VAL(p->key)[0]) { if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); + ZSTR_SET_CE_CACHE_EX(ce->name, ZEND_CE_TO_REF(ce), 0); } if (UNEXPECTED(call_observers)) { _zend_observer_class_linked_notify(ce, p->key); diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index da60564ad6687..03b02b56cb495 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_types.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -449,7 +450,8 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri } if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { - return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + return class_ref ? class_ref->ce : NULL; } else { zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); if (ce) { diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index f333a35d0dcad..583a3ffc54a5d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -1186,8 +1186,9 @@ object ":" uiv ":" ["] { zend_string *lc_name; if (!(*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) { - ce = ZSTR_GET_CE_CACHE(class_name); - if (ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + if (class_ref) { + ce = class_ref->ce; break; } } @@ -1205,8 +1206,8 @@ object ":" uiv ":" ["] { } if ((*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) { - ce = ZSTR_GET_CE_CACHE(class_name); - if (ce) { + zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); + if (class_ref) { zend_string_release_ex(lc_name, 0); break; } From 11f39c6bbfed199bf92086f3a229314ad94979c9 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 12 Jan 2024 15:17:46 +0100 Subject: [PATCH 05/10] Opcache support --- Zend/Optimizer/compact_literals.c | 73 +++++++++++++- Zend/Optimizer/zend_dump.c | 35 +++++++ Zend/Optimizer/zend_inference.h | 2 + Zend/Optimizer/zend_optimizer.c | 15 +-- Zend/zend_compile.c | 11 +++ Zend/zend_compile.h | 1 + Zend/zend_execute.h | 1 - Zend/zend_execute_API.c | 23 ++--- Zend/zend_inheritance.c | 16 ++- Zend/zend_opcode.c | 23 +++-- Zend/zend_types.h | 21 +++- Zend/zend_variables.c | 3 +- ext/opcache/ZendAccelerator.c | 30 ++++-- ext/opcache/tests/opt/dce_006.phpt | 2 +- ext/opcache/zend_file_cache.c | 151 +++++++++++++++++++++++++---- ext/opcache/zend_persist.c | 120 ++++++++++++++++++----- ext/opcache/zend_persist_calc.c | 93 +++++++++++++++--- 17 files changed, 519 insertions(+), 101 deletions(-) diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index 9248f0b822441..c984407d470e1 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -24,8 +24,11 @@ #include "Optimizer/zend_optimizer.h" #include "Optimizer/zend_optimizer_internal.h" #include "zend_API.h" +#include "zend_compile.h" #include "zend_constants.h" #include "zend_execute.h" +#include "zend_string.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_extensions.h" @@ -35,6 +38,9 @@ #define LITERAL_STATIC_METHOD 2 #define LITERAL_STATIC_PROPERTY 3 +// TODO: temp hack +#define LITERAL_PNR_AND_KEY (1<<7) + typedef struct _literal_info { uint8_t num_related; } literal_info; @@ -245,7 +251,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx break; case ZEND_NEW: if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, 2); + LITERAL_INFO(opline->op1.constant, 2 | LITERAL_PNR_AND_KEY); } break; case ZEND_DECLARE_CLASS: @@ -312,6 +318,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx memset(map, 0, op_array->last_literal * sizeof(int)); for (i = 0; i < op_array->last_literal; i++) { if (!info[i].num_related) { + // TODO: temp hack + if (Z_TYPE(op_array->literals[i]) == IS_PNR) { + zend_pnr_destroy(Z_PNR(op_array->literals[i])); + } /* unset literal */ zval_ptr_dtor_nogc(&op_array->literals[i]); continue; @@ -443,6 +453,67 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; } + case IS_PNR: { + ZEND_ASSERT(info[i].num_related & LITERAL_PNR_AND_KEY); + ZEND_ASSERT(Z_TYPE(op_array->literals[i+1]) == IS_STRING); + + uint8_t num_related = info[i].num_related & ~LITERAL_PNR_AND_KEY; + ZEND_ASSERT(num_related == 2); + + zend_packed_name_reference pnr = Z_PNR(op_array->literals[i]); + zend_string *base_key = Z_STR(op_array->literals[i+1]); + zend_string *key; + if (ZEND_PNR_IS_COMPLEX(pnr)) { + key = ZEND_PNR_COMPLEX_GET_KEY(pnr); + } else { + key = ZEND_PNR_SIMPLE_GET_NAME(pnr); + } + key = zend_string_concat2( + ZSTR_VAL(key), ZSTR_LEN(key)+1, + ZSTR_VAL(base_key), ZSTR_LEN(base_key)); + bias_key(key, 300 + num_related - 1); + + if ((pos = zend_hash_find(&hash, key)) != NULL) { + ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_PNR && + Z_TYPE(op_array->literals[Z_LVAL_P(pos)+1]) == IS_STRING && + info[i].num_related == info[Z_LVAL_P(pos)].num_related); + zend_string_release_ex(key, 0); + map[i] = Z_LVAL_P(pos); + zend_packed_name_reference pnr2 = Z_PNR(op_array->literals[i]); + if (ZEND_PNR_IS_SIMPLE(pnr2)) { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr2)); + } else { + zend_name_reference *class_ref = ZEND_PNR_COMPLEX_GET_REF(pnr2); + zend_string_release(class_ref->name); + zend_string_release(class_ref->key); + } + n = num_related; + while (n > 1) { + i++; + zval_ptr_dtor_nogc(&op_array->literals[i]); + n--; + } + } else { + map[i] = j; + ZVAL_LONG(&zv, j); + zend_hash_add_new(&hash, key, &zv); + zend_string_release_ex(key, 0); + if (i != j) { + op_array->literals[j] = op_array->literals[i]; + info[j] = info[i]; + } + j++; + n = num_related; + while (n > 1) { + i++; + if (i != j) op_array->literals[j] = op_array->literals[i]; + j++; + n--; + } + } + + break; + } case IS_ARRAY: ZEND_ASSERT(info[i].num_related == 1); if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) { diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c index 1001a935d80a4..3bfca0493bab3 100644 --- a/Zend/Optimizer/zend_dump.c +++ b/Zend/Optimizer/zend_dump.c @@ -23,7 +23,9 @@ #include "zend_func_info.h" #include "zend_call_graph.h" #include "zend_dump.h" +#include "zend_smart_str.h" #include "ext/standard/php_string.h" +#include "zend_types.h" void zend_dump_ht(HashTable *ht) { @@ -76,6 +78,39 @@ void zend_dump_const(const zval *zv) case IS_ARRAY: fprintf(stderr, " array(...)"); break; + case IS_PNR: { + zend_packed_name_reference pnr = Z_PNR_P(zv); + zend_string *name; + const zend_type_list *type_args; + ZEND_PNR_UNPACK(pnr, name, type_args); + + smart_str str = {0}; + smart_str_append(&str, name); + + if (type_args != &zend_empty_type_list) { + smart_str_appendc(&str, '<'); + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH((zend_type_list*)type_args, single_type) { + if (single_type != type_args->types) { + smart_str_appendc(&str, ','); + } + zend_string *type_str = zend_type_to_string(*single_type, NULL); + smart_str_append(&str, type_str); + zend_string_release(type_str); + } ZEND_TYPE_LIST_FOREACH_END(); + smart_str_appendc(&str, '>'); + } + + name = smart_str_extract(&str); + zend_string *escaped_string = php_addcslashes(name, "\0..\37\"\\\177..\377", 2); + zend_string_release(name); + + fprintf(stderr, " pnr(\"%s\")", ZSTR_VAL(escaped_string)); + + zend_string_release(escaped_string); + + break; + } default: fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv)); break; diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index ffcf4f0ae1379..31c742c16d994 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -148,6 +148,8 @@ END_EXTERN_C() static zend_always_inline uint32_t _const_op_type(const zval *zv) { if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY; + } else if (Z_TYPE_P(zv) == IS_PNR) { + return MAY_BE_STRING; } else if (Z_TYPE_P(zv) == IS_ARRAY) { return zend_array_type_info(zv); } else { diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index cf1c98c64bb59..f20de6e61d0c8 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -24,6 +24,7 @@ #include "zend_API.h" #include "zend_constants.h" #include "zend_execute.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_cfg.h" #include "zend_func_info.h" @@ -323,11 +324,13 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array, zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); break; case ZEND_NEW: - REQUIRES_STRING(val); - drop_leading_backslash(val); + if (Z_TYPE_P(val) != IS_PNR) { + return 0; + } + // TODO drop_leading_backslash(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); opline->op2.num = alloc_cache_slots(op_array, 1); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); + zend_optimizer_add_literal_string(op_array, zend_string_tolower(ZEND_PNR_GET_NAME(Z_PNR_P(val)))); break; case ZEND_INIT_STATIC_METHOD_CALL: REQUIRES_STRING(val); @@ -816,9 +819,9 @@ zend_class_entry *zend_optimizer_get_class_entry_from_op1( const zend_script *script, const zend_op_array *op_array, const zend_op *opline) { if (opline->op1_type == IS_CONST) { zval *op1 = CRT_CONSTANT(opline->op1); - if (Z_TYPE_P(op1) == IS_STRING) { - return zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); - } + ZEND_ASSERT(Z_TYPE_P(op1) == IS_PNR || Z_TYPE_P(op1) == IS_STRING); + ZEND_ASSERT(Z_TYPE_P(op1 + 1) == IS_STRING); + return zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); } else if (opline->op1_type == IS_UNUSED && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 24219ffd566d2..34c3aff03d5e5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1930,6 +1930,16 @@ static zend_packed_name_reference zend_compile_pnr( } } +ZEND_API void zend_pnr_destroy(zend_packed_name_reference pnr) { + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(pnr)); + } else { + zend_name_reference *name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); + zend_string_release(name_ref->name); + zend_string_release(name_ref->key); + } +} + static zend_packed_name_reference zend_compile_default_pnr( zend_ast *class_ast, const char *type) { zend_ast *name_ast = class_ast->child[0]; @@ -8410,6 +8420,7 @@ zend_class_entry *zend_init_class_entry_header(zend_class_entry_storage *ptr, ze zend_class_entry *ce = (zend_class_entry *) ((char *) ptr + ZEND_CLASS_ENTRY_HEADER_SIZE); ref->ce = ce; ref->key = key; + zend_string_addref(key); ref->args.num_types = 0; return ce; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 881d75f54beaf..475c38721f128 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -868,6 +868,7 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_type_release(zend_type type, bool persistent); ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name); +ZEND_API void zend_pnr_destroy(zend_packed_name_reference pnr); void zend_packed_name_reference_release(zend_packed_name_reference ref, bool persistent); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 2ee0c7a17b4e9..40e51ba392302 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -391,7 +391,6 @@ ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fe ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, uint32_t fetch_type, zend_class_entry *scope); ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type); ZEND_API zend_class_reference *zend_fetch_generic_class_by_ref(zend_name_reference *name_ref, zend_string *base_key, uint32_t fetch_type); -ZEND_API zend_class_reference *zend_register_generic_class(zend_name_reference *name_ref, zend_class_entry *ce); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, size_t len); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 2086ef6124d0b..c6bb05a293ce9 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1123,19 +1123,6 @@ ZEND_API bool zend_is_valid_class_name(zend_string *name) { return 1; } -ZEND_API zend_class_reference *zend_register_generic_class(zend_name_reference *name_ref, zend_class_entry *ce) -{ - zend_class_reference *class_ref = zend_build_class_reference(name_ref, ce); - if (!UNEXPECTED(class_ref)) { - return NULL; - } - - void *ptr = zend_hash_add_ptr(&EG(generic_class_table), name_ref->key, class_ref); - ZEND_ASSERT(ptr); - - return class_ref; -} - ZEND_API zend_class_reference *zend_lookup_generic_class(zend_name_reference *name_ref, zend_string *base_key, uint32_t flags) /* {{{ */ { zend_class_entry *ce = NULL; @@ -1169,7 +1156,15 @@ ZEND_API zend_class_reference *zend_lookup_generic_class(zend_name_reference *na return NULL; } - return zend_register_generic_class(name_ref, ce); + zend_class_reference *class_ref = zend_build_class_reference(name_ref, ce); + if (!UNEXPECTED(class_ref)) { + return NULL; + } + + void *ptr = zend_hash_add_ptr(&EG(generic_class_table), name_ref->key, class_ref); + ZEND_ASSERT(ptr); + + return class_ref; } ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d4562368cd85b..bc015a540e7bb 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -30,6 +30,7 @@ #include "zend_attributes.h" #include "zend_constants.h" #include "zend_observer.h" +#include "zend_string.h" #include "zend_types.h" ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; @@ -1625,17 +1626,23 @@ static void update_parents(zend_class_entry *ce, zend_class_entry *parent_ce) { pemalloc(sizeof(zend_class_reference *) * (parent_ce->num_parents + 1), ce->type == ZEND_INTERNAL_CLASS); if (ce->parent_name) { + zend_string *key; if (ZEND_PNR_IS_COMPLEX(ce->parent_name)) { /* Ownership of the type arguments has been taken by generic arg binding. */ zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ce->parent_name); zend_string_release(ref->name); + key = ZEND_PNR_COMPLEX_GET_KEY(ce->parent_name); + zend_string_addref(key); // efree(ref); } else { zend_string_release(ZEND_PNR_SIMPLE_GET_NAME(ce->parent_name)); + key = ZEND_CE_TO_REF(ce)->key; + zend_string_addref(key); } zend_class_reference *ref = emalloc(ZEND_CLASS_REF_SIZE(parent_ce->num_generic_params)); ref->ce = parent_ce; + ref->key = key; ref->args.num_types = parent_ce->num_generic_params; generic_args -= ref->args.num_types; memcpy(ref->args.types, generic_args, sizeof(zend_type) * ref->args.num_types); @@ -1652,6 +1659,8 @@ static void update_parents(zend_class_entry *ce, zend_class_entry *parent_ce) { } else { zend_class_reference *new_ref = emalloc(ZEND_CLASS_REF_SIZE(ref->args.num_types)); new_ref->ce = ref->ce; + new_ref->key = ref->key; + zend_string_addref(new_ref->key); new_ref->args.num_types = ref->args.num_types; generic_args -= ref->args.num_types; memcpy(new_ref->args.types, generic_args, sizeof(zend_type) * ref->args.num_types); @@ -3048,11 +3057,14 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) { static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce) { + zend_class_reference *class_ref; zend_class_entry *ce; Bucket *p, *end; - ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); - memcpy(ce, pce, sizeof(zend_class_entry)); + class_ref = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry_storage)); + memcpy(class_ref, ZEND_CE_TO_REF(pce), sizeof(zend_class_entry_storage)); + ce = (zend_class_entry*)((char*)class_ref + ZEND_CLASS_ENTRY_HEADER_SIZE); + class_ref->ce = ce; ce->ce_flags &= ~ZEND_ACC_IMMUTABLE; ce->refcount = 1; ce->inheritance_cache = NULL; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 917b26912100b..f7401a27831a3 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -371,16 +371,6 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->num_parents) { if (!(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { zend_pnr_release(ce->parent_name, /* uses_arena */ 1, /* persistent */ 0); - } else { - for (uint32_t i = 0; i < ce->num_parents; i++) { - zend_class_reference *ref = ce->parents[i]; - if (!ZEND_REF_IS_TRIVIAL(ref)) { - /* The type arguments are owned by bound_generic_args, - * and will be destroyed there. */ - efree(ref); - } - } - efree(ce->parents); } } @@ -427,6 +417,19 @@ ZEND_API void destroy_zend_class(zval *zv) } } + if (ce->num_parents && (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(ref)) { + /* The type arguments are owned by bound_generic_args, + * and will be destroyed there. */ + zend_string_release(ref->key); + efree(ref); + } + } + efree(ce->parents); + } + if (ce->default_properties_table) { zval *p = ce->default_properties_table; zval *end = p + ce->default_properties_count; diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 356f7245ee0f1..228e2ab82df54 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -291,6 +291,9 @@ typedef struct { #define ZEND_TYPE_SET_PTR(t, _ptr) \ ((t).ptr = (_ptr)) +#define ZEND_TYPE_SET_PNR(t, _pnr) \ + ZEND_TYPE_SET_PTR((t), (void *) (uintptr_t) (_pnr)) + #define ZEND_TYPE_SET_GENERIC_PARAM_ID(t, _id) \ ((t).ptr = (void *) (uintptr_t) (_id)) @@ -448,6 +451,19 @@ static const zend_type_list zend_empty_type_list = {0}; } \ } while (0) +#define ZEND_PNR_UNPACK_NAME_KEY_ARGS(pnr, name_var, key_var) do { \ + if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *__ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + (name_var) = __ref->name; \ + (key_var) = __ref->key; \ + (args_var) = &__ref->args; \ + } else { \ + (name_var) = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + (key_var) = NULL; \ + (args_var) = &zend_empty_type_list; \ + } \ +} while (0) + typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ @@ -760,7 +776,8 @@ struct _zend_ast_ref { #define IS_INDIRECT 12 #define IS_PTR 13 #define IS_ALIAS_PTR 14 -#define _IS_ERROR 15 +#define IS_PNR 15 +#define _IS_ERROR 16 /* used for casts */ #define _IS_BOOL 18 @@ -1401,7 +1418,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define ZVAL_PNR(z, c) do { \ Z_PNR_P(z) = (c); \ - Z_TYPE_INFO_P(z) = IS_PTR; \ + Z_TYPE_INFO_P(z) = IS_PNR; \ } while (0) #define ZVAL_ALIAS_PTR(z, p) do { \ diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c index 27e09d7db22b1..4fa8c9265784e 100644 --- a/Zend/zend_variables.c +++ b/Zend/zend_variables.c @@ -48,7 +48,8 @@ static const zend_rc_dtor_func_t zend_rc_dtor_func[] = { [IS_OBJECT] = (zend_rc_dtor_func_t)zend_objects_store_del, [IS_RESOURCE] = (zend_rc_dtor_func_t)zend_list_free, [IS_REFERENCE] = (zend_rc_dtor_func_t)zend_reference_destroy, - [IS_CONSTANT_AST] = (zend_rc_dtor_func_t)zend_ast_ref_destroy + [IS_CONSTANT_AST] = (zend_rc_dtor_func_t)zend_ast_ref_destroy, + [IS_PNR] = (zend_rc_dtor_func_t)zend_empty_destroy, }; ZEND_API void ZEND_FASTCALL rc_dtor_func(zend_refcounted *p) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index ac5aa8920153e..9d3d809b6a871 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -619,8 +619,18 @@ static inline void accel_copy_permanent_list_types( ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(*single_type)); accel_copy_permanent_list_types(new_interned_string, *single_type); } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - ZEND_TYPE_SET_PTR(*single_type, new_interned_string(ZEND_TYPE_NAME(*single_type))); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_packed_name_reference pnr = ZEND_TYPE_PNR(*single_type); + if (ZEND_PNR_IS_SIMPLE(pnr)) { + ZEND_TYPE_SET_PTR(*single_type, new_interned_string(ZEND_PNR_SIMPLE_GET_NAME(pnr))); + } else { + zend_name_reference *name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); + name_ref->name = new_interned_string(name_ref->name); + name_ref->key = new_interned_string(name_ref->key); + for (uint32_t i = 0; i < name_ref->args.num_types; i++) { + accel_copy_permanent_list_types(new_interned_string, name_ref->args.types[i]); + } + } } } ZEND_TYPE_FOREACH_END(); } @@ -667,8 +677,10 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int /* class table hash keys, class names, properties, methods, constants, etc */ ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) { zend_class_entry *ce; + zend_class_reference *class_ref; ce = (zend_class_entry*)Z_PTR(p->val); + class_ref = ZEND_CE_TO_REF(ce); if (p->key) { p->key = new_interned_string(p->key); @@ -679,6 +691,8 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int ZEND_ASSERT(ZSTR_HAS_CE_CACHE(ce->name)); } + class_ref->key = new_interned_string(class_ref->key); + ZEND_HASH_MAP_FOREACH_BUCKET(&ce->properties_info, q) { zend_property_info *info; @@ -3647,8 +3661,8 @@ static void preload_sort_classes(void *base, size_t count, size_t siz, compare_f while (b1 < end) { try_again: ce = (zend_class_entry*)Z_PTR(b1->val); - if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) { - p = ce->parent; + if (ce->num_parents && (ce->ce_flags & ZEND_ACC_LINKED)) { + p = ce->parents[0]->ce; if (p->type == ZEND_USER_CLASS) { b2 = b1 + 1; while (b2 < end) { @@ -3694,12 +3708,12 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e memset(error, 0, sizeof(preload_error)); if (ce->parent_name) { - zend_string *key = zend_string_tolower(ce->parent_name); + zend_string *key = zend_string_tolower(ZEND_PNR_GET_NAME(ce->parent_name)); zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key); zend_string_release(key); if (!parent) { error->kind = "Unknown parent "; - error->name = ZSTR_VAL(ce->parent_name); + error->name = ZSTR_VAL(ZEND_PNR_GET_NAME(ce->parent_name)); return FAILURE; } } @@ -3773,7 +3787,7 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) } } if (ce->default_static_members_count) { - uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count; + uint32_t count = ce->num_parents ? ce->default_static_members_count - ce->parents[0]->ce->default_static_members_count : ce->default_static_members_count; bool resolved = true; val = ce->default_static_members_table + ce->default_static_members_count - 1; @@ -3916,7 +3930,7 @@ static void preload_link(void) uint32_t temporary_flags = ZEND_ACC_FILE_CACHED|ZEND_ACC_CACHED; ce->ce_flags |= temporary_flags; if (ce->parent_name) { - zend_string_addref(ce->parent_name); + zend_string_addref(ZEND_PNR_GET_NAME(ce->parent_name)); } /* Record and suppress errors during inheritance. */ diff --git a/ext/opcache/tests/opt/dce_006.phpt b/ext/opcache/tests/opt/dce_006.phpt index 97d2988793907..6d65c8302e1a9 100644 --- a/ext/opcache/tests/opt/dce_006.phpt +++ b/ext/opcache/tests/opt/dce_006.phpt @@ -30,7 +30,7 @@ foo: ; (after optimizer) ; %sdce_006.php:5-8 0000 CV0($x) = RECV 1 -0001 V2 = NEW 0 string("A") +0001 V2 = NEW 0 pnr("A") 0002 DO_FCALL 0003 CV1($a) = QM_ASSIGN V2 0004 ASSIGN_OBJ CV1($a) string("foo") diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index edcaf689ab7f1..32ceb89ef2402 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -17,6 +17,7 @@ */ #include "zend.h" +#include "zend_types.h" #include "zend_virtual_cwd.h" #include "zend_compile.h" #include "zend_vm.h" @@ -186,6 +187,66 @@ static int zend_file_cache_flock(int fd, int type) } \ } while (0) +# define SERIALIZE_PNR(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + SERIALIZE_STR(_name); \ + if (ZEND_PNR_ENCODE_NAME(_name) != (pnr)) { \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + SERIALIZE_STR(_name_ref->name); \ + SERIALIZE_STR(_name_ref->key); \ + zend_file_cache_serialize_type_list(&_name_ref->args, script, info, buf); \ + SERIALIZE_PTR(_name_ref); \ + (pnr) = ZEND_PNR_ENCODE_REF(_name_ref); \ + } \ + } while (0) + +# define UNSERIALIZE_PNR(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + UNSERIALIZE_STR(_name); \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + UNSERIALIZE_PTR(_name_ref); \ + UNSERIALIZE_STR(_name_ref->name); \ + UNSERIALIZE_STR(_name_ref->key); \ + zend_file_cache_unserialize_type_list(&_name_ref->args, script, buf); \ + (pnr) = ZEND_PNR_ENCODE_REF(_name_ref); \ + } \ + } while (0) + +# define SERIALIZE_TYPE_PNR(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference _pnr = ZEND_TYPE_PNR(type); \ + SERIALIZE_PNR(_pnr); \ + ZEND_TYPE_SET_PNR((type), _pnr); \ + } while (0) + +# define UNSERIALIZE_TYPE_PNR(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference _pnr = ZEND_TYPE_PNR(type); \ + UNSERIALIZE_PNR(_pnr); \ + ZEND_TYPE_SET_PNR((type), _pnr); \ + } while (0) + +# define SERIALIZE_ZV_PNR(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference _pnr = Z_PNR_P(zv); \ + SERIALIZE_PNR(_pnr); \ + Z_PNR_P(zv) = _pnr; \ + } while (0) + +# define UNSERIALIZE_ZV_PNR(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference _pnr = Z_PNR_P(zv); \ + UNSERIALIZE_PNR(_pnr); \ + Z_PNR_P(zv) = _pnr; \ + } while (0) + static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -239,6 +300,13 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_persistent_script *script, void *buf); +static void zend_file_cache_serialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, zend_file_cache_metainfo *info, + void *buf); + +static void zend_file_cache_unserialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, void *buf); + static void *zend_file_cache_serialize_interned(zend_string *str, zend_file_cache_metainfo *info) { @@ -404,6 +472,9 @@ static void zend_file_cache_serialize_zval(zval *zv, zend_file_cache_serialize_ast(GC_AST(ast), script, info, buf); } break; + case IS_PNR: + SERIALIZE_ZV_PNR(zv); + break; case IS_INDIRECT: /* Used by static properties. */ SERIALIZE_PTR(Z_INDIRECT_P(zv)); @@ -448,13 +519,20 @@ static void zend_file_cache_serialize_type( ZEND_TYPE_LIST_FOREACH(list, list_type) { zend_file_cache_serialize_type(list_type, script, info, buf); } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string *type_name = ZEND_TYPE_NAME(*type); - SERIALIZE_STR(type_name); - ZEND_TYPE_SET_PTR(*type, type_name); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + SERIALIZE_TYPE_PNR(*type); } } +static void zend_file_cache_serialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, zend_file_cache_metainfo *info, + void *buf) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_file_cache_serialize_type(single_type, script, info, buf); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_file_cache_serialize_op_array(zend_op_array *op_array, zend_persistent_script *script, zend_file_cache_metainfo *info, @@ -714,12 +792,25 @@ static void zend_file_cache_serialize_class(zval *zv, ce = Z_PTR_P(zv); UNSERIALIZE_PTR(ce); + zend_class_reference *class_ref = ZEND_CE_TO_REF(ce); + SERIALIZE_PTR(class_ref->ce); + SERIALIZE_STR(class_ref->key); + SERIALIZE_STR(ce->name); - if (ce->parent) { + if (ce->num_parents) { if (!(ce->ce_flags & ZEND_ACC_LINKED)) { - SERIALIZE_STR(ce->parent_name); + SERIALIZE_PNR(ce->parent_name); } else { - SERIALIZE_PTR(ce->parent); + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *parent_ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(parent_ref)) { + SERIALIZE_PTR(parent_ref->ce); + SERIALIZE_STR(parent_ref->key); + zend_file_cache_serialize_type_list(&parent_ref->args, script, info, buf); + } + SERIALIZE_PTR(ce->parents[i]); + } + SERIALIZE_PTR(ce->parents); } } zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func); @@ -1252,6 +1343,9 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_file_cache_unserialize_ast(Z_ASTVAL_P(zv), script, buf); } break; + case IS_PNR: + UNSERIALIZE_ZV_PNR(zv); + break; case IS_INDIRECT: /* Used by static properties. */ UNSERIALIZE_PTR(Z_INDIRECT_P(zv)); @@ -1280,7 +1374,7 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri } static void zend_file_cache_unserialize_type( - zend_type *type, zend_class_entry *scope, zend_persistent_script *script, void *buf) + zend_type *type, zend_persistent_script *script, void *buf) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *list = ZEND_TYPE_LIST(*type); @@ -1289,20 +1383,26 @@ static void zend_file_cache_unserialize_type( zend_type *list_type; ZEND_TYPE_LIST_FOREACH(list, list_type) { - zend_file_cache_unserialize_type(list_type, scope, script, buf); + zend_file_cache_unserialize_type(list_type, script, buf); } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string *type_name = ZEND_TYPE_NAME(*type); - UNSERIALIZE_STR(type_name); - ZEND_TYPE_SET_PTR(*type, type_name); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + UNSERIALIZE_TYPE_PNR(*type); if (!script->corrupted) { - zend_accel_get_class_name_map_ptr(type_name); + zend_accel_get_class_name_map_ptr(ZEND_TYPE_PNR_NAME(*type)); } else { - zend_alloc_ce_cache(type_name); + zend_alloc_ce_cache(ZEND_TYPE_PNR_NAME(*type)); } } } +static void zend_file_cache_unserialize_type_list(zend_type_list *type_list, + zend_persistent_script *script, void *buf) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_file_cache_unserialize_type(single_type, script, buf); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_file_cache_unserialize_op_array(zend_op_array *op_array, zend_persistent_script *script, void *buf) @@ -1444,7 +1544,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr if (!IS_UNSERIALIZED(p->name)) { UNSERIALIZE_STR(p->name); } - zend_file_cache_unserialize_type(&p->type, (op_array->fn_flags & ZEND_ACC_CLOSURE) ? NULL : op_array->scope, script, buf); + zend_file_cache_unserialize_type(&p->type, script, buf); p++; } } @@ -1510,7 +1610,7 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, UNSERIALIZE_STR(prop->doc_comment); } UNSERIALIZE_ATTRIBUTES(prop->attributes); - zend_file_cache_unserialize_type(&prop->type, prop->ce, script, buf); + zend_file_cache_unserialize_type(&prop->type, script, buf); } } } @@ -1535,7 +1635,7 @@ static void zend_file_cache_unserialize_class_constant(zval * UNSERIALIZE_STR(c->doc_comment); } UNSERIALIZE_ATTRIBUTES(c->attributes); - zend_file_cache_unserialize_type(&c->type, c->ce, script, buf); + zend_file_cache_unserialize_type(&c->type, script, buf); } } } @@ -1557,11 +1657,20 @@ static void zend_file_cache_unserialize_class(zval *zv, zend_alloc_ce_cache(ce->name); } } - if (ce->parent) { + if (ce->num_parents) { if (!(ce->ce_flags & ZEND_ACC_LINKED)) { - UNSERIALIZE_STR(ce->parent_name); + UNSERIALIZE_PNR(ce->parent_name); } else { - UNSERIALIZE_PTR(ce->parent); + UNSERIALIZE_PTR(ce->parents); + for (uint32_t i = 0; i < ce->num_parents; i++) { + UNSERIALIZE_PTR(ce->parents[i]); + zend_class_reference *parent_ref = ce->parents[i]; + if (!ZEND_REF_IS_TRIVIAL(parent_ref)) { + UNSERIALIZE_PTR(parent_ref->ce); + UNSERIALIZE_STR(parent_ref->key); + zend_file_cache_unserialize_type_list(&parent_ref->args, script, buf); + } + } } } zend_file_cache_unserialize_hash(&ce->function_table, diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index b10e5a8046ab1..d84581efbf6fb 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -24,6 +24,7 @@ #include "zend_persist.h" #include "zend_extensions.h" #include "zend_shared_alloc.h" +#include "zend_types.h" #include "zend_vm.h" #include "zend_constants.h" #include "zend_operators.h" @@ -81,10 +82,52 @@ } \ } while (0) +# define zend_accel_store_pnr(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + zend_accel_store_interned_string(_name); \ + if (ZEND_PNR_ENCODE_NAME(_name) != (pnr)) { \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + zend_string *_new_name_ref = zend_shared_alloc_get_xlat_entry(_name_ref); \ + if (_new_name_ref) { \ + (pnr) = ZEND_PNR_ENCODE_REF(_new_name_ref); \ + } else { \ + _name_ref = zend_shared_memdup_put((void*)_name_ref, ZEND_CLASS_REF_SIZE(_name_ref->args.num_types)); \ + zend_accel_store_interned_string(_name_ref->name); \ + zend_accel_store_interned_string(_name_ref->key); \ + zend_persist_type_list(&_name_ref->args); \ + (pnr) = ZEND_PNR_ENCODE_REF(_name_ref); \ + } \ + } \ + } while (0) + +# define zend_accel_store_zv_pnr(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference _pnr = Z_PNR_P(zv); \ + zend_accel_store_pnr(_pnr); \ + if (Z_PNR_P(zv) != _pnr) { \ + Z_PNR_P(zv) = _pnr; \ + } \ + } while (0) + +# define zend_accel_store_type_pnr(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference _pnr = ZEND_TYPE_PNR(type); \ + zend_accel_store_pnr(_pnr); \ + if (ZEND_TYPE_PNR(type) != _pnr) { \ + ZEND_TYPE_SET_PNR((type), _pnr); \ + } \ + } while (0) + typedef void (*zend_persist_func_t)(zval*); static void zend_persist_zval(zval *z); static void zend_persist_op_array(zval *zv); +static void zend_persist_type_list(zend_type_list *type_list); + static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -265,6 +308,9 @@ static void zend_persist_zval(zval *z) efree(old_ref); } break; + case IS_PNR: + zend_accel_store_zv_pnr(z); + break; default: ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); break; @@ -357,17 +403,22 @@ static void zend_persist_type(zend_type *type) { zend_persist_type(single_type); continue; } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *type_name = ZEND_TYPE_NAME(*single_type); - zend_accel_store_interned_string(type_name); - ZEND_TYPE_SET_PTR(*single_type, type_name); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_accel_store_type_pnr(*single_type); if (!ZCG(current_persistent_script)->corrupted) { - zend_accel_get_class_name_map_ptr(type_name); + zend_accel_get_class_name_map_ptr(ZEND_TYPE_PNR_NAME(*single_type)); } } } ZEND_TYPE_FOREACH_END(); } +static void zend_persist_type_list(zend_type_list *type_list) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_persist_type(single_type); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script) { zend_op *persist_ptr; @@ -855,7 +906,14 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) if (new_ce) { return new_ce; } - ce = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); + + zend_class_reference *class_ref = zend_shared_memdup_put( + ZEND_CE_TO_REF(ce), sizeof(zend_class_entry_storage)); + ce = (zend_class_entry*)((char*)class_ref + ZEND_CLASS_ENTRY_HEADER_SIZE); + zend_shared_alloc_register_xlat_entry(orig_ce, ce); + class_ref->ce = ce; + zend_accel_store_interned_string(class_ref->key); + if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { ce->ce_flags |= ZEND_ACC_IMMUTABLE; if ((ce->ce_flags & ZEND_ACC_LINKED) @@ -867,6 +925,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) } else { ce->ce_flags |= ZEND_ACC_FILE_CACHED; } + ce->inheritance_cache = NULL; if (!(ce->ce_flags & ZEND_ACC_CACHED)) { @@ -879,7 +938,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) zend_accel_get_class_name_map_ptr(ce->name); } if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { - zend_accel_store_interned_string(ce->parent_name); + zend_accel_store_pnr(ce->parent_name); } } @@ -904,7 +963,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) /* Persist only static properties in this class. * Static properties from parent classes will be handled in class_copy_ctor */ - i = (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) ? ce->parent->default_static_members_count : 0; + i = (ce->num_parents && (ce->ce_flags & ZEND_ACC_LINKED)) ? ce->parents[0]->ce->default_static_members_count : 0; for (; i < ce->default_static_members_count; i++) { zend_persist_zval(&ce->default_static_members_table[i]); } @@ -1066,24 +1125,36 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) void zend_update_parent_ce(zend_class_entry *ce) { if (ce->ce_flags & ZEND_ACC_LINKED) { - if (ce->parent) { - int i, end; - zend_class_entry *parent = ce->parent; - - if (parent->type == ZEND_USER_CLASS) { - zend_class_entry *p = zend_shared_alloc_get_xlat_entry(parent); - + if (ce->num_parents) { + ce->parents = zend_shared_memdup_free(ce->parents, + sizeof(zend_class_reference *) * ce->num_parents); + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *parent_ref = ce->parents[i]; + zend_class_reference *p = zend_shared_alloc_get_xlat_entry(parent_ref); if (p) { - ce->parent = parent = p; + ce->parents[i] = parent_ref = p; + } else { + ZEND_ASSERT(!ZEND_REF_IS_TRIVIAL(parent_ref)); + ce->parents[i] = parent_ref = zend_shared_memdup_free( + parent_ref, + ZEND_CLASS_REF_SIZE(parent_ref->args.num_types)); + zend_accel_store_interned_string(parent_ref->key); + zend_persist_type_list(&parent_ref->args); + zend_class_entry *ce = zend_shared_alloc_get_xlat_entry(parent_ref->ce); + if (ce) { + parent_ref->ce = ce; + } } } /* Create indirections to static properties from parent classes */ - i = parent->default_static_members_count - 1; - while (parent && parent->default_static_members_table) { - end = parent->parent ? parent->parent->default_static_members_count : 0; - for (; i >= end; i--) { - zval *p = &ce->default_static_members_table[i]; + zend_class_entry *parent = ce->parents[0]->ce; + int j = parent->default_static_members_count - 1; + int end; + while (parent->default_static_members_table) { + end = parent->num_parents ? parent->parents[0]->ce->default_static_members_count : 0; + for (; j >= end; j--) { + zval *p = &ce->default_static_members_table[j]; /* The static property may have been overridden by a trait * during inheritance. In that case, the property default * value is replaced by zend_declare_typed_property() at the @@ -1091,11 +1162,14 @@ void zend_update_parent_ce(zend_class_entry *ce) * point to the parent property value if the child value was * already indirect. */ if (Z_TYPE_P(p) == IS_INDIRECT) { - ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); + ZVAL_INDIRECT(p, &parent->default_static_members_table[j]); } } - parent = parent->parent; + if (!parent->num_parents) { + break; + } + parent = parent->parents[0]->ce; } } diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 2d4a3c92afa29..aa90ddbd8e03e 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -26,6 +26,7 @@ #include "zend_shared_alloc.h" #include "zend_operators.h" #include "zend_attributes.h" +#include "zend_types.h" #define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s) #define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m) @@ -45,8 +46,50 @@ } \ } while (0) +# define ADD_PNR(pnr) do { \ + if (ZEND_PNR_IS_SIMPLE(pnr)) { \ + zend_string *_name = ZEND_PNR_SIMPLE_GET_NAME(pnr); \ + ADD_INTERNED_STRING(_name); \ + if (ZEND_PNR_ENCODE_NAME(_name) != (pnr)) { \ + (pnr) = ZEND_PNR_ENCODE_NAME(_name); \ + } \ + } else if (ZEND_PNR_IS_COMPLEX(pnr)) { \ + zend_name_reference *_name_ref = ZEND_PNR_COMPLEX_GET_REF(pnr); \ + if (!ZCG(current_persistent_script)->corrupted \ + && zend_accel_in_shm(_name_ref)) { \ + break; \ + } \ + size_t _size = zend_shared_memdup_size(_name_ref, ZEND_CLASS_REF_SIZE(_name_ref->args.num_types)); \ + if (_size) { \ + ADD_SIZE(_size); \ + ADD_INTERNED_STRING(_name_ref->name); \ + ADD_INTERNED_STRING(_name_ref->key); \ + zend_persist_type_list_calc(&_name_ref->args); \ + } \ + } \ + } while (0) + +# define ADD_TYPE_PNR(type) do { \ + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(type)); \ + zend_packed_name_reference pnr = ZEND_TYPE_PNR(type); \ + ADD_PNR(pnr); \ + if (ZEND_TYPE_PNR(type) != pnr) { \ + ZEND_TYPE_SET_PNR((type), pnr); \ + } \ + } while (0) + +# define ADD_ZV_PNR(zv) do { \ + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PNR); \ + zend_packed_name_reference pnr = Z_PNR_P(zv); \ + ADD_PNR(pnr); \ + if (Z_PNR_P(zv) != pnr) { \ + Z_PNR_P(zv) = pnr; \ + } \ + } while (0) + static void zend_persist_zval_calc(zval *z); static void zend_persist_op_array_calc(zval *zv); +static void zend_persist_type_list_calc(zend_type_list *type_list); static void zend_hash_persist_calc(HashTable *ht) { @@ -146,6 +189,10 @@ static void zend_persist_zval_calc(zval *z) } } break; + case IS_PNR: { + ADD_ZV_PNR(z); + break; + } default: ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); break; @@ -191,14 +238,22 @@ static void zend_persist_type_calc(zend_type *type) zend_persist_type_calc(single_type); continue; } - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *type_name = ZEND_TYPE_NAME(*single_type); - ADD_INTERNED_STRING(type_name); - ZEND_TYPE_SET_PTR(*single_type, type_name); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + ADD_TYPE_PNR(*single_type); } } ZEND_TYPE_FOREACH_END(); } +static void zend_persist_type_list_calc(zend_type_list *type_list) +{ + ADD_SIZE(ZEND_TYPE_LIST_SIZE(type_list->num_types)); + + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(type_list, single_type) { + zend_persist_type_calc(single_type); + } ZEND_TYPE_LIST_FOREACH_END(); +} + static void zend_persist_op_array_calc_ex(zend_op_array *op_array) { if (op_array->function_name) { @@ -408,18 +463,32 @@ void zend_persist_class_entry_calc(zend_class_entry *ce) Bucket *p; if (ce->type == ZEND_USER_CLASS) { - /* The same zend_class_entry may be reused by class_alias */ - if (zend_shared_alloc_get_xlat_entry(ce)) { - return; - } + ZEND_ASSERT(!zend_shared_alloc_get_xlat_entry(ce)); zend_shared_alloc_register_xlat_entry(ce, ce); + zend_shared_alloc_register_xlat_entry(ZEND_CE_TO_REF(ce), ZEND_CE_TO_REF(ce)); - ADD_SIZE(sizeof(zend_class_entry)); + ADD_SIZE(sizeof(zend_class_entry_storage)); + ADD_INTERNED_STRING(ZEND_CE_TO_REF(ce)->key); if (!(ce->ce_flags & ZEND_ACC_CACHED)) { ADD_INTERNED_STRING(ce->name); if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { - ADD_INTERNED_STRING(ce->parent_name); + ADD_PNR(ce->parent_name); + } + } + if (ce->ce_flags & ZEND_ACC_LINKED) { + if (ce->num_parents) { + ADD_SIZE(sizeof(zend_class_reference *) * ce->num_parents); + for (uint32_t i = 0; i < ce->num_parents; i++) { + zend_class_reference *parent_ref = ce->parents[i]; + if (zend_shared_alloc_get_xlat_entry(parent_ref)) { + continue; + } + ZEND_ASSERT(!ZEND_REF_IS_TRIVIAL(parent_ref)); + ADD_SIZE(ZEND_CLASS_REF_SIZE(parent_ref->args.num_types)); + ADD_INTERNED_STRING(parent_ref->key); + zend_persist_type_list_calc(&parent_ref->args); + } } } @@ -563,7 +632,9 @@ static void zend_accel_persist_class_table_calc(HashTable *class_table) ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { ZEND_ASSERT(p->key != NULL); ADD_INTERNED_STRING(p->key); - zend_persist_class_entry_calc(Z_CE(p->val)); + if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { + zend_persist_class_entry_calc(Z_CE(p->val)); + } } ZEND_HASH_FOREACH_END(); } From 4ff8adaaa817dc72762bd0f8a81ef9545b55b748 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 12 Jan 2024 18:32:02 +0100 Subject: [PATCH 06/10] Build fixes --- Zend/zend_execute.c | 37 ++++++++++++++++++++++++++++---- ext/dom/php_dom.c | 16 +++++++------- ext/dom/php_dom.h | 2 +- ext/libxml/libxml.c | 6 ++---- ext/standard/var_unserializer.re | 1 + ext/tokenizer/tokenizer.c | 2 +- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e87b7af0ecd5f..e9a1795641ea2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -18,6 +18,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_portability.h" #include "zend_types.h" #define ZEND_INTENSIVE_DEBUGGING 0 @@ -1098,7 +1099,7 @@ static zend_never_inline bool check_property_type_generic( } #endif -static zend_type zend_resolve_generic_type(zend_class_reference *scope, uint32_t param_id) { +static zend_always_inline zend_type zend_resolve_generic_type(zend_class_reference *scope, uint32_t param_id) { if (param_id >= scope->ce->num_bound_generic_args) { ZEND_ASSERT(param_id - scope->ce->num_bound_generic_args < scope->args.num_types); return scope->args.types[param_id - scope->ce->num_bound_generic_args]; @@ -1222,6 +1223,10 @@ static zend_always_inline bool zend_validate_generic_args( # define HAVE_CACHE_SLOT 1 #endif +// TODO: cache_slot may not be worth it since ZSTR_CE_CACHE +#undef HAVE_CACHE_SLOT +#define HAVE_CACHE_SLOT 0 + #define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;} static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( @@ -1305,6 +1310,11 @@ static bool zend_check_intersection_type_from_cache_slot(zend_type_list *interse return status; } +// TODO +static bool zend_check_type_slow_recursive( + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type, bool is_internal); + static zend_always_inline bool zend_check_type_slow( zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope, bool is_return_type, bool is_internal) @@ -1342,8 +1352,14 @@ static zend_always_inline bool zend_check_type_slow( if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { return true; } - if (zend_check_type_slow( - &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { + if (ZEND_TYPE_HAS_PNR(real_type) && Z_TYPE_P(arg) == IS_OBJECT) { + ce = zend_fetch_ce_from_cache_slot(cache_slot, &real_type); + if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + return true; + } + } + if (zend_check_type_slow_recursive( + &real_type, arg, ref, cache_slot, scope, is_return_type, is_internal)) { return true; } } @@ -1368,7 +1384,13 @@ static zend_always_inline bool zend_check_type_slow( if (ZEND_TYPE_CONTAINS_CODE(real_type, Z_TYPE_P(arg))) { return true; } - if (zend_check_type_slow( + if (ZEND_TYPE_HAS_PNR(real_type) && Z_TYPE_P(arg) == IS_OBJECT) { + ce = zend_fetch_ce_from_cache_slot(cache_slot, &real_type); + if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + return true; + } + } + if (zend_check_type_slow_recursive( &real_type, arg, ref, /* cache_slot */ NULL, scope, is_return_type, is_internal)) { return true; } @@ -1402,6 +1424,13 @@ static zend_always_inline bool zend_check_type_slow( * because this case is already checked at compile-time. */ } +static bool zend_check_type_slow_recursive( + zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + zend_class_entry *scope, bool is_return_type, bool is_internal) +{ + return zend_check_type_slow(type, arg, ref, cache_slot, scope, is_return_type, is_internal); +} + static zend_always_inline bool zend_check_type( zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope, bool is_return_type, bool is_internal) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 632da61c59cd2..6f93c24b853d5 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -342,15 +342,15 @@ zval *dom_write_property(zend_object *object, zend_string *name, zval *value, vo if (hnd) { if (!hnd->write_func) { - zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); + zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(OBJ_NAME(object)), ZSTR_VAL(name)); return &EG(error_zval); } - zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true); + zend_property_info *prop = zend_get_property_info(OBJ_CE(object), name, /* silent */ true); if (prop && ZEND_TYPE_IS_SET(prop->type)) { zval tmp; ZVAL_COPY(&tmp, value); - if (!zend_verify_property_type(prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) { + if (!zend_verify_property_type(object, prop, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) { zval_ptr_dtor(&tmp); return &EG(error_zval); } @@ -510,11 +510,11 @@ static void dom_update_refcount_after_clone(dom_object *original, xmlNodePtr ori static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ { dom_object *intern = php_dom_obj_from_obj(zobject); - dom_object *clone = dom_objects_set_class(intern->std.ce); + dom_object *clone = dom_objects_set_class(OBJ_CE(&intern->std)); clone->std.handlers = dom_get_obj_handlers(); - if (instanceof_function(intern->std.ce, dom_node_class_entry)) { + if (instanceof_function(OBJ_CE(&intern->std), dom_node_class_entry)) { xmlNodePtr node = (xmlNodePtr)dom_object_get_node(intern); if (node != NULL) { xmlNodePtr cloned_node = xmlDocCopyNode(node, node->doc, 1); @@ -534,7 +534,7 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) { dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject); - zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce); + zend_object *clone = dom_objects_namespace_node_new(OBJ_CE(&intern->dom.std)); dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone); xmlNodePtr original_node = dom_object_get_node(&intern->dom); @@ -1087,8 +1087,8 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern) { zend_class_entry *base_class = class_type; - while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->parent != NULL) { - base_class = base_class->parent; + while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->num_parents != 0) { + base_class = base_class->parents[0]->ce; } intern->prop_handler = zend_hash_find_ptr(&classes, base_class->name); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index bb778e7ab9d41..c85d59f58a383 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -194,7 +194,7 @@ void dom_mark_namespaces_for_copy_based_on_copy(xmlNodePtr copy, const xmlNode * #define DOM_GET_INTERN(__id, __intern) { \ __intern = Z_DOMOBJ_P(__id); \ if (UNEXPECTED(__intern->ptr == NULL)) { \ - zend_throw_error(NULL, "Couldn't fetch %s", ZSTR_VAL(__intern->std.ce->name));\ + zend_throw_error(NULL, "Couldn't fetch %s", ZSTR_VAL(OBJ_NAME(&__intern->std)));\ RETURN_THROWS();\ } \ } diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 6d7f567b95994..bd6093fd963ba 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -15,6 +15,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_API.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -1258,10 +1259,7 @@ PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) php_libxml_func_handler *export_hnd; if (Z_TYPE_P(object) == IS_OBJECT) { - ce = Z_OBJCE_P(object); - while (ce->parent != NULL) { - ce = ce->parent; - } + ce = zend_class_entry_get_root(Z_OBJCE_P(object)); if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) { node = export_hnd->export_func(object); } diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 583a3ffc54a5d..19f876fee969d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -1184,6 +1184,7 @@ object ":" uiv ":" ["] { do { zend_string *lc_name; + ce = NULL; if (!(*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) { zend_class_reference *class_ref = ZSTR_GET_CE_CACHE(class_name); diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index 75cc99d7b84c6..d8d575e39a65f 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -100,7 +100,7 @@ PHP_METHOD(PhpToken, tokenize) Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END(); - token_class = zend_get_called_scope(execute_data); + token_class = zend_get_called_scope(execute_data)->ce; /* Check construction preconditions in advance, so these are not repeated for each token. */ if (token_class->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) { From e4e2fc3d50d18cf44b3f7f5a1b33e52538aab5d8 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 22 Jan 2024 17:58:54 +0100 Subject: [PATCH 07/10] Disallow usage of type params in static class members --- .../generic_param_in_static_context_001.phpt | 10 +++++++++ .../generic_param_in_static_context_002.phpt | 10 +++++++++ .../generic_param_in_static_context_003.phpt | 10 +++++++++ .../generic_param_in_static_context_004.phpt | 10 +++++++++ .../generic_param_in_static_context_005.phpt | 10 +++++++++ Zend/zend_compile.c | 21 +++++++++++++++++++ Zend/zend_globals.h | 1 + 7 files changed, 72 insertions(+) create mode 100644 Zend/tests/generics/generic_param_in_static_context_001.phpt create mode 100644 Zend/tests/generics/generic_param_in_static_context_002.phpt create mode 100644 Zend/tests/generics/generic_param_in_static_context_003.phpt create mode 100644 Zend/tests/generics/generic_param_in_static_context_004.phpt create mode 100644 Zend/tests/generics/generic_param_in_static_context_005.phpt diff --git a/Zend/tests/generics/generic_param_in_static_context_001.phpt b/Zend/tests/generics/generic_param_in_static_context_001.phpt new file mode 100644 index 0000000000000..6e02d4d991159 --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_001.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 001 +--FILE-- + { + public static T $a; +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_002.phpt b/Zend/tests/generics/generic_param_in_static_context_002.phpt new file mode 100644 index 0000000000000..001dda76f988d --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_002.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 002 +--FILE-- + { + public static function f(T $a) {} +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_003.phpt b/Zend/tests/generics/generic_param_in_static_context_003.phpt new file mode 100644 index 0000000000000..652aa76b83b63 --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_003.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 003 +--FILE-- + { + public static function f($a): T {} +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_004.phpt b/Zend/tests/generics/generic_param_in_static_context_004.phpt new file mode 100644 index 0000000000000..d72b163b5d37d --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_004.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 004 +--FILE-- + { + public static function f($a): T {} +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/tests/generics/generic_param_in_static_context_005.phpt b/Zend/tests/generics/generic_param_in_static_context_005.phpt new file mode 100644 index 0000000000000..40fd91ffc5b60 --- /dev/null +++ b/Zend/tests/generics/generic_param_in_static_context_005.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic param in static context 004 +--FILE-- + { + const T A = 1; +} +--EXPECTF-- +Fatal error: Cannot use type parameter "T" in static context in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 34c3aff03d5e5..e5cff0b2fd0f6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6748,6 +6748,11 @@ static zend_type zend_compile_single_typename(zend_ast *ast) zend_string *orig_name = zend_ast_get_str(name_ast); uint32_t generic_param_id = lookup_generic_param_id(orig_name); if (name_ast->attr == ZEND_NAME_NOT_FQ && generic_param_id != (uint32_t) -1) { + if (CG(in_static_class_member)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use type parameter \"%s\" in static context", + ZSTR_VAL(orig_name)); + } return (zend_type) ZEND_TYPE_INIT_GENERIC_PARAM(generic_param_id, 0); } @@ -7875,6 +7880,11 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) closure_info info; memset(&info, 0, sizeof(closure_info)); + bool orig_in_static_class_member = CG(in_static_class_member); + if (orig_class_entry && (decl->flags & ZEND_ACC_STATIC)) { + CG(in_static_class_member) = true; + } + init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE); if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { @@ -8007,6 +8017,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) CG(active_op_array) = orig_op_array; CG(active_class_entry) = orig_class_entry; + CG(in_static_class_member) = orig_in_static_class_member; } /* }}} */ @@ -8024,6 +8035,10 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f zend_error_noreturn(E_COMPILE_ERROR, "Enum %s cannot include properties", ZSTR_VAL(ce->name)); } + if (flags & ZEND_ACC_STATIC) { + CG(in_static_class_member) = true; + } + for (i = 0; i < children; ++i) { zend_property_info *info; zend_ast *prop_ast = list->child[i]; @@ -8110,6 +8125,8 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0); } } + + CG(in_static_class_member) = false; } /* }}} */ @@ -8139,6 +8156,8 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_class_entry *ce = CG(active_class_entry); uint32_t i, children = list->children; + CG(in_static_class_member) = true; + for (i = 0; i < children; ++i) { zend_class_constant *c; zend_ast *const_ast = list->child[i]; @@ -8185,6 +8204,8 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0); } } + + CG(in_static_class_member) = false; } static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */ diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index fab2dc2909da4..407f1fdd12f43 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -83,6 +83,7 @@ struct _zend_compiler_globals { zend_stack loop_var_stack; zend_class_entry *active_class_entry; + bool in_static_class_member; /* compiling a static prop, static method, or class const */ zend_string *compiled_filename; From 03fc706527e7acc02c090d8df434fdbe9047e81e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 23 Jan 2024 16:04:39 +0100 Subject: [PATCH 08/10] Add def-site variance syntax --- Zend/zend.h | 7 +++++++ Zend/zend_ast.h | 2 +- Zend/zend_compile.c | 32 +++++++++++++++++++++++++++----- Zend/zend_language_parser.y | 22 +++++++++++++++------- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/Zend/zend.h b/Zend/zend.h index 026094feddb8d..ab00cc743252a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -144,10 +144,17 @@ struct _zend_inheritance_cache_entry { zend_class_entry *traits_and_interfaces[1]; }; +typedef enum _zend_generic_param_variance { + ZEND_GENERIC_PARAM_INVARIANT, + ZEND_GENERIC_PARAM_IN, + ZEND_GENERIC_PARAM_OUT, +} zend_generic_param_variance; + typedef struct _zend_generic_param { zend_string *name; zend_type bound_type; zend_type default_type; + zend_generic_param_variance variance; } zend_generic_param; struct _zend_class_entry { diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 4d2894a646946..b7cbb6ab1ac3e 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -165,7 +165,6 @@ enum _zend_ast_kind { ZEND_AST_PROP_ELEM, ZEND_AST_CONST_ELEM, ZEND_AST_CLASS_CONST_GROUP, - ZEND_AST_GENERIC_PARAM, // Pseudo node for initializing enums ZEND_AST_CONST_ENUM_INIT, @@ -174,6 +173,7 @@ enum _zend_ast_kind { ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_FOREACH, ZEND_AST_ENUM_CASE, + ZEND_AST_GENERIC_PARAM, /* 5 child nodes */ ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e5cff0b2fd0f6..a18424dd882de 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8382,6 +8382,21 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type_release(type, 0); } +static zend_generic_param_variance zend_compile_generic_param_variance(zend_string *str) +{ + if (str == NULL) { + return ZEND_GENERIC_PARAM_INVARIANT; + } + if (zend_binary_strcasecmp(ZSTR_VAL(str), ZSTR_LEN(str), "in", 2) == 0) { + return ZEND_GENERIC_PARAM_IN; + } + if (zend_binary_strcasecmp(ZSTR_VAL(str), ZSTR_LEN(str), "out", 3) == 0) { + return ZEND_GENERIC_PARAM_OUT; + } + zend_error_noreturn(E_COMPILE_ERROR, + "Unexpected \"%s\", expected \"in\" or \"out\"", ZSTR_VAL(str)); +} + static void zend_compile_generic_params(zend_ast *params_ast) { zend_ast_list *list = zend_ast_get_list(params_ast); @@ -8391,9 +8406,15 @@ static void zend_compile_generic_params(zend_ast *params_ast) bool have_optional = 0; for (uint32_t i = 0; i < list->children; i++) { zend_ast *param_ast = list->child[i]; - zend_string *name = zend_ast_get_str(param_ast->child[0]); + zend_string *name = zend_ast_get_str(param_ast->child[1]); zend_type bound_type = ZEND_TYPE_INIT_NONE(0); zend_type default_type = ZEND_TYPE_INIT_NONE(0); + zend_generic_param_variance variance = ZEND_GENERIC_PARAM_INVARIANT; + + if (param_ast->child[0] != NULL) { + variance = zend_compile_generic_param_variance( + zend_ast_get_str(param_ast->child[0])); + } if (zend_string_equals(name, CG(active_class_entry)->name)) { zend_error_noreturn(E_COMPILE_ERROR, @@ -8407,11 +8428,11 @@ static void zend_compile_generic_params(zend_ast *params_ast) } } - if (param_ast->child[1]) { - bound_type = zend_compile_typename(param_ast->child[1]); - } if (param_ast->child[2]) { - default_type = zend_compile_typename(param_ast->child[2]); + bound_type = zend_compile_typename(param_ast->child[2]); + } + if (param_ast->child[3]) { + default_type = zend_compile_typename(param_ast->child[3]); } if (ZEND_TYPE_IS_SET(default_type)) { @@ -8424,6 +8445,7 @@ static void zend_compile_generic_params(zend_ast *params_ast) generic_params[i].name = zend_string_copy(name); generic_params[i].bound_type = bound_type; generic_params[i].default_type = default_type; + generic_params[i].variance = variance; // TODO: Validate potential additional constraints on the types. // For example, can "void" be used? diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 795bd88bc2e1b..23b6adeeae754 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -283,7 +283,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list %type enum_declaration_statement enum_backing_type enum_case enum_case_expr %type function_name non_empty_member_modifiers -%type optional_generic_params generic_params generic_param +%type optional_generic_params generic_params generic_param generic_param_variance %type returns_ref function fn is_reference is_variadic property_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -603,13 +603,21 @@ generic_params: { $$ = zend_ast_list_add($1, $3); } ; -/* TODO: in/out indicator */ generic_param: - T_STRING { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, NULL, NULL); } - | T_STRING ':' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $3, NULL); } - | T_STRING '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, NULL, $3); } - | T_STRING ':' type_expr '=' type_expr - { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $3, $5); } + T_STRING { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, NULL, NULL); } + | generic_param_variance T_STRING { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, NULL, NULL); } + | T_STRING ':' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, $3, NULL); } + | generic_param_variance T_STRING ':' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, $4, NULL); } + | T_STRING '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, NULL, $3); } + | generic_param_variance T_STRING '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, NULL, $4); } + | T_STRING ':' type_expr '=' type_expr { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, NULL, $1, $3, $5); } + | generic_param_variance T_STRING ':' type_expr '=' type_expr + { $$ = zend_ast_create(ZEND_AST_GENERIC_PARAM, $1, $2, $4, $6); } +; + +generic_param_variance: + /* TODO: maybe introduce actual tokens */ + T_STRING { $$ = $1; } ; class_declaration_statement: From 046551f37d0f05438ee7be7077da7bce3fcceba7 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 9 Feb 2024 15:20:57 +0100 Subject: [PATCH 09/10] Extract zend_ssa_find_sccs from zend_inference.c --- Zend/Optimizer/dfa_pass.c | 1 + Zend/Optimizer/zend_inference.c | 409 +------------------------------- Zend/Optimizer/zend_inference.h | 1 - Zend/Optimizer/zend_scc.c | 374 +++++++++++++++++++++++++++++ Zend/Optimizer/zend_scc.h | 27 +++ Zend/Optimizer/zend_ssa.h | 50 ++++ configure.ac | 1 + 7 files changed, 465 insertions(+), 398 deletions(-) create mode 100644 Zend/Optimizer/zend_scc.c create mode 100644 Zend/Optimizer/zend_scc.h diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index a26f26b65d65e..93a7247dd5d7a 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -29,6 +29,7 @@ #include "zend_call_graph.h" #include "zend_inference.h" #include "zend_dump.h" +#include "zend_scc.h" #ifndef ZEND_DEBUG_DFA # define ZEND_DEBUG_DFA ZEND_DEBUG diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 15f8c458181e9..47be51031026d 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -60,40 +60,6 @@ #define LOG_NEG_RANGE(...) #endif -/* Pop elements in unspecified order from worklist until it is empty */ -#define WHILE_WORKLIST(worklist, len, i) do { \ - bool _done = 0; \ - while (!_done) { \ - _done = 1; \ - ZEND_BITSET_FOREACH(worklist, len, i) { \ - zend_bitset_excl(worklist, i); \ - _done = 0; - -#define WHILE_WORKLIST_END() \ - } ZEND_BITSET_FOREACH_END(); \ - } \ -} while (0) - -#define CHECK_SCC_VAR(var2) \ - do { \ - if (!ssa->vars[var2].no_val) { \ - if (ssa->vars[var2].scc < 0) { \ - zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \ - } \ - if (ssa->vars[var2].scc < ssa->vars[var].scc) { \ - ssa->vars[var].scc = ssa->vars[var2].scc; \ - is_root = 0; \ - } \ - } \ - } while (0) - -#define CHECK_SCC_ENTRY(var2) \ - do { \ - if (ssa->vars[var2].scc != ssa->vars[var].scc) { \ - ssa->vars[var2].scc_entry = 1; \ - } \ - } while (0) - #define ADD_SCC_VAR(_var) \ do { \ if (ssa->vars[_var].scc == scc && \ @@ -111,56 +77,19 @@ } \ } while (0) -#define FOR_EACH_DEFINED_VAR(line, MACRO) \ - do { \ - if (ssa->ops[line].op1_def >= 0) { \ - MACRO(ssa->ops[line].op1_def); \ - } \ - if (ssa->ops[line].op2_def >= 0) { \ - MACRO(ssa->ops[line].op2_def); \ - } \ - if (ssa->ops[line].result_def >= 0) { \ - MACRO(ssa->ops[line].result_def); \ - } \ - if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \ - if (ssa->ops[line-1].op1_def >= 0) { \ - MACRO(ssa->ops[line-1].op1_def); \ - } \ - if (ssa->ops[line-1].op2_def >= 0) { \ - MACRO(ssa->ops[line-1].op2_def); \ - } \ - if (ssa->ops[line-1].result_def >= 0) { \ - MACRO(ssa->ops[line-1].result_def); \ - } \ - } else if ((uint32_t)line+1 < op_array->last && \ - op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \ - if (ssa->ops[line+1].op1_def >= 0) { \ - MACRO(ssa->ops[line+1].op1_def); \ - } \ - if (ssa->ops[line+1].op2_def >= 0) { \ - MACRO(ssa->ops[line+1].op2_def); \ - } \ - if (ssa->ops[line+1].result_def >= 0) { \ - MACRO(ssa->ops[line+1].result_def); \ - } \ - } \ - } while (0) - +/* Pop elements in unspecified order from worklist until it is empty */ +#define WHILE_WORKLIST(worklist, len, i) do { \ + bool _done = 0; \ + while (!_done) { \ + _done = 1; \ + ZEND_BITSET_FOREACH(worklist, len, i) { \ + zend_bitset_excl(worklist, i); \ + _done = 0; -#define FOR_EACH_VAR_USAGE(_var, MACRO) \ - do { \ - zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ - int use = ssa->vars[_var].use_chain; \ - while (use >= 0) { \ - FOR_EACH_DEFINED_VAR(use, MACRO); \ - use = zend_ssa_next_use(ssa->ops, _var, use); \ - } \ - p = ssa->vars[_var].phi_use_chain; \ - while (p) { \ - MACRO(p->ssa_var); \ - p = zend_ssa_next_use_phi(ssa, _var, p); \ - } \ - } while (0) +#define WHILE_WORKLIST_END() \ + } ZEND_BITSET_FOREACH_END(); \ + } \ +} while (0) static inline bool add_will_overflow(zend_long a, zend_long b) { return (b > 0 && a > ZEND_LONG_MAX - b) @@ -173,320 +102,6 @@ static inline bool sub_will_overflow(zend_long a, zend_long b) { } #endif -#if 0 -/* Recursive Pearce's SCC algorithm implementation */ -static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */ -{ - int is_root = 1; -#ifdef SYM_RANGE - zend_ssa_phi *p; -#endif - - ssa->vars[var].scc = *index; - (*index)++; - - FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); - -#ifdef SYM_RANGE - /* Process symbolic control-flow constraints */ - p = ssa->vars[var].sym_use_chain; - while (p) { - CHECK_SCC_VAR(p->ssa_var); - p = p->sym_use_chain; - } -#endif - - if (is_root) { - ssa->sccs--; - while (stack->len > 0) { - int var2 = zend_worklist_stack_peek(stack); - if (ssa->vars[var2].scc < ssa->vars[var].scc) { - break; - } - zend_worklist_stack_pop(stack); - ssa->vars[var2].scc = ssa->sccs; - (*index)--; - } - ssa->vars[var].scc = ssa->sccs; - ssa->vars[var].scc_entry = 1; - (*index)--; - } else { - zend_worklist_stack_push(stack, var); - } -} -/* }}} */ - -ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - int index = 0; - zend_worklist_stack stack; - int j; - ALLOCA_FLAG(stack_use_heap) - - ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - - /* Find SCCs using Pearce's algorithm. */ - ssa->sccs = ssa->vars_count; - for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack); - } - } - - if (ssa->sccs) { - /* Shift SCC indexes. */ - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc -= ssa->sccs; - } - } - } - ssa->sccs = ssa->vars_count - ssa->sccs; - - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - int var = j; - FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); - } - } - - ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); -} -/* }}} */ - -#else -/* Iterative Pearce's SCC algorithm implementation */ - -typedef struct _zend_scc_iterator { - int state; - int last; - union { - int use; - zend_ssa_phi *phi; - }; -} zend_scc_iterator; - -static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */ -{ - zend_ssa_phi *phi; - int use, var2; - - switch (iterator->state) { - case 0: goto state_0; - case 1: use = iterator->use; goto state_1; - case 2: use = iterator->use; goto state_2; - case 3: use = iterator->use; goto state_3; - case 4: use = iterator->use; goto state_4; - case 5: use = iterator->use; goto state_5; - case 6: use = iterator->use; goto state_6; - case 7: use = iterator->use; goto state_7; - case 8: use = iterator->use; goto state_8; - case 9: phi = iterator->phi; goto state_9; -#ifdef SYM_RANGE - case 10: phi = iterator->phi; goto state_10; -#endif - case 11: goto state_11; - } - -state_0: - use = ssa->vars[var].use_chain; - while (use >= 0) { - iterator->use = use; - var2 = ssa->ops[use].op1_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 1; - return var2; - } -state_1: - var2 = ssa->ops[use].op2_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 2; - return var2; - } -state_2: - var2 = ssa->ops[use].result_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 3; - return var2; - } -state_3: - if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { - var2 = ssa->ops[use-1].op1_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 4; - return var2; - } -state_4: - var2 = ssa->ops[use-1].op2_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 5; - return var2; - } -state_5: - var2 = ssa->ops[use-1].result_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 8; - return var2; - } - } else if ((uint32_t)use+1 < op_array->last && - op_array->opcodes[use+1].opcode == ZEND_OP_DATA) { - var2 = ssa->ops[use+1].op1_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 6; - return var2; - } -state_6: - var2 = ssa->ops[use+1].op2_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 7; - return var2; - } -state_7: - var2 = ssa->ops[use+1].result_def; - if (var2 >= 0 && !ssa->vars[var2].no_val) { - iterator->state = 8; - return var2; - } - } -state_8: - use = zend_ssa_next_use(ssa->ops, var, use); - } - - phi = ssa->vars[var].phi_use_chain; - while (phi) { - var2 = phi->ssa_var; - if (!ssa->vars[var2].no_val) { - iterator->state = 9; - iterator->phi = phi; - return var2; - } -state_9: - phi = zend_ssa_next_use_phi(ssa, var, phi); - } - -#ifdef SYM_RANGE - /* Process symbolic control-flow constraints */ - phi = ssa->vars[var].sym_use_chain; - while (phi) { - var2 = phi->ssa_var; - if (!ssa->vars[var2].no_val) { - iterator->state = 10; - iterator->phi = phi; - return var2; - } -state_10: - phi = phi->sym_use_chain; - } -#endif - - iterator->state = 11; -state_11: - return -1; -} -/* }}} */ - -static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack, zend_worklist_stack *vstack, zend_scc_iterator *iterators) /* {{{ */ -{ -restart: - zend_worklist_stack_push(vstack, var); - iterators[var].state = 0; - iterators[var].last = -1; - ssa->vars[var].scc_entry = 1; - ssa->vars[var].scc = *index; - (*index)++; - - while (vstack->len > 0) { - var = zend_worklist_stack_peek(vstack); - while (1) { - int var2; - - if (iterators[var].last >= 0) { - /* finish edge */ - var2 = iterators[var].last; - if (ssa->vars[var2].scc < ssa->vars[var].scc) { - ssa->vars[var].scc = ssa->vars[var2].scc; - ssa->vars[var].scc_entry = 0; - } - } - var2 = zend_scc_next(op_array, ssa, var, iterators + var); - iterators[var].last = var2; - if (var2 < 0) break; - /* begin edge */ - if (ssa->vars[var2].scc < 0) { - var = var2; - goto restart; - } - } - - /* finish visiting */ - zend_worklist_stack_pop(vstack); - if (ssa->vars[var].scc_entry) { - ssa->sccs--; - while (stack->len > 0) { - int var2 = zend_worklist_stack_peek(stack); - if (ssa->vars[var2].scc < ssa->vars[var].scc) { - break; - } - zend_worklist_stack_pop(stack); - ssa->vars[var2].scc = ssa->sccs; - (*index)--; - } - ssa->vars[var].scc = ssa->sccs; - (*index)--; - } else { - zend_worklist_stack_push(stack, var); - } - } -} -/* }}} */ - -ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - int index = 0; - zend_worklist_stack stack, vstack; - zend_scc_iterator *iterators; - int j; - ALLOCA_FLAG(stack_use_heap) - ALLOCA_FLAG(vstack_use_heap) - ALLOCA_FLAG(iterators_use_heap) - - iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap); - ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap); - ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - - /* Find SCCs using Pearce's algorithm. */ - ssa->sccs = ssa->vars_count; - for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators); - } - } - - if (ssa->sccs) { - /* Shift SCC indexes. */ - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc -= ssa->sccs; - } - } - } - ssa->sccs = ssa->vars_count - ssa->sccs; - - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - int var = j; - FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); - } - } - - ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); - ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap); - free_alloca(iterators, iterators_use_heap); -} -/* }}} */ - -#endif - ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { zend_ssa_var *ssa_vars = ssa->vars; diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index 31c742c16d994..8279a7292be6b 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -220,7 +220,6 @@ static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b) BEGIN_EXTERN_C() ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa); -ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); ZEND_API zend_result zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level); ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, int write, int insert); diff --git a/Zend/Optimizer/zend_scc.c b/Zend/Optimizer/zend_scc.c new file mode 100644 index 0000000000000..683bdccbdf6ce --- /dev/null +++ b/Zend/Optimizer/zend_scc.c @@ -0,0 +1,374 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend_compile.h" +#include "zend_worklist.h" +#include "zend_optimizer_internal.h" + +#define CHECK_SCC_VAR(var2) \ + do { \ + if (!ssa->vars[var2].no_val) { \ + if (ssa->vars[var2].scc < 0) { \ + zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \ + } \ + if (ssa->vars[var2].scc < ssa->vars[var].scc) { \ + ssa->vars[var].scc = ssa->vars[var2].scc; \ + is_root = 0; \ + } \ + } \ + } while (0) + +#define CHECK_SCC_ENTRY(var2) \ + do { \ + if (ssa->vars[var2].scc != ssa->vars[var].scc) { \ + ssa->vars[var2].scc_entry = 1; \ + } \ + } while (0) + +#define ADD_SCC_VAR(_var) \ + do { \ + if (ssa->vars[_var].scc == scc && \ + !(ssa->var_info[_var].type & MAY_BE_REF)) { \ + zend_bitset_incl(worklist, _var); \ + } \ + } while (0) + +#define ADD_SCC_VAR_1(_var) \ + do { \ + if (ssa->vars[_var].scc == scc && \ + !(ssa->var_info[_var].type & MAY_BE_REF) && \ + !zend_bitset_in(visited, _var)) { \ + zend_bitset_incl(worklist, _var); \ + } \ + } while (0) + +#if 0 +/* Recursive Pearce's SCC algorithm implementation */ +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */ +{ + int is_root = 1; +#ifdef SYM_RANGE + zend_ssa_phi *p; +#endif + + ssa->vars[var].scc = *index; + (*index)++; + + FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + p = ssa->vars[var].sym_use_chain; + while (p) { + CHECK_SCC_VAR(p->ssa_var); + p = p->sym_use_chain; + } +#endif + + if (is_root) { + ssa->sccs--; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + (*index)--; + } + ssa->vars[var].scc = ssa->sccs; + ssa->vars[var].scc_entry = 1; + (*index)--; + } else { + zend_worklist_stack_push(stack, var); + } +} +/* }}} */ + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ +{ + int index = 0; + zend_worklist_stack stack; + int j; + ALLOCA_FLAG(stack_use_heap) + + ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); + + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack); + } + } + + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + int var = j; + FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); + } + } + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); +} +/* }}} */ + +#else +/* Iterative Pearce's SCC algorithm implementation */ + +typedef struct _zend_scc_iterator { + int state; + int last; + union { + int use; + zend_ssa_phi *phi; + }; +} zend_scc_iterator; + +static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */ +{ + zend_ssa_phi *phi; + int use, var2; + + switch (iterator->state) { + case 0: goto state_0; + case 1: use = iterator->use; goto state_1; + case 2: use = iterator->use; goto state_2; + case 3: use = iterator->use; goto state_3; + case 4: use = iterator->use; goto state_4; + case 5: use = iterator->use; goto state_5; + case 6: use = iterator->use; goto state_6; + case 7: use = iterator->use; goto state_7; + case 8: use = iterator->use; goto state_8; + case 9: phi = iterator->phi; goto state_9; +#ifdef SYM_RANGE + case 10: phi = iterator->phi; goto state_10; +#endif + case 11: goto state_11; + } + +state_0: + use = ssa->vars[var].use_chain; + while (use >= 0) { + iterator->use = use; + var2 = ssa->ops[use].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 1; + return var2; + } +state_1: + var2 = ssa->ops[use].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 2; + return var2; + } +state_2: + var2 = ssa->ops[use].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 3; + return var2; + } +state_3: + if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use-1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 4; + return var2; + } +state_4: + var2 = ssa->ops[use-1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 5; + return var2; + } +state_5: + var2 = ssa->ops[use-1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } else if ((uint32_t)use+1 < op_array->last && + op_array->opcodes[use+1].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use+1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 6; + return var2; + } +state_6: + var2 = ssa->ops[use+1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 7; + return var2; + } +state_7: + var2 = ssa->ops[use+1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } +state_8: + use = zend_ssa_next_use(ssa->ops, var, use); + } + + phi = ssa->vars[var].phi_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 9; + iterator->phi = phi; + return var2; + } +state_9: + phi = zend_ssa_next_use_phi(ssa, var, phi); + } + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + phi = ssa->vars[var].sym_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 10; + iterator->phi = phi; + return var2; + } +state_10: + phi = phi->sym_use_chain; + } +#endif + + iterator->state = 11; +state_11: + return -1; +} +/* }}} */ + +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack, zend_worklist_stack *vstack, zend_scc_iterator *iterators) /* {{{ */ +{ +restart: + zend_worklist_stack_push(vstack, var); + iterators[var].state = 0; + iterators[var].last = -1; + ssa->vars[var].scc_entry = 1; + ssa->vars[var].scc = *index; + (*index)++; + + while (vstack->len > 0) { + var = zend_worklist_stack_peek(vstack); + while (1) { + int var2; + + if (iterators[var].last >= 0) { + /* finish edge */ + var2 = iterators[var].last; + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + ssa->vars[var].scc = ssa->vars[var2].scc; + ssa->vars[var].scc_entry = 0; + } + } + var2 = zend_scc_next(op_array, ssa, var, iterators + var); + iterators[var].last = var2; + if (var2 < 0) break; + /* begin edge */ + if (ssa->vars[var2].scc < 0) { + var = var2; + goto restart; + } + } + + /* finish visiting */ + zend_worklist_stack_pop(vstack); + if (ssa->vars[var].scc_entry) { + ssa->sccs--; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + (*index)--; + } + ssa->vars[var].scc = ssa->sccs; + (*index)--; + } else { + zend_worklist_stack_push(stack, var); + } + } +} +/* }}} */ + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ +{ + int index = 0; + zend_worklist_stack stack, vstack; + zend_scc_iterator *iterators; + int j; + ALLOCA_FLAG(stack_use_heap) + ALLOCA_FLAG(vstack_use_heap) + ALLOCA_FLAG(iterators_use_heap) + + iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); + + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators); + } + } + + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + int var = j; + FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); + } + } + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); + ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap); + free_alloca(iterators, iterators_use_heap); +} +/* }}} */ + +#endif + + diff --git a/Zend/Optimizer/zend_scc.h b/Zend/Optimizer/zend_scc.h new file mode 100644 index 0000000000000..254234175da4a --- /dev/null +++ b/Zend/Optimizer/zend_scc.h @@ -0,0 +1,27 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SCC_H +#define ZEND_SCC_H + +#include "zend_compile.h" +#include "zend_ssa.h" + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); + +#endif /* ZEND_SCC_H */ diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h index b59c59a1b8673..88a66d0d6df98 100644 --- a/Zend/Optimizer/zend_ssa.h +++ b/Zend/Optimizer/zend_ssa.h @@ -325,4 +325,54 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend } FOREACH_BLOCK_END(); \ } while (0) +#define FOR_EACH_DEFINED_VAR(line, MACRO) \ + do { \ + if (ssa->ops[line].op1_def >= 0) { \ + MACRO(ssa->ops[line].op1_def); \ + } \ + if (ssa->ops[line].op2_def >= 0) { \ + MACRO(ssa->ops[line].op2_def); \ + } \ + if (ssa->ops[line].result_def >= 0) { \ + MACRO(ssa->ops[line].result_def); \ + } \ + if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \ + if (ssa->ops[line-1].op1_def >= 0) { \ + MACRO(ssa->ops[line-1].op1_def); \ + } \ + if (ssa->ops[line-1].op2_def >= 0) { \ + MACRO(ssa->ops[line-1].op2_def); \ + } \ + if (ssa->ops[line-1].result_def >= 0) { \ + MACRO(ssa->ops[line-1].result_def); \ + } \ + } else if ((uint32_t)line+1 < op_array->last && \ + op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \ + if (ssa->ops[line+1].op1_def >= 0) { \ + MACRO(ssa->ops[line+1].op1_def); \ + } \ + if (ssa->ops[line+1].op2_def >= 0) { \ + MACRO(ssa->ops[line+1].op2_def); \ + } \ + if (ssa->ops[line+1].result_def >= 0) { \ + MACRO(ssa->ops[line+1].result_def); \ + } \ + } \ + } while (0) + +#define FOR_EACH_VAR_USAGE(_var, MACRO) \ + do { \ + zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ + int use = ssa->vars[_var].use_chain; \ + while (use >= 0) { \ + FOR_EACH_DEFINED_VAR(use, MACRO); \ + use = zend_ssa_next_use(ssa->ops, _var, use); \ + } \ + p = ssa->vars[_var].phi_use_chain; \ + while (p) { \ + MACRO(p->ssa_var); \ + p = zend_ssa_next_use_phi(ssa, _var, p); \ + } \ + } while (0) + #endif /* ZEND_SSA_H */ diff --git a/configure.ac b/configure.ac index ed765780448e1..66bdf0db3f2a9 100644 --- a/configure.ac +++ b/configure.ac @@ -1767,6 +1767,7 @@ PHP_ADD_SOURCES(Zend, \ Optimizer/escape_analysis.c \ Optimizer/compact_vars.c \ Optimizer/zend_dump.c \ + Optimizer/zend_scc.c \ , -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_BUILD_DIR(main main/streams) From e595c7415cb52c35ea42fdb1b7785500e6ea4e4e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 12 Feb 2024 19:43:10 +0100 Subject: [PATCH 10/10] Basic type inference, variance, bounds --- .gdbinit | 9 +- Zend/Optimizer/ssa_integrity.c | 20 +- Zend/Optimizer/zend_cfg.h | 5 + Zend/Optimizer/zend_dump.c | 21 +- Zend/Optimizer/zend_optimizer.c | 4 +- Zend/Optimizer/zend_optimizer.h | 2 + Zend/Optimizer/zend_ssa.c | 166 +- Zend/tests/generics/bounds.phpt | 24 + Zend/tests/generics/inference.phpt | 155 ++ Zend/tests/generics/symbolic_inference.inc | 95 + Zend/tests/generics/symbolic_inference.phpt | 642 ++++++ .../symbolic_inference_overloading.phpt | 37 + Zend/tests/generics/type_checking.phpt | 1173 ++++++++++ Zend/tests/generics/variance.phpt | 70 + Zend/zend_API.c | 21 +- Zend/zend_compile.c | 139 +- Zend/zend_compile.h | 2 + Zend/zend_execute.c | 477 +++- Zend/zend_execute.h | 10 + Zend/zend_execute_API.c | 15 + Zend/zend_inheritance.c | 34 +- Zend/zend_language_scanner.l | 7 + Zend/zend_opcode.c | 4 +- Zend/zend_operators.c | 60 +- Zend/zend_operators.h | 18 +- Zend/zend_string.h | 1 - Zend/zend_symbolic_inference.c | 2040 +++++++++++++++++ Zend/zend_symbolic_inference.h | 13 + Zend/zend_type_tools.c | 334 +++ Zend/zend_type_tools.h | 14 + Zend/zend_types.h | 22 +- Zend/zend_vm_def.h | 19 +- Zend/zend_vm_execute.h | 51 +- configure.ac | 3 + ext/gmp/gmp.c | 2 +- ext/reflection/php_reflection.c | 4 +- ext/standard/var.c | 24 +- 37 files changed, 5574 insertions(+), 163 deletions(-) create mode 100644 Zend/tests/generics/bounds.phpt create mode 100644 Zend/tests/generics/inference.phpt create mode 100644 Zend/tests/generics/symbolic_inference.inc create mode 100644 Zend/tests/generics/symbolic_inference.phpt create mode 100644 Zend/tests/generics/symbolic_inference_overloading.phpt create mode 100644 Zend/tests/generics/type_checking.phpt create mode 100644 Zend/tests/generics/variance.phpt create mode 100644 Zend/zend_symbolic_inference.c create mode 100644 Zend/zend_symbolic_inference.h create mode 100644 Zend/zend_type_tools.c create mode 100644 Zend/zend_type_tools.h diff --git a/.gdbinit b/.gdbinit index d3b456239e375..150f7aa3575d6 100644 --- a/.gdbinit +++ b/.gdbinit @@ -208,7 +208,7 @@ define ____printzv_contents set $handle = $zvalue->value.obj.handle set $handlers = $zvalue->value.obj.handlers set $zobj = $zvalue->value.obj - set $cname = $zobj->ce->name->val + set $cname = $zobj->cr->ce->name->val printf "(%s) #%d", $cname, $handle if ! $arg1 if $handlers->get_properties == &zend_std_get_properties @@ -225,7 +225,7 @@ define ____printzv_contents end else printf " {\n" - set $ht = &$zobj->ce->properties_info + set $ht = &$zobj->cr->ce->properties_info set $k = 0 set $num = $ht->nNumUsed while $k < $num @@ -653,3 +653,8 @@ document lookup_root lookup a refcounted in root usage: lookup_root [ptr]. end + +define print_opcode + set $opcode = $arg0 + print zend_get_opcode_name($opcode) +end diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c index b525f8d5ee226..8ffc805eeb207 100644 --- a/Zend/Optimizer/ssa_integrity.c +++ b/Zend/Optimizer/ssa_integrity.c @@ -123,7 +123,6 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex for (i = 0; i < ssa->vars_count; i++) { zend_ssa_var *var = &ssa->vars[i]; int use; - uint32_t type = ssa->var_info[i].type; if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) { if (var->use_chain >= 0) { @@ -192,14 +191,17 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex } } FOREACH_PHI_USE_END(); - if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) { - FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); - } - if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) { - FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); - } - if ((type & MAY_BE_REF) && ssa->var_info[i].ce) { - FAIL("var " VARFMT " may be ref but has ce\n", VAR(i)); + if (ssa->var_info) { + uint32_t type = ssa->var_info[i].type; + if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) { + FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); + } + if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) { + FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); + } + if ((type & MAY_BE_REF) && ssa->var_info[i].ce) { + FAIL("var " VARFMT " may be ref but has ce\n", VAR(i)); + } } } diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h index 93d455060686e..75e46bc32bbe9 100644 --- a/Zend/Optimizer/zend_cfg.h +++ b/Zend/Optimizer/zend_cfg.h @@ -99,6 +99,11 @@ typedef struct _zend_cfg { #define ZEND_CFG_RECV_ENTRY (1<<24) #define ZEND_CALL_TREE (1<<23) #define ZEND_SSA_USE_CV_RESULTS (1<<22) +/* Chain function call opcodes by adding fake defs and uses. E.g. in a sequence + * INIT_FCALL->SEND->SEND->DO_FCALL, each element defines an SSA var that is + * used by the next one. This can be used to ensure that a worklist algorithm + * would visit DO_FCALL if any of the previous op codes were visited. */ +#define ZEND_SSA_CALL_CHAINS (1<<21) #define CRT_CONSTANT_EX(op_array, opline, node) \ (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \ diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c index 3bfca0493bab3..12945a3ae76df 100644 --- a/Zend/Optimizer/zend_dump.c +++ b/Zend/Optimizer/zend_dump.c @@ -408,7 +408,11 @@ ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *s } else { fprintf(stderr, "#?."); } - zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num); + if (var_num >= 0) { + zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num); + } else { + fprintf(stderr, "?"); + } if (ssa_var_num >= 0 && ssa->vars) { if (ssa->vars[ssa_var_num].no_val) { @@ -481,13 +485,14 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block uint32_t n = 0; if (!ssa_op || ssa_op->result_use < 0) { - if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - if (ssa_op && ssa_op->result_def >= 0) { - int ssa_var_num = ssa_op->result_def; - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags); - } else { - zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); - } + if (ssa_op && ssa_op->result_def >= 0) { + int ssa_var_num = ssa_op->result_def; + zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, + (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) ? EX_VAR_TO_NUM(opline->result.var) : -1, + dump_flags); + fprintf(stderr, " = "); + } else if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { + zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); fprintf(stderr, " = "); } } diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index f20de6e61d0c8..61b69d949a888 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -1078,7 +1078,7 @@ static void zend_optimize(zend_op_array *op_array, } } -static void zend_revert_pass_two(zend_op_array *op_array) +void zend_revert_pass_two(zend_op_array *op_array) { zend_op *opline, *end; @@ -1110,7 +1110,7 @@ static void zend_revert_pass_two(zend_op_array *op_array) op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO; } -static void zend_redo_pass_two(zend_op_array *op_array) +void zend_redo_pass_two(zend_op_array *op_array) { zend_op *opline, *end; #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR diff --git a/Zend/Optimizer/zend_optimizer.h b/Zend/Optimizer/zend_optimizer.h index 16bfd75520d89..9a5accb6b42cd 100644 --- a/Zend/Optimizer/zend_optimizer.h +++ b/Zend/Optimizer/zend_optimizer.h @@ -98,6 +98,8 @@ ZEND_API int zend_optimizer_register_pass(zend_optimizer_pass_t pass); ZEND_API void zend_optimizer_unregister_pass(int idx); zend_result zend_optimizer_startup(void); zend_result zend_optimizer_shutdown(void); +void zend_revert_pass_two(zend_op_array *op_array); +void zend_redo_pass_two(zend_op_array *op_array); END_EXTERN_C() #endif diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 590df8155e395..633b6628c32c4 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -17,12 +17,14 @@ +----------------------------------------------------------------------+ */ +#include "zend_arena.h" #include "zend_compile.h" #include "zend_dfg.h" #include "zend_ssa.h" #include "zend_dump.h" #include "zend_inference.h" #include "Optimizer/zend_optimizer_internal.h" +#include "zend_vm_opcodes.h" static bool dominates(const zend_basic_block *blocks, int a, int b) { while (blocks[b].level > blocks[a].level) { @@ -540,6 +542,81 @@ static void place_essa_pis( } /* }}} */ +static const zend_op *zend_ssa_find_prev_send_op(const zend_op_array *op_array, const zend_op *opline) +{ + int depth = 1; + + for (opline--; opline >= op_array->opcodes; opline--) { + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth--; + if (depth == 0) { + return NULL; + } + break; + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + if (depth == 1) { + return opline; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth++; + break; + } + } + + ZEND_UNREACHABLE(); +} + +static const zend_op *zend_ssa_find_fcall_init_op(const zend_op_array *op_array, const zend_op *opline) +{ + int depth = 1; + + for (opline--; opline >= op_array->opcodes; opline--) { + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth--; + if (depth == 0) { + return opline; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth++; + break; + } + } + + ZEND_UNREACHABLE(); +} + static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */ { const zend_op *next; @@ -680,12 +757,6 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: case ZEND_BIND_INIT_STATIC_OR_JMP: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_REF: - case ZEND_SEND_UNPACK: case ZEND_FE_RESET_RW: case ZEND_MAKE_REF: case ZEND_PRE_INC_OBJ: @@ -703,7 +774,67 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, goto add_op1_def; } break; + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: case ZEND_SEND_VAR: + if (build_flags & ZEND_SSA_CALL_CHAINS) { + // TODO: this is inefficient + const zend_op *init_op = zend_ssa_find_fcall_init_op(op_array, opline); + ssa_ops[k].result_def = ssa_vars_count; + ssa_vars_count++; + if (opline->extended_value == 0) { + if (init_op->opcode != ZEND_NEW) { + const zend_ssa_op *init_ssa_op = &ssa_ops[init_op-op_array->opcodes]; + ssa_ops[k].result_use = init_ssa_op->result_def; + } + } else { + // TODO: this is inefficient + const zend_op *prev_send_op = zend_ssa_find_prev_send_op(op_array, opline); + ssa_ops[k].result_use = ssa_ops[prev_send_op-op_array->opcodes].result_def; + } + } else if (opline->op1_type == IS_CV) { + if (opline->opcode != ZEND_SEND_VAR || (build_flags & ZEND_SSA_RC_INFERENCE)) { + goto add_op1_def; + } + } + break; + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + if (build_flags & ZEND_SSA_CALL_CHAINS) { + ssa_ops[k].result_def = ssa_vars_count; + ssa_vars_count++; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_CALLABLE_CONVERT: + if (build_flags & ZEND_SSA_CALL_CHAINS) { + // TODO: this is inefficient + const zend_op *init_op = zend_ssa_find_fcall_init_op(op_array, opline); + const zend_op *prev_send_op = zend_ssa_find_prev_send_op(op_array, opline); + if (init_op->opcode == ZEND_NEW) { + if (prev_send_op) { + zend_ssa_op *init_ssa_op = &ssa_ops[init_op-op_array->opcodes]; + init_ssa_op->result_use = ssa_ops[prev_send_op-op_array->opcodes].result_def; + } + } else { + if (prev_send_op) { + ssa_ops[k].result_use = ssa_ops[prev_send_op-op_array->opcodes].result_def; + } else { + const zend_ssa_op *init_ssa_op = &ssa_ops[init_op-op_array->opcodes]; + ssa_ops[k].result_use = init_ssa_op->result_def; + } + } + } + break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: @@ -787,7 +918,7 @@ ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *op } /* }}} */ -static zend_result zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */ +static zend_result zend_ssa_rename(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */ { zend_basic_block *blocks = ssa->cfg.blocks; zend_ssa_block *ssa_blocks = ssa->blocks; @@ -891,7 +1022,7 @@ static zend_result zend_ssa_rename(const zend_op_array *op_array, uint32_t build j = blocks[n].children; while (j >= 0) { // FIXME: Tail call optimization? - if (zend_ssa_rename(op_array, build_flags, ssa, var, j) == FAILURE) + if (zend_ssa_rename(arena, op_array, build_flags, ssa, var, j) == FAILURE) return FAILURE; j = blocks[j].next_child; } @@ -1039,7 +1170,7 @@ ZEND_API zend_result zend_build_ssa(zend_arena **arena, const zend_script *scrip var[j] = j; } ssa->vars_count = op_array->last_var; - if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) == FAILURE) { + if (zend_ssa_rename(arena, op_array, build_flags, ssa, var, 0) == FAILURE) { free_alloca(var, var_use_heap); free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; @@ -1091,15 +1222,21 @@ ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_ ssa_vars[op->result_use].use_chain = i; } if (op->op1_def >= 0) { - ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var); + if (op_array->opcodes[i].op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var); + } ssa_vars[op->op1_def].definition = i; } if (op->op2_def >= 0) { - ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var); + if (op_array->opcodes[i].op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var); + } ssa_vars[op->op2_def].definition = i; } if (op->result_def >= 0) { - ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var); + if (op_array->opcodes[i].result_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var); + } ssa_vars[op->result_def].definition = i; } } @@ -1164,7 +1301,10 @@ ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_ } for (i = op_array->last_var; i < ssa->vars_count; i++) { if (ssa_vars[i].var < op_array->last_var) { - ssa_vars[i].alias = ssa_vars[ssa_vars[i].var].alias; + int var = ssa_vars[i].var; + if (var >= 0) { + ssa_vars[i].alias = ssa_vars[var].alias; + } } } } diff --git a/Zend/tests/generics/bounds.phpt b/Zend/tests/generics/bounds.phpt new file mode 100644 index 0000000000000..af8f2390d35f7 --- /dev/null +++ b/Zend/tests/generics/bounds.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bounds +--FILE-- + {} + +var_dump(new C()); +var_dump(new C()); +try { + new C(); +} catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); +} + +?> +--EXPECT-- +object(C)#1 (0) { +} +object(C)#1 (0) { +} +TypeError: Class C: Generic type argument #0 (T) must be of type A, stdClass given diff --git a/Zend/tests/generics/inference.phpt b/Zend/tests/generics/inference.phpt new file mode 100644 index 0000000000000..c354aef9211fa --- /dev/null +++ b/Zend/tests/generics/inference.phpt @@ -0,0 +1,155 @@ +--TEST-- +Generic type inference +--FILE-- + {} +class GenB extends GenA {} +class GenC extends GenA {} +class GenD extends GenA {} + +class NoConstructor {} + +class TypeParamNotInConstructor { + public function __construct() {} +} + +class RequiredTypeParam { + public function __construct(T $t) {} +} + +class OptionalTypeParam { + public function __construct(T $t, U $u = null) {} +} + +class ParamUsedTwice { + public function __construct(T $t, T $u) {} +} + +class ParamInUnion { + public function __construct(T|A $t) {} +} + +class ParamInNullable { + public function __construct(?T $t) {} +} + +class ParamInGenericClass { + public function __construct(GenA $t) {} +} + +function test(A&B $ab, B&C $bc, A $a, B $b) { + var_dump(new RequiredTypeParam("string")); + var_dump(new RequiredTypeParam(1)); + var_dump(new RequiredTypeParam(new stdClass)); + + var_dump(new OptionalTypeParam(1, "string")); + var_dump(new OptionalTypeParam(1, 1)); + var_dump(new OptionalTypeParam(1, new stdClass)); + var_dump(new OptionalTypeParam(1)); + + var_dump(new ParamUsedTwice(1, "string")); + var_dump(new ParamUsedTwice(1, new stdClass)); + var_dump(new ParamUsedTwice(new DateTime, new stdClass)); + var_dump(new ParamUsedTwice($ab, new stdClass)); + var_dump(new ParamUsedTwice($ab, $ab)); + var_dump(new ParamUsedTwice($ab, $bc)); + var_dump(new ParamUsedTwice($ab, $a)); + var_dump(new ParamUsedTwice($a, $ab)); + + var_dump(new ParamInUnion(new stdClass)); + var_dump(new ParamInUnion($a)); + var_dump(new ParamInUnion($ab)); + var_dump(new ParamInUnion(rand() ? $a : $b)); + + var_dump(new ParamInNullable(new stdClass)); + var_dump(new ParamInNullable(rand() ? $a : null)); + var_dump(new ParamInNullable(rand() ? $b : null)); + + var_dump(new ParamInGenericClass(new GenA)); + var_dump(new ParamInGenericClass(new GenB)); + var_dump(new ParamInGenericClass(new GenC)); + var_dump(new ParamInGenericClass(new GenD)); + + try { + var_dump(new NoConstructor()); + } catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); + } + + try { + var_dump(new TypeParamNotInConstructor()); + } catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); + } + + +} + +test( + new class implements A, B {}, + new class implements B, C {}, + new class implements A {}, + new class implements B {}, +); + +?> +--EXPECT-- +object(RequiredTypeParam)#5 (0) { +} +object(RequiredTypeParam)#5 (0) { +} +object(RequiredTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(OptionalTypeParam)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice<(A&B)|stdClass>)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice<(A&B)|(B&C)>)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamUsedTwice)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInUnion)#5 (0) { +} +object(ParamInNullable)#5 (0) { +} +object(ParamInNullable)#5 (0) { +} +object(ParamInNullable)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +object(ParamInGenericClass)#5 (0) { +} +Error: Generic type T can not be inferred because it is not referenced by a constructor parameter or no argument was passed to such parameter +Error: Generic type T can not be inferred because it is not referenced by a constructor parameter or no argument was passed to such parameter diff --git a/Zend/tests/generics/symbolic_inference.inc b/Zend/tests/generics/symbolic_inference.inc new file mode 100644 index 0000000000000..6d5d5c124c3f9 --- /dev/null +++ b/Zend/tests/generics/symbolic_inference.inc @@ -0,0 +1,95 @@ + { + public function __construct(T $t) {} + } + + class D { + const A = 1; + const ?int B = 2; + + public $a; + public int $b; + public stdClass $c; + public C $d; + public ?int $e; + + public function __construct() { + $this->a = null; + $this->b = 1; + $this->c = new stdClass; + $this->d = new C(1); + $this->e = null; + } + + public static function staticMethod(): int { + return 1; + } + + public function method(): string { + return "string"; + } + + public function d(): D { + return $this; + } + } + + function _f($a): int { + return (int) $a; + } + + function _g($a): int { + return (int) $a; + } + + function _int(): int { + return 1; + } + + function _nullable_int(): ?int { + return 1; + } + + function _float(): float { + return 1.5; + } + + function _gmp(): GMP { + return gmp_init(0); + } + + function _array(): array { + return []; + } + + function _string(): string { + return "string"; + } + + function _mixed(): mixed { + return null; + } + + function _mixed_long(): mixed { + return 1; + } + + function _extended_d(): D { + return new class extends D { + function extended(): D { + return new D(); + } + }; + } + +} + +namespace Ns { + const RootAndNsConstant = 1; +} diff --git a/Zend/tests/generics/symbolic_inference.phpt b/Zend/tests/generics/symbolic_inference.phpt new file mode 100644 index 0000000000000..1286874a78520 --- /dev/null +++ b/Zend/tests/generics/symbolic_inference.phpt @@ -0,0 +1,642 @@ +--TEST-- +Symbolic inference +--FILE-- + $d, ?int $e = null) { + var_dump(new C($a)); + var_dump(new C($b)); + var_dump(new C($c)); + var_dump(new C($d)); + var_dump(new C($e)); + }; + $f(1, 1, new stdClass, new C(1), null); + } + + function recv_variadic1() { + $f = function(...$a) { + var_dump(new C($a)); + }; + $f(1,2,3); + } + + function recv_variadic2() { + $f = function(int ...$a) { + var_dump(new C($a)); + }; + $f(1,2,3); + } + + function assign() { + $a = 1; + var_dump(new C($a)); + var_dump(new C($a = "string")); + } + + function assign_dim($i=1) { + $a[0] = 1; + var_dump(new C($a[0])); + var_dump(new C($a[1] = "string")); + @var_dump(new C($a[2]|=1)); + @var_dump(new C($a[3].="string")); + } + + function fetch_dim() { + $a = [1]; + var_dump(new C($a[0])); + } + + class J { + public T $t; + } + class KextendsJ extends J { + } + + function fetch_obj() { + $props = new D(); + var_dump(new C($props->a)); + var_dump(new C($props->b)); + var_dump(new C($props->c)); + var_dump(new C($props->d)); + var_dump(new C($props->e)); + + $props = new J(); + var_dump(new C($props->t ?? null)); + + //$props = new KextendsJ(); + //var_dump(new C($props->t ?? null)); + } + + class E { + const DefinedInE = 'string'; + + function useSelfDefinedInE() { + var_dump(new C(self::DefinedInE)); + } + + function useStaticDefinedInE() { + var_dump(new C(static::DefinedInE)); + } + + function useThisDefinedInE() { + var_dump(new C($this::DefinedInE)); + } + + function useStaticDefinedInFFromE() { + var_dump(new C(static::DefinedInF)); + } + + function useThisDefinedInFFromE() { + var_dump(new C($this::DefinedInF)); + } + + } + + class FextendsE extends E { + const DefinedInF = 1; + + function useSelfDefinedInEFromF() { + var_dump(new C(self::DefinedInE)); + } + + function useParentDefinedInEFromF() { + var_dump(new C(parent::DefinedInE)); + } + + function useStaticDefinedInEFromF() { + var_dump(new C(parent::DefinedInE)); + } + + function useThisDefinedInEFromF() { + var_dump(new C($this::DefinedInE)); + } + + function useSelfDefinedInF() { + var_dump(new C(self::DefinedInF)); + } + + function useStaticDefinedInF() { + var_dump(new C(static::DefinedInF)); + } + + function useThisDefinedInF() { + var_dump(new C($this::DefinedInF)); + } + } + + function fetch_class_constant() { + var_dump(new C(D::A)); + var_dump(new C(D::B)); + + $e = new E(); + foreach (get_class_methods($e) as $method) { + printf("\$e->%s:\n", $method); + try { + [$e, $method](); + } catch (Error $error) { + printf("%s: %s\n", $error::class, $error->getMessage()); + } + } + + $f = new FextendsE(); + foreach (get_class_methods($f) as $method) { + printf("\$f->%s:\n", $method); + try { + [$f, $method](); + } catch (Error $error) { + printf("%s: %s\n", $error::class, $error->getMessage()); + } + } + } + + function fetch_constant1() { + var_dump(new C(RootOnlyConstant)); + var_dump(new C(RootAndNsConstant)); + } + +} + +namespace Ns { + + function fetch_constant() { + var_dump(new \C(RootOnlyConstant)); + var_dump(new \C(RootAndNsConstant)); + } + +} + +namespace { + + function bools(mixed $a = null, mixed $b = null) { + var_dump(new C(!$a)); + var_dump(new C($a && $b)); + var_dump(new C($a || $b)); + var_dump(new C($a xor $b)); + var_dump(new C($a and $b)); + var_dump(new C($a or $b)); + } + + function arithmetic() { + var_dump(new C(_int() + _int())); + var_dump(new C(_array() + _array())); + var_dump(new C(_mixed_long() + _mixed_long())); + + var_dump(new C(_int() * _int())); + + var_dump(new C(_int() ^ _int())); + var_dump(new C(_string() ^ _string())); + + var_dump(new C(_int() % _int())); + } + + function concat() { + var_dump(new C(_string() . _string())); + var_dump(new C(_int() . _int())); + + $a = _string(); + var_dump(new C("string $a")); + } + + function fcall() { + var_dump(new C(_int())); + } + + class G { + public static function f(): string { + return 'string'; + } + public function callSelfF() { + var_dump(new C(self::f())); + } + public function callStaticF() { + var_dump(new C(static::f())); + } + public function callThisF() { + var_dump(new C($this::f())); + } + } + + class H { + public function __construct(public mixed $p) {} + public function f(): T { + return $this->p; + } + public function callSelfF() { + var_dump(new C(self::f())); + } + public function callStaticF() { + var_dump(new C(static::f())); + } + public function callThisF() { + var_dump(new C($this::f())); + } + } + + class IextendsH extends H { + public function callParentF() { + var_dump(new C(parent::f())); + } + } + + function mcall() { + $o = new D(); + var_dump(new C($o->staticMethod())); + var_dump(new C(D::staticMethod())); + var_dump(new C($o->method())); + + $o = _extended_d(); + var_dump(new C($o->extended())); + + $g = new G(); + printf("\$g->callSelfF\n"); + $g->callSelfF(); + printf("\$g->callStaticF\n"); + $g->callStaticF(); + printf("\$g->callThisF\n"); + $g->callThisF(); + + $h = new H('string'); + printf("\$h->callSelfF\n"); + $h->callSelfF(); + printf("\$h->callStaticF\n"); + $h->callStaticF(); + printf("\$h->callThisF\n"); + $h->callThisF(); + + printf("\$h->f\n"); + var_dump(new C($h->f())); + + $i = new IextendsH('string'); + printf("\$i->callParentF\n"); + $i->callParentF(); + } + + function new_() { + var_dump(new C(new D())); + var_dump(new C(new C(1))); + var_dump(new C(new C(1))); + } + + function coalesce() { + var_dump(new C(_nullable_int() ?? _string())); + var_dump(new C(_nullable_int() ?? null)); + } + + function isset_() { + var_dump(new C(isset($a))); + var_dump(new C(empty($a))); + } + + function lambda() { + var_dump(new C(function () {})); + } + + function assign_obj() { + $d = new D(); + $c = new C(1); + var_dump(new C($d->a = 1)); + var_dump(new C($d->b = 1)); + var_dump(new C($d->d = &$c)); + var_dump(new C($d->b += 1)); + } + + function init_array(): void { + $a = [rand(), rand()]; + var_dump(new C($a)); + $a = [...$a]; + var_dump(new C($a)); + } + + function phi(): void { + $a = 1; + if (rand()) { + $a = 'string'; + } + var_dump(new C($a)); + } + + function recursive_fcall($i = 1): void { + $a = "string"; + do { + // _f(...(_f(_f(string|_f(string)))) -> _f(string|_f(string)) + $a = _f($a); + } while ($i--); + var_dump(new C($a)); + } + + function recursive_fcall2($i = 1): void { + $a = "string"; + do { + $a = _f($a); + $a = _g($a); + } while ($i--); + var_dump(new C($a)); + } + + function recursive_arith1($i = 1): void { + $a = "0"; + do { + $a = $a + 1; + } while ($i--); + var_dump(new C($a)); + } + + function recursive_arith2($i = 1): void { + $a = "0"; + do { + $a = _f($a + 1); + } while ($i--); + var_dump(new C($a)); + } + + function recursive_mcall($i = 1): void { + $d = new D(); + do { + $d = $d->d(); + } while ($i--); + var_dump(new C($d)); + } + + // TODO: + // - @ + // - assign_op + + foreach (get_defined_functions()['user'] as $function) { + if (str_starts_with($function, '_')) { + continue; + } + + printf("\n# %s\n", $function); + + $function(); + } + +} + +?> +--EXPECTF-- +# %s +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C>)#4 (0) { +} +object(C)#4 (0) { +} + +# recv_variadic1 +object(C)#3 (0) { +} + +# recv_variadic2 +object(C)#3 (0) { +} + +# assign +object(C)#1 (0) { +} +object(C)#1 (0) { +} + +# assign_dim +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} + +# fetch_dim +object(C)#1 (0) { +} + +# fetch_obj +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C)#4 (0) { +} +object(C>)#4 (0) { +} +object(C)#4 (0) { +} +object(C)#1 (0) { +} + +# fetch_class_constant +object(C)#4 (0) { +} +object(C)#4 (0) { +} +$e->useSelfDefinedInE: +object(C)#1 (0) { +} +$e->useStaticDefinedInE: +object(C)#1 (0) { +} +$e->useThisDefinedInE: +object(C)#1 (0) { +} +$e->useStaticDefinedInFFromE: +Error: Undefined constant E::DefinedInF +$e->useThisDefinedInFFromE: +Error: Undefined constant E::DefinedInF +$f->useSelfDefinedInEFromF: +object(C)#1 (0) { +} +$f->useParentDefinedInEFromF: +object(C)#1 (0) { +} +$f->useStaticDefinedInEFromF: +object(C)#1 (0) { +} +$f->useThisDefinedInEFromF: +object(C)#1 (0) { +} +$f->useSelfDefinedInF: +object(C)#1 (0) { +} +$f->useStaticDefinedInF: +object(C)#1 (0) { +} +$f->useThisDefinedInF: +object(C)#1 (0) { +} +$f->useSelfDefinedInE: +object(C)#1 (0) { +} +$f->useStaticDefinedInE: +object(C)#1 (0) { +} +$f->useThisDefinedInE: +object(C)#1 (0) { +} +$f->useStaticDefinedInFFromE: +object(C)#1 (0) { +} +$f->useThisDefinedInFFromE: +object(C)#1 (0) { +} + +# fetch_constant1 +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# ns\fetch_constant +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# bools +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# arithmetic +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# concat +object(C)#2 (0) { +} +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# fcall +object(C)#2 (0) { +} + +# mcall +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#2 (0) { +} +$g->callSelfF +object(C)#4 (0) { +} +$g->callStaticF +object(C)#4 (0) { +} +$g->callThisF +object(C)#4 (0) { +} +$h->callSelfF +object(C)#7 (0) { +} +$h->callStaticF +object(C)#7 (0) { +} +$h->callThisF +object(C)#7 (0) { +} +$h->f +object(C)#7 (0) { +} +$i->callParentF +object(C)#3 (0) { +} + +# new_ +object(C)#7 (0) { +} +object(C>)#7 (0) { +} +object(C)#7 (0) { +} + +# coalesce +object(C)#7 (0) { +} +object(C)#7 (0) { +} + +# isset_ +object(C)#7 (0) { +} +object(C)#7 (0) { +} + +# lambda +object(C)#7 (0) { +} + +# assign_obj +object(C)#6 (0) { +} +object(C)#6 (0) { +} +object(C>)#6 (0) { +} +object(C)#6 (0) { +} + +# init_array +object(C)#2 (0) { +} +object(C)#2 (0) { +} + +# phi +object(C)#2 (0) { +} + +# recursive_fcall +object(C)#2 (0) { +} + +# recursive_fcall2 +object(C)#2 (0) { +} + +# recursive_arith1 +object(C)#2 (0) { +} + +# recursive_arith2 +object(C)#2 (0) { +} + +# recursive_mcall +object(C)#6 (0) { +} diff --git a/Zend/tests/generics/symbolic_inference_overloading.phpt b/Zend/tests/generics/symbolic_inference_overloading.phpt new file mode 100644 index 0000000000000..312194917ce9f --- /dev/null +++ b/Zend/tests/generics/symbolic_inference_overloading.phpt @@ -0,0 +1,37 @@ +--TEST-- +Symbolic inference with overloading +--EXTENSIONS-- +gmp +--FILE-- + +--EXPECT-- +# arithmetic +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} +object(C)#1 (0) { +} diff --git a/Zend/tests/generics/type_checking.phpt b/Zend/tests/generics/type_checking.phpt new file mode 100644 index 0000000000000..f887543c60b13 --- /dev/null +++ b/Zend/tests/generics/type_checking.phpt @@ -0,0 +1,1173 @@ +--TEST-- +Type checking +--FILE-- + {} +class Base {} +interface I {} +class D extends Base implements I {} + +function f1(C $a) {} +function f2(C $a) {} +function f3(C $a) {} +function f4(C $a) {} +function f5(C $a) {} +function f6(C $a) {} +function f7(C $a) {} +function f8(C $a) {} +function f9(C<(Base&I)|stdClass> $a) {} +function f10(C > $a) {} +function f11(C|int> $a) {} +function f12(array $a) {} +function f13(C $a) {} +function f14(C $a) {} +function f15(C|stdClass $a) {} +function f16(C|null $a) {} + +class Test { + function m1(C $a) {} + function m2(C $a) {} + function m3(C $a) {} + function m4(C $a) {} + function m5(C $a) {} + function m6(C $a) {} +} + +function test(ReflectionFunctionAbstract $rf, object $object = null, string $objectName = null) { + $functionName = $rf->getName(); + if ($object) { + $functionName = sprintf('%s::%s', $objectName, $functionName); + } + $typeName = $rf->getParameters()[0]->getType()->__toString(); + + printf("function %s(%s):\n", $functionName, $typeName); + + $values = [ + 'mixed' => new C(), + 'int' => new C(), + 'string' => new C(), + 'string|int' => new C(), + 'stdClass' => new C(), + 'stdClass|int' => new C(), + 'stdClass|DateTime|int' => new C(), + 'Base' => new C(), + 'D' => new C(), + 'C' => new C >(), + 'C|int' => new C|int>(), + 'array' => new C(), + 'object' => new C(), + ]; + + foreach ($values as $label => $value) { + try { + if ($object) { + $rf->invoke($object, $value); + } else { + $rf->invoke($value); + } + printf(" - C<%s>: accepted\n", $label); + } catch (Error $e) { + printf(" - C<%s>: %s\n", $label, $e->getMessage()); + } + } +} + +$functions = get_defined_functions()['user']; + +foreach ($functions as $function) { + $rf = new ReflectionFunction($function); + if ($rf->getFilename() !== __FILE__ || !str_starts_with($function, 'f')) { + continue; + } + + test($rf); +} + +$methods = (new ReflectionClass(Test::class))->getMethods(); + +$objects = [ + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), + 'Test' => new Test(), +]; + +foreach ($objects as $objectName => $object) { + foreach ($methods as $method) { + test($method, $object, $objectName); + } +} + +?> +--EXPECT-- +function f1(C): + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C>: f1(): Argument #1 ($a) must be of type C, C given + - C|int>: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given + - C: f1(): Argument #1 ($a) must be of type C, C given +function f2(C): + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C>: f2(): Argument #1 ($a) must be of type C, C given + - C|int>: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given + - C: f2(): Argument #1 ($a) must be of type C, C given +function f3(C): + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: accepted + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C>: f3(): Argument #1 ($a) must be of type C, C given + - C|int>: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given + - C: f3(): Argument #1 ($a) must be of type C, C given +function f4(C): + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C>: f4(): Argument #1 ($a) must be of type C, C given + - C|int>: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given + - C: f4(): Argument #1 ($a) must be of type C, C given +function f5(C): + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C>: f5(): Argument #1 ($a) must be of type C, C given + - C|int>: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given + - C: f5(): Argument #1 ($a) must be of type C, C given +function f6(C): + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: accepted + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given + - C>: f6(): Argument #1 ($a) must be of type C, C given + - C|int>: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given + - C: f6(): Argument #1 ($a) must be of type C, C given +function f7(C): + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C>: f7(): Argument #1 ($a) must be of type C, C given + - C|int>: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given + - C: f7(): Argument #1 ($a) must be of type C, C given +function f8(C): + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C>: f8(): Argument #1 ($a) must be of type C, C given + - C|int>: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given + - C: f8(): Argument #1 ($a) must be of type C, C given +function f9(C<(Base&I)|stdClass>): + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: accepted + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: accepted + - C>: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C|int>: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given + - C: f9(): Argument #1 ($a) must be of type C<(Base&I)|stdClass>, C given +function f10(C>): + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C>: accepted + - C|int>: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given + - C: f10(): Argument #1 ($a) must be of type C>, C given +function f11(C|int>): + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: accepted + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C>: f11(): Argument #1 ($a) must be of type C|int>, C given + - C|int>: accepted + - C: f11(): Argument #1 ($a) must be of type C|int>, C given + - C: f11(): Argument #1 ($a) must be of type C|int>, C given +function f12(array): + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C>: f12(): Argument #1 ($a) must be of type array, C given + - C|int>: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given + - C: f12(): Argument #1 ($a) must be of type array, C given +function f13(C): + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C: f13(): Argument #1 ($a) must be of type C, C given + - C>: f13(): Argument #1 ($a) must be of type C, C given + - C|int>: f13(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f13(): Argument #1 ($a) must be of type C, C given +function f14(C): + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: f14(): Argument #1 ($a) must be of type C, C given + - C: f14(): Argument #1 ($a) must be of type C, C given + - C: accepted +function f15(C|stdClass): + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: accepted + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C>: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C|int>: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given + - C: f15(): Argument #1 ($a) must be of type C|stdClass, C given +function f16(?C): + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: accepted + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C>: f16(): Argument #1 ($a) must be of type ?C, C given + - C|int>: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given + - C: f16(): Argument #1 ($a) must be of type ?C, C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = stdClass), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: accepted + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClassU = int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = intU = int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: accepted + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = object), C given + - C: accepted +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = object), C given + - C: accepted +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = objectU = stdClass), C given + - C: accepted +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = object), C given + - C: accepted +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = object), C given + - C: accepted +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted + - C: accepted + - C: accepted + - C>: accepted + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = object), C given + - C: accepted +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = object), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = int|float), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = int|floatU = int|float), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: accepted + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = int|float), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = int|float), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = int|float), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = int|float), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTimeU = stdClass|DateTime), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = stdClass|DateTime|int), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = stdClass|DateTime|intU = stdClass|DateTime|int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = stdClass|DateTime|int), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = stdClass|DateTime|int), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: accepted + - C: accepted + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = stdClass|DateTime|int), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = stdClass|DateTime|int), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: accepted + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base&I), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: accepted + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base&IU = Base&I), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: accepted + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base&I), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: accepted + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base&I), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: accepted + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base&I), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base&I), C given +function Test::m1(C<0>): + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: accepted + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C|int>: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given + - C: Test::m1(): Argument #1 ($a) must be of type C<0> (where T = Base|stdClass), C given +function Test::m2(C<0|1>): + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: accepted + - C: accepted + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: accepted + - C: accepted + - C>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C|int>: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given + - C: Test::m2(): Argument #1 ($a) must be of type C<0|1> (where T = Base|stdClassU = int), C given +function Test::m3(C<0|stdClass>): + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: accepted + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C|int>: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given + - C: Test::m3(): Argument #1 ($a) must be of type C<0|stdClass> (where T = Base|stdClass), C given +function Test::m4(C<0|int>): + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C|int>: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given + - C: Test::m4(): Argument #1 ($a) must be of type C<0|int> (where T = Base|stdClass), C given +function Test::m5(C): + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: accepted + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: accepted + - C: accepted + - C>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C|int>: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given + - C: Test::m5(): Argument #1 ($a) must be of type C (where T = Base|stdClass), C given +function Test::m6(C<0&I>): + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: accepted + - C>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C|int>: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given + - C: Test::m6(): Argument #1 ($a) must be of type C<0&I> (where T = Base|stdClass), C given diff --git a/Zend/tests/generics/variance.phpt b/Zend/tests/generics/variance.phpt new file mode 100644 index 0000000000000..e0ec1f46302ae --- /dev/null +++ b/Zend/tests/generics/variance.phpt @@ -0,0 +1,70 @@ +--TEST-- +Variance +--FILE-- + {} +class Out {} +class In {} + +$objects = [ + 'Invariant' => new Invariant, + 'Invariant' => new Invariant, + 'Out' => new Out, + 'Out' => new Out, + 'In' => new In, + 'In' => new In, +]; + +$functions = [ + Invariant::class => [ + function (Invariant $a) {}, + function (Invariant $b) {}, + ], + Out::class => [ + function (Out $a) {}, + function (Out $b) {}, + ], + In::class => [ + function (In $a) {}, + function (In $b) {}, + ], +]; + +foreach ($objects as $objectName => $object) { + printf("%s:\n", $objectName); + foreach ($functions[$object::class] as $function) { + $rf = new ReflectionFunction($function); + printf(" - function(%s): ", $rf->getParameters()[0]->getType()->__toString()); + try { + $rf->invoke($object); + printf("accepted\n"); + } catch (Error $e) { + printf("%s\n", $e->getMessage()); + } + } +} + +?> +--EXPECT-- +Invariant: + - function(Invariant): accepted + - function(Invariant): {closure}(): Argument #1 ($b) must be of type Invariant, Invariant given +Invariant: + - function(Invariant): {closure}(): Argument #1 ($a) must be of type Invariant, Invariant given + - function(Invariant): accepted +Out: + - function(Out): accepted + - function(Out): {closure}(): Argument #1 ($b) must be of type Out, Out given +Out: + - function(Out): accepted + - function(Out): accepted +In: + - function(In): accepted + - function(In): accepted +In: + - function(In): {closure}(): Argument #1 ($a) must be of type In, In given + - function(In): accepted diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e93eec27279de..e701b1844f240 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -20,6 +20,7 @@ */ #include "zend.h" +#include "zend_compile.h" #include "zend_execute.h" #include "zend_API.h" #include "zend_modules.h" @@ -1778,7 +1779,25 @@ ZEND_API zend_class_reference *zend_build_class_reference(zend_name_reference *n uint8_t param_id = 0; for (; param_id < type_args->num_types; param_id++) { - // TODO: check ce->generic_params[param_id].bound_type + if (ZEND_TYPE_IS_SET(ce->generic_params[param_id].bound_type)) { + if (UNEXPECTED(!zend_type_accepts(&ce->generic_params[param_id].bound_type, + NULL, &type_args->types[param_id], NULL, NULL))) { + zend_string *bound_type_name = zend_type_to_string( + ce->generic_params[param_id].bound_type, ce); + zend_string *actual_type_name = zend_type_to_string( + type_args->types[param_id], ce); + zend_throw_error(zend_ce_type_error, + "Class %s: Generic type argument #%d (%s) must be of type %s, %s given", + ZSTR_VAL(ce->name), param_id, + ZSTR_VAL(ce->generic_params[param_id].name), + ZSTR_VAL(bound_type_name), + ZSTR_VAL(actual_type_name)); + zend_string_release(bound_type_name); + zend_string_release(actual_type_name); + efree(class_ref); + return NULL; + } + } class_ref->args.types[param_id] = type_args->types[param_id]; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a18424dd882de..d0713d0cee2b7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -28,6 +28,8 @@ #include "zend_API.h" #include "zend_exceptions.h" #include "zend_interfaces.h" +#include "zend_operators.h" +#include "zend_portability.h" #include "zend_types.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" @@ -38,6 +40,8 @@ #include "zend_observer.h" #include "zend_call_stack.h" #include "zend_smart_str.h" +#include "zend_symbolic_inference.h" +#include "Optimizer/zend_optimizer.h" // TODO: for revert_pass_two #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -1393,12 +1397,24 @@ static zend_string *add_intersection_type(zend_string *str, zend_string *intersection_str = NULL; ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) { - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); - ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*single_type)); - zend_string *name = ZEND_TYPE_PNR_NAME(*single_type); - zend_string *resolved = resolve_class_name(name, scope, resolve); - intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true); - zend_string_release(resolved); + if (ZEND_TYPE_HAS_PNR(*single_type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(*single_type); + zend_string *resolved = resolve_class_name(name, scope, resolve); + intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true); + zend_string_release(resolved); + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*single_type)); + uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*single_type); + if (scope) { + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + intersection_str = add_type_string(intersection_str, param->name, /* is_intersection */ true); + } else { + zend_string *id_str = zend_long_to_str(generic_param_id); + intersection_str = add_type_string(intersection_str, id_str, /* is_intersection */ true); + zend_string_release(id_str); + } + } } ZEND_TYPE_LIST_FOREACH_END(); ZEND_ASSERT(intersection_str); @@ -1444,9 +1460,15 @@ static zend_string *zend_type_to_string_impl( } else { ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(*list_type)); uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(*list_type); - generic_param_id -= scope->num_bound_generic_args; - zend_generic_param *param = &scope->generic_params[generic_param_id]; - str = add_type_string(str, param->name, /* is_intersection */ false); + if (scope) { + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + str = add_type_string(str, param->name, /* is_intersection */ false); + } else { + zend_string *id_str = zend_long_to_str(generic_param_id); + str = add_type_string(str, id_str, /* is_intersection */ false); + zend_string_release(id_str); + } } } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_PNR(type)) { @@ -1458,9 +1480,15 @@ static zend_string *zend_type_to_string_impl( str = zend_class_ref_to_string(ce_ref); } else if (ZEND_TYPE_HAS_GENERIC_PARAM(type)) { uint32_t generic_param_id = ZEND_TYPE_GENERIC_PARAM_ID(type); - generic_param_id -= scope->num_bound_generic_args; - zend_generic_param *param = &scope->generic_params[generic_param_id]; - str = zend_string_copy(param->name); + if (scope) { + generic_param_id -= scope->num_bound_generic_args; + zend_generic_param *param = &scope->generic_params[generic_param_id]; + str = zend_string_copy(param->name); + } else { + zend_string *id_str = zend_long_to_str(generic_param_id); + str = add_type_string(str, id_str, /* is_intersection */ false); + zend_string_release(id_str); + } } uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); @@ -1890,6 +1918,59 @@ static zend_string *zend_resolve_const_class_name_reference(zend_ast *class_ast, static zend_type zend_compile_typename(zend_ast *ast); +static zend_always_inline void zend_name_reference_key_start(smart_str *key, zend_string *class_name) +{ + zend_string *lcname = zend_string_tolower(class_name); + smart_str_append(key, lcname); + zend_string_release(lcname); + + smart_str_appendc(key, '<'); +} + +static zend_always_inline void zend_name_reference_key_start_lc(smart_str *key, zend_string *lcname) +{ + smart_str_append(key, lcname); + smart_str_appendc(key, '<'); +} + +static zend_always_inline void zend_name_reference_key_add_type(smart_str *key, uint32_t nth_type, zend_type type) +{ + if (nth_type != 0) { + smart_str_appendc(key, ','); + } + + zend_type_to_key_impl(key, type); +} + +static zend_always_inline void zend_name_reference_key_end(smart_str *key) +{ + smart_str_appendc(key, '>'); +} + +void zend_compile_name_reference_key(zend_name_reference *ref, zend_string *lcname) { + if (ref->args.num_types == 0) { + if (lcname) { + ref->key = zend_new_interned_string(lcname); + } else { + lcname = zend_string_tolower(ref->name); + ref->key = zend_new_interned_string(lcname); + zend_string_release(lcname); + } + } else { + smart_str key = {0}; + if (lcname) { + zend_name_reference_key_start_lc(&key, lcname); + } else { + zend_name_reference_key_start(&key, ref->name); + } + for (uint32_t i = 0; i < ref->args.num_types; i++) { + zend_name_reference_key_add_type(&key, i, ref->args.types[i]); + } + zend_name_reference_key_end(&key); + ref->key = zend_new_interned_string(smart_str_extract(&key)); + } +} + static zend_name_reference *zend_compile_name_reference( zend_string *name, zend_ast *args_ast) { zend_ast_list *list = args_ast ? zend_ast_get_list(args_ast) : NULL; @@ -1902,19 +1983,13 @@ static zend_name_reference *zend_compile_name_reference( } else { ZEND_ASSERT(list); smart_str key = {0}; - zend_string *lcname = zend_string_tolower(name); - smart_str_append(&key, lcname); - zend_string_release(lcname); - smart_str_appendc(&key, '<'); + zend_name_reference_key_start(&key, name); for (uint32_t i = 0; i < num_types; i++) { - if (i != 0) { - smart_str_appendc(&key, ','); - } zend_ast *type_ast = list->child[i]; ref->args.types[i] = zend_compile_typename(type_ast); - zend_type_to_key_impl(&key, ref->args.types[i]); + zend_name_reference_key_add_type(&key, i, ref->args.types[i]); } - smart_str_appendc(&key, '>'); + zend_name_reference_key_end(&key); ref->key = zend_new_interned_string(smart_str_extract(&key)); } ref->args.num_types = num_types; @@ -5145,11 +5220,20 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ if (class_node.op_type == IS_CONST) { opline->op1_type = IS_CONST; opline->op1.constant = zend_add_pnr_literal(Z_PNR(class_node.u.constant)); + + if (ZEND_PNR_IS_SIMPLE(Z_PNR(class_node.u.constant))) { + /* literal placeholder for type inference */ + zval zv; + ZVAL_NULL(&zv); + zend_add_literal(&zv); + } + opline->op2.num = zend_alloc_cache_slot(); } else { SET_NODE(opline->op1, &class_node); } + zend_compile_call_common(&ctor_result, args_ast, NULL, ast->lineno); zend_do_free(&ctor_result); } @@ -6975,7 +7059,6 @@ static zend_type zend_compile_typename_ex( type = single_type; ZEND_TYPE_FULL_MASK(type) |= extra_type_mask; } else { - ZEND_ASSERT(!ZEND_TYPE_HAS_GENERIC_PARAM(single_type) && "Not implemented"); if (type_list->num_types == 0) { /* Switch from single name to name list. */ @@ -7037,15 +7120,16 @@ static zend_type zend_compile_typename_ex( zend_string_release_ex(standard_type_str, false); } /* An intersection of standard types cannot exist so invalidate it */ - if (ZEND_TYPE_IS_ONLY_MASK(single_type)) { + if (!ZEND_TYPE_IS_COMPLEX(single_type)) { zend_string *standard_type_str = zend_type_to_string(single_type, CG(active_class_entry)); zend_error_noreturn(E_COMPILE_ERROR, "Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str)); zend_string_release_ex(standard_type_str, false); } /* Check for "self" and "parent" too */ - if (zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "self") - || zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "parent")) { + if (ZEND_TYPE_HAS_PNR(single_type) + && (zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "self") + || zend_string_equals_literal_ci(ZEND_TYPE_PNR_NAME(single_type), "parent"))) { zend_error_noreturn(E_COMPILE_ERROR, "Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_PNR_NAME(single_type))); } @@ -8004,6 +8088,11 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) zend_emit_final_return(0); pass_two(CG(active_op_array)); + + zend_revert_pass_two(op_array); /* TODO */ + zend_symbolic_inference(op_array, &CG(arena)); + zend_redo_pass_two(op_array); + zend_oparray_context_end(&orig_oparray_context); /* Pop the loop variable stack separator */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 475c38721f128..a1228852770af 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -870,6 +870,8 @@ ZEND_API void zend_type_release(zend_type type, bool persistent); ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name); ZEND_API void zend_pnr_destroy(zend_packed_name_reference pnr); +void zend_compile_name_reference_key(zend_name_reference *ref, zend_string *lcname); +void zend_name_reference_release(zend_name_reference *name_ref, bool uses_arena, bool persistent); void zend_packed_name_reference_release(zend_packed_name_reference ref, bool persistent); ZEND_API ZEND_COLD void zend_user_exception_handler(void); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e9a1795641ea2..e659e2ccc9860 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -18,8 +18,6 @@ +----------------------------------------------------------------------+ */ -#include "zend_portability.h" -#include "zend_types.h" #define ZEND_INTENSIVE_DEBUGGING 0 #include @@ -47,6 +45,10 @@ #include "zend_call_stack.h" #include "Optimizer/zend_func_info.h" #include "zend_bitset.h" +#include "zend_portability.h" +#include "zend_types.h" +#include "zend_type_tools.h" +#include "zend_symbolic_inference.h" /* Virtual current working directory support */ #include "zend_virtual_cwd.h" @@ -983,6 +985,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons } } +#if 0 static zend_always_inline const zend_class_entry *zend_ce_from_type( const zend_class_entry *scope, const zend_type *type) { ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*type)); @@ -999,6 +1002,7 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type( } return resolve_single_class_type(name, scope); } +#endif static bool zend_check_intersection_for_property_or_class_constant_class_type( const zend_class_entry *scope, zend_type_list *intersection_type_list, const zend_class_entry *value_ce) @@ -1099,10 +1103,19 @@ static zend_never_inline bool check_property_type_generic( } #endif -static zend_always_inline zend_type zend_resolve_generic_type(zend_class_reference *scope, uint32_t param_id) { +static zend_always_inline zend_type zend_resolve_generic_type( + const zend_class_reference *scope, uint32_t param_id) { + if (!scope) { + zend_error_noreturn(E_CORE_ERROR, + "Unexpectedly tried to resolve a type parameter without a scope (todo/bug)"); + } if (param_id >= scope->ce->num_bound_generic_args) { - ZEND_ASSERT(param_id - scope->ce->num_bound_generic_args < scope->args.num_types); - return scope->args.types[param_id - scope->ce->num_bound_generic_args]; + if (param_id - scope->ce->num_bound_generic_args < scope->args.num_types) { + return scope->args.types[param_id - scope->ce->num_bound_generic_args]; + } else { + zend_error_noreturn(E_CORE_ERROR, + "Scope does not bind parameter %d (todo/bug)", param_id); + } } else { return scope->ce->bound_generic_args[param_id]; } @@ -1226,11 +1239,13 @@ static zend_always_inline bool zend_validate_generic_args( // TODO: cache_slot may not be worth it since ZSTR_CE_CACHE #undef HAVE_CACHE_SLOT #define HAVE_CACHE_SLOT 0 +#define PROGRESS_CACHE_SLOT_EX(_) +#define ZEND_TYPE_NUM_NAMES(_) #define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;} static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( - void **cache_slot, zend_type *type) + void **cache_slot, const zend_type *type) { if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) { return (zend_class_entry *) *cache_slot; @@ -1263,6 +1278,240 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( return ce; } +static zend_always_inline zend_class_reference *zend_fetch_cr_from_cache_slot( + void **cache_slot, const zend_type *type) +{ + if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) { + return (zend_class_reference *) *cache_slot; + } + + zend_class_reference *class_ref; + zend_packed_name_reference pnr = ZEND_TYPE_PNR(*type); + zend_string *key = ZEND_PNR_IS_SIMPLE(pnr) + ? ZEND_PNR_SIMPLE_GET_NAME(pnr) + : ZEND_PNR_COMPLEX_GET_KEY(pnr); + + if (ZSTR_HAS_CE_CACHE(key)) { + class_ref = ZSTR_GET_CE_CACHE(key); + if (!class_ref) { + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_class_entry *ce = zend_lookup_class_ex( + ZEND_PNR_SIMPLE_GET_NAME(pnr), NULL, + ZEND_FETCH_CLASS_NO_AUTOLOAD); + class_ref = ce ? ZEND_CE_TO_REF(ce) : NULL; + } else { + class_ref = zend_lookup_generic_class( + ZEND_PNR_COMPLEX_GET_REF(pnr), NULL, + ZEND_FETCH_CLASS_NO_AUTOLOAD); + } + if (UNEXPECTED(!class_ref)) { + /* Cannot resolve */ + return NULL; + } + } + } else { + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_class_entry *ce = zend_lookup_class_ex( + ZEND_PNR_SIMPLE_GET_NAME(pnr), NULL, + ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); + class_ref = ce ? ZEND_CE_TO_REF(ce) : NULL; + } else { + class_ref = zend_lookup_generic_class( + ZEND_PNR_COMPLEX_GET_REF(pnr), NULL, + ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); + } + + if (UNEXPECTED(!class_ref)) { + return NULL; + } + } + + if (HAVE_CACHE_SLOT) { + *cache_slot = (void *) class_ref; + } + + return class_ref; +} + +static bool zend_type_accepts_slow( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot); + +ZEND_API bool zend_type_accepts( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot) +{ + uint32_t accepted_mask = ZEND_TYPE_PURE_MASK(*accepted_type); + uint32_t instance_mask = (ZEND_TYPE_FULL_MASK(*instance_type) + & (_ZEND_TYPE_MAY_BE_MASK | (_ZEND_TYPE_COMPLEX_MASK&~_ZEND_TYPE_PNR_BIT))) + | (ZEND_TYPE_HAS_PNR(*instance_type) ? MAY_BE_OBJECT : 0); + + if (accepted_mask && (instance_mask & ~accepted_mask) == 0) { + return true; + } + + return zend_type_accepts_slow(accepted_type, accepted_scope, instance_type, + instance_scope, cache_slot); +} + +static bool zend_type_accepts_slow( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot) +{ + if (ZEND_TYPE_HAS_GENERIC_PARAM(*instance_type)) { + zend_type tmp = zend_resolve_generic_type(instance_scope, + ZEND_TYPE_GENERIC_PARAM_ID(*instance_type)); + return zend_type_accepts(accepted_type, accepted_scope, + &tmp, instance_scope, cache_slot); + } + + if (ZEND_TYPE_HAS_PNR(*accepted_type)) { + if (ZEND_TYPE_PURE_MASK(*instance_type) & ~ZEND_TYPE_PURE_MASK(*accepted_type)) { + return false; + } + + zend_class_reference *accepted_ref = zend_fetch_cr_from_cache_slot(cache_slot, accepted_type); + if (!accepted_ref) { + return false; // TODO? + } + + if (ZEND_TYPE_HAS_PNR(*instance_type)) { + // TODO: type name may not have a cache ptr if it does not appear in + // a type. + zend_class_reference *instance_ref = zend_fetch_cr_from_cache_slot(NULL, instance_type); + if (!instance_ref) { + return false; // TODO? + } + return instanceof_ref(instance_ref, instance_scope, + accepted_ref, accepted_scope); + } else if (ZEND_TYPE_IS_INTERSECTION(*instance_type)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*instance_type), single_type) { + if (zend_type_accepts(accepted_type, accepted_scope, + single_type, instance_scope, NULL)) { + return true; + } + } ZEND_TYPE_LIST_FOREACH_END(); + return false; + } else if (ZEND_TYPE_IS_UNION(*instance_type)) { + zend_type *instance_item; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*instance_type), instance_item) { + if (!zend_type_accepts(accepted_type, accepted_scope, + instance_item, instance_scope, NULL)) { + return false; + } + } ZEND_TYPE_LIST_FOREACH_END(); + + return true; + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(*instance_type)); + return false; + } + } else if (ZEND_TYPE_IS_INTERSECTION(*accepted_type)) { + if (ZEND_TYPE_PURE_MASK(*instance_type) & ~ZEND_TYPE_PURE_MASK(*accepted_type)) { + return false; + } + + zend_type *accepted_item; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*accepted_type), accepted_item) { + if (!zend_type_accepts(accepted_item, accepted_scope, + instance_type, instance_scope, cache_slot)) { + return false; + } + PROGRESS_CACHE_SLOT_EX(ZEND_TYPE_NUM_NAMES(*accepted_item)); + } ZEND_TYPE_LIST_FOREACH_END(); + return true; + } else if (ZEND_TYPE_IS_UNION(*accepted_type)) { + if (ZEND_TYPE_IS_UNION(*instance_type)) { + zend_type *instance_item; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*instance_type), instance_item) { + if (!zend_type_accepts_slow(accepted_type, accepted_scope, + instance_item, instance_scope, cache_slot)) { + return false; + } + /* Do not progress cache slot, as we are iterating instance_type */ + } ZEND_TYPE_LIST_FOREACH_END(); + return (ZEND_TYPE_PURE_MASK(*instance_type) & ~ZEND_TYPE_PURE_MASK(*accepted_type)) == 0; + } else { + zend_type *accepted_item; + zend_type tmp = *instance_type; + bool result; + if (ZEND_TYPE_IS_COMPLEX(*instance_type)) { + result = false; + ZEND_TYPE_FULL_MASK(tmp) &= ~ZEND_TYPE_PURE_MASK(tmp); + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*accepted_type), accepted_item) { + if (zend_type_accepts(accepted_item, accepted_scope, + &tmp, instance_scope, cache_slot)) { + result = true; + break; + } + PROGRESS_CACHE_SLOT_EX(ZEND_TYPE_NUM_NAMES(*accepted_item)); + } ZEND_TYPE_LIST_FOREACH_END(); + if (!result) { + return false; + } + } + uint32_t mask = ZEND_TYPE_PURE_MASK(*instance_type); + if ((mask & ~ZEND_TYPE_PURE_MASK(*accepted_type)) == 0) { + return true; + } + if (mask) { + for (uint32_t i = (1<ce, &class_ref->args) && + instanceof_ref(Z_OBJCR_P(arg), Z_OBJCR_P(arg), + class_ref, zend_get_called_scope(EG(current_execute_data)))) { return true; } } @@ -1367,15 +1619,12 @@ static zend_always_inline bool zend_check_type_slow( } ZEND_TYPE_LIST_FOREACH_END(); } } else if (ZEND_TYPE_HAS_PNR(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { - ce = zend_fetch_ce_from_cache_slot(cache_slot, type); - zend_string *name; - const zend_type_list *args; - ZEND_PNR_UNPACK(ZEND_TYPE_PNR(*type), name, args); - (void) name; - /* If we have a CE we check if it satisfies the type constraint, + zend_class_reference *class_ref = zend_fetch_cr_from_cache_slot(cache_slot, type); + /* If we have a CR we check if it satisfies the type constraint, * otherwise it will check if a standard type satisfies it. */ - if (ce && zend_validate_generic_args(ce, args) - && instanceof_unpacked(Z_OBJCR_P(arg), ce, args)) { + if (class_ref && zend_validate_generic_args(class_ref->ce, &class_ref->args) + && instanceof_ref(Z_OBJCR_P(arg), Z_OBJCR_P(arg), class_ref, + zend_get_called_scope(EG(current_execute_data)))) { return true; } } else if (UNEXPECTED(ZEND_TYPE_HAS_GENERIC_PARAM(*type))) { @@ -5777,3 +6026,199 @@ ZEND_API zval *zend_get_zval_ptr(const zend_op *opline, int op_type, const znode } return ret; } + +zend_type zend_materialize_generic_types(zend_arena **arena, zend_type t, zend_class_reference *scope) { + if (!ZEND_TYPE_IS_COMPLEX(t)) { + return t; + } + if (ZEND_TYPE_HAS_LIST(t)) { + zend_type_list *list = ZEND_TYPE_LIST(t); + zend_type_list *new_list = NULL; + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(list, single_type) { + zend_type tmp = zend_materialize_generic_types(arena, *single_type, scope); + if (tmp.type_mask != single_type->type_mask + || (ZEND_TYPE_IS_COMPLEX(tmp) && tmp.ptr != single_type->ptr)) { + if (!new_list) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types)); + ZEND_TYPE_SET_PTR(t, new_list); + } + new_list->types[single_type-list->types] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + if (ZEND_TYPE_IS_UNION(t)) { + t = zend_type_normalize_union_in_place(t); + } else { + t = zend_type_normalize_intersection_in_place(t); + } + } else if (ZEND_TYPE_HAS_COMPLEX_PNR(t)) { + zend_name_reference *name_ref = ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(t)); + zend_name_reference *new_ref = NULL; + for (uint32_t i = 0; i < name_ref->args.num_types; i++) { + zend_type *single_type = &name_ref->args.types[i]; + zend_type tmp = zend_materialize_generic_types(arena, *single_type, scope); + if (tmp.type_mask != single_type->type_mask + || (ZEND_TYPE_IS_COMPLEX(tmp) && tmp.ptr != single_type->ptr)) { + if (!new_ref) { + new_ref = zend_arena_alloc(arena, ZEND_CLASS_REF_SIZE(name_ref->args.num_types)); + memcpy(new_ref, name_ref, ZEND_CLASS_REF_SIZE(name_ref->args.num_types)); + } + new_ref->args.types[i] = tmp; + } + } + if (new_ref) { + ZEND_TYPE_SET_PNR(t, ZEND_PNR_ENCODE_REF(new_ref)); + zend_compile_name_reference_key(new_ref, NULL); + } + } else if (ZEND_TYPE_HAS_GENERIC_PARAM(t)) { + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(t); + return zend_resolve_generic_type(scope, param_id); + } + + return t; +} + +void zend_infer_types(zend_arena **arena, zend_type_list *infered_types, + zend_type param_type, zend_type arg_type, zend_class_reference *scope) { + if (!ZEND_TYPE_IS_COMPLEX(param_type)) { + return; + } + + if (ZEND_TYPE_IS_UNION(param_type)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param_type), single_type) { + // TODO: semantics: might be desirable that T|null -> null not in T + zend_infer_types(arena, infered_types, *single_type, arg_type, + scope); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_IS_INTERSECTION(param_type)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param_type), single_type) { + zend_infer_types(arena, infered_types, *single_type, arg_type, + scope); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_PNR(param_type)) { + if (ZEND_TYPE_HAS_COMPLEX_PNR(param_type)) { + zend_class_reference *param_ref = zend_fetch_cr_from_cache_slot(NULL, ¶m_type); + if (!param_ref) { + return; + } + // TODO + if (ZEND_TYPE_HAS_PNR(arg_type)) { + zend_class_reference *arg_ref = zend_fetch_cr_from_cache_slot(NULL, &arg_type); + if (!arg_ref) { + return; + } + if (arg_ref->ce == param_ref->ce) { + for (uint32_t j = 0; j < param_ref->args.num_types; j++) { + // TODO + if (j < arg_ref->args.num_types) { + zend_infer_types(arena, infered_types, + param_ref->args.types[j], + arg_ref->args.types[j], arg_ref); + } + } + return; + } + + for (uint32_t i = 0; i < arg_ref->ce->num_parents; i++) { + zend_class_reference *ref = arg_ref->ce->parents[i]; + if (ref->ce == param_ref->ce) { + for (uint32_t j = 0; j < param_ref->args.num_types; j++) { + // TODO + if (j < ref->args.num_types) { + zend_infer_types(arena, infered_types, + param_ref->args.types[j], + ref->args.types[j], + arg_ref); + } + } + return; + } + } + } + } + } else { + ZEND_ASSERT(ZEND_TYPE_HAS_GENERIC_PARAM(param_type)); + uint32_t param_id = ZEND_TYPE_GENERIC_PARAM_ID(param_type); + ZEND_ASSERT(param_id <= infered_types->num_types); + infered_types->types[param_id] = zend_type_union( + &CG(arena), infered_types->types[param_id], + zend_materialize_generic_types(arena, arg_type, scope)); + } +} + +zend_type zend_value_type(zval *value) { + if (Z_TYPE_P(value) == IS_OBJECT) { + zend_class_reference *class_ref = Z_OBJ_P(value)->cr; + if (class_ref->args.num_types == 0) { + return (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(class_ref->ce->name), 0, 0); + } + zend_name_reference *name_ref = emalloc(ZEND_CLASS_REF_SIZE(class_ref->args.num_types)); + name_ref->name = class_ref->ce->name; + name_ref->key = class_ref->key; + memcpy(&name_ref->args, &class_ref->args, ZEND_TYPE_LIST_SIZE(class_ref->args.num_types)); + return (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_REF(name_ref), 0, 0); + } + + if (Z_TYPE_P(value) & (IS_TRUE|IS_FALSE)) { + return (zend_type) ZEND_TYPE_INIT_MASK(1 << (IS_TRUE|IS_FALSE)); + } + + if (UNEXPECTED(Z_TYPE_P(value) >= IS_REFERENCE)) { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + return (zend_type) ZEND_TYPE_INIT_MASK(1 << Z_TYPE_P(value)); +} + +void zend_infer_instantiation_types(zend_type_list *dest, zend_class_entry *ce, + zend_type_list *args, zend_class_reference *scope) { + if (UNEXPECTED(ce->num_generic_params == 0)) { + return; + } + + zend_function* constructor = ce->constructor; + if (EXPECTED(constructor)) { + ZEND_ASSERT(dest->num_types == ce->num_generic_params); + zend_arg_info *arg_infos = constructor->common.arg_info; + uint32_t num_args = constructor->common.num_args; + + for (uint32_t i = 0, l = MIN(num_args, args->num_types); i < l; i++) { + zend_arg_info *arg_info = &arg_infos[i]; + zend_type t = zend_resolve_symbolic_type(&CG(arena), args->types[i]); + zend_infer_types(&CG(arena), dest, arg_info->type, t, scope); + } + } + + for (uint32_t i = 0; i < ce->num_generic_params; i++) { + if (EXPECTED(ZEND_TYPE_IS_SET(dest->types[i]))) { + continue; + } + if (EXPECTED(i >= ce->num_required_generic_params)) { + dest->types[i] = ce->generic_params[i].default_type; + } else { + // TODO: Check if the cause is always true and check this at compile time + zend_throw_error(NULL, "Generic type %s can not be inferred because it is not referenced by a constructor parameter or no argument was passed to such parameter", + ZSTR_VAL(ce->generic_params->name)); + return; + } + } +} + +zend_name_reference *zend_infer_instantiation_name_reference( + zend_class_entry *ce, zend_string *lcname, zend_type_list *args, + zend_class_reference *scope) { + zend_name_reference *ref = ecalloc(1, ZEND_CLASS_REF_SIZE(ce->num_generic_params)); + + ref->args.num_types = ce->num_generic_params; + zend_infer_instantiation_types(&ref->args, ce, args, scope); + + ref->name = ce->name; + zend_string_addref(ref->name); + + zend_compile_name_reference_key(ref, lcname); + + return ref; +} diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 40e51ba392302..612e81e0a772d 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -51,6 +51,8 @@ ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_val ZEND_API bool zend_is_valid_class_name(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags); +ZEND_API zend_class_reference *zend_lookup_generic_class(zend_name_reference *name_ref, zend_string *base_key, uint32_t flags); +ZEND_API zend_class_reference *zend_lookup_class_by_pnr(zend_packed_name_reference pnr, zend_string *base_key, uint32_t flags); ZEND_API zend_class_reference *zend_get_called_scope(zend_execute_data *ex); ZEND_API zend_object *zend_get_this_object(zend_execute_data *ex); ZEND_API zend_result zend_eval_string(const char *str, zval *retval_ptr, const char *string_name); @@ -103,6 +105,10 @@ ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope, bool is_return_type); +ZEND_API bool zend_type_accepts( + const zend_type *accepted_type, const zend_class_reference *accepted_scope, + const zend_type *instance_type, const zend_class_reference *instance_scope, + void **cache_slot); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call); @@ -532,6 +538,10 @@ static zend_always_inline void *zend_get_bad_ptr(void) return NULL; } +zend_name_reference *zend_infer_instantiation_name_reference( + zend_class_entry *ce, zend_string *lcname, zend_type_list *args, + zend_class_reference *scope); + END_EXTERN_C() #endif /* ZEND_EXECUTE_H */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index c6bb05a293ce9..a5dfa957d1ece 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1291,6 +1291,21 @@ ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ } /* }}} */ +ZEND_API zend_class_reference *zend_lookup_class_by_pnr(zend_packed_name_reference pnr, zend_string *base_key, uint32_t flags) /* {{{ */ +{ + if (ZEND_PNR_IS_SIMPLE(pnr)) { + zend_class_entry *ce = zend_lookup_class_ex( + ZEND_PNR_SIMPLE_GET_NAME(pnr), base_key, flags); + if (!ce) { + return NULL; + } + return ZEND_CE_TO_REF(ce); + } + + return zend_lookup_generic_class(ZEND_PNR_COMPLEX_GET_REF(pnr), base_key, flags); +} +/* }}} */ + ZEND_API zend_class_reference *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ { while (ex) { diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index bc015a540e7bb..bc1f2368f1132 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -32,6 +32,7 @@ #include "zend_observer.h" #include "zend_string.h" #include "zend_types.h" +#include "zend_type_tools.h" ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL; @@ -62,39 +63,6 @@ static void ZEND_COLD emit_incompatible_method_error( const zend_function *parent, zend_class_entry *parent_scope, inheritance_status status); -static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); - -static void zend_type_list_copy_ctor( - zend_type *const parent_type, - bool use_arena, - bool persistent -) { - const zend_type_list *const old_list = ZEND_TYPE_LIST(*parent_type); - size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); - zend_type_list *new_list = use_arena - ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); - - memcpy(new_list, old_list, size); - ZEND_TYPE_SET_LIST(*parent_type, new_list); - if (use_arena) { - ZEND_TYPE_FULL_MASK(*parent_type) |= _ZEND_TYPE_ARENA_BIT; - } - - zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(new_list, list_type) { - zend_type_copy_ctor(list_type, use_arena, persistent); - } ZEND_TYPE_LIST_FOREACH_END(); -} - -static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent) { - if (ZEND_TYPE_HAS_LIST(*type)) { - zend_type_list_copy_ctor(type, use_arena, persistent); - } else if (ZEND_TYPE_HAS_PNR(*type)) { - // TODO: Duplicate name reference... - zend_string_addref(ZEND_TYPE_PNR_NAME(*type)); - } -} - static zend_function *zend_duplicate_internal_function(zend_function *func, zend_class_entry *ce) /* {{{ */ { zend_function *new_function; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 054ed7bdc1ef6..3d5c14a00e9e2 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -46,6 +46,8 @@ #include "zend_strtod.h" #include "zend_exceptions.h" #include "zend_virtual_cwd.h" +#include "zend_symbolic_inference.h" +#include "Optimizer/zend_optimizer.h" /* TODO: for zend_revert_pass_two */ #define YYCTYPE unsigned char #define YYFILL(n) { if ((YYCURSOR + n) >= (YYLIMIT + ZEND_MMAP_AHEAD)) { return 0; } } @@ -621,6 +623,11 @@ static zend_op_array *zend_compile(int type) op_array->line_start = 1; op_array->line_end = last_lineno; pass_two(op_array); + + zend_revert_pass_two(op_array); /* TODO */ + zend_symbolic_inference(op_array, &CG(arena)); + zend_redo_pass_two(op_array); + zend_oparray_context_end(&original_oparray_context); zend_file_context_end(&original_file_context); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index f7401a27831a3..3789634b0b173 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -116,9 +116,7 @@ void zend_name_reference_release( zend_type *arg_type; zend_string_release(name_ref->name); ZEND_TYPE_LIST_FOREACH(&name_ref->args, arg_type) { - if (ZEND_TYPE_HAS_PNR(*arg_type)) { - zend_pnr_release(ZEND_TYPE_PNR(*arg_type), uses_arena, persistent); - } + zend_type_release(*arg_type, persistent); } ZEND_TYPE_LIST_FOREACH_END(); if (!uses_arena) { pefree(name_ref, persistent); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index f5c4a8530fdad..028756dc127d2 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -21,7 +21,11 @@ #include #include "zend.h" +#include "zend_execute.h" +#include "zend_extensions.h" #include "zend_operators.h" +#include "zend_portability.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_globals.h" #include "zend_list.h" @@ -2490,40 +2494,62 @@ ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *ins /* }}} */ static zend_always_inline bool zend_type_lists_compatible( - const zend_type_list *list1, const zend_type_list *list2, const zend_class_entry *ce) { + const zend_type_list *list1, + const zend_class_reference *list1_scope, + const zend_type_list *list2, + const zend_class_reference *list2_scope, + const zend_class_entry *ce) { /* list1 is complete, while list2 may have defaulted arguments. */ uint32_t i = 0; - for (; i < list2->num_types; i++) { - const zend_type *type1 = &list1->types[i]; - const zend_type *type2 = &list2->types[i]; - // TODO: Implement proper type comparison. - if (type1->type_mask != type2->type_mask || type1->ptr != type2->ptr) { - return 0; - } - } for (; i < list1->num_types; i++) { const zend_type *type1 = &list1->types[i]; - const zend_type *type2 = &ce->generic_params[i].default_type; - // TODO: Implement proper type comparison. - if (type1->type_mask != type2->type_mask || type1->ptr != type2->ptr) { - return 0; + const zend_type *type2; + if (i < list2->num_types) { + type2 = &list2->types[i]; + } else { + type2 = &ce->generic_params[i].default_type; + } + switch (ce->generic_params[i].variance) { + case ZEND_GENERIC_PARAM_OUT: + if (!zend_type_accepts(type2, list2_scope, type1, list1_scope, NULL)) { + return 0; + } + break; + case ZEND_GENERIC_PARAM_IN: + if (!zend_type_accepts(type1, list1_scope, type2, list2_scope, NULL)) { + return 0; + } + break; + case ZEND_GENERIC_PARAM_INVARIANT: + // TODO: variance-aware zend_types_compatible() + if (!zend_type_accepts(type1, list1_scope, type2, list2_scope, NULL) + || !zend_type_accepts(type2, list2_scope, type1, list1_scope, NULL)) { + return 0; + } + break; + EMPTY_SWITCH_DEFAULT_CASE(); } } + return 1; } -ZEND_API bool ZEND_FASTCALL instanceof_unpacked_slow( - const zend_class_reference *instance_ref, const zend_class_entry *ce, const zend_type_list *args) { +ZEND_API bool ZEND_FASTCALL instanceof_ref_slow( + const zend_class_reference *instance_ref, + const zend_class_reference *instance_scope, + const zend_class_reference *ref, + const zend_class_reference *ref_scope) { zend_class_entry *instance_ce = instance_ref->ce; + zend_class_entry *ce = ref->ce; if (ce->ce_flags & ZEND_ACC_INTERFACE) { return instanceof_function(instance_ce, ce); } else { - if (instance_ce == ce && zend_type_lists_compatible(&instance_ref->args, args, ce)) { + if (instance_ce == ce && zend_type_lists_compatible(&instance_ref->args, instance_scope, &ref->args, ref_scope, ce)) { return 1; } for (uint32_t i = 0; i < instance_ce->num_parents; i++) { zend_class_reference *parent_ref = instance_ce->parents[i]; - if (parent_ref->ce == ce && zend_type_lists_compatible(&parent_ref->args, args, ce)) { + if (parent_ref->ce == ce && zend_type_lists_compatible(&parent_ref->args, instance_scope, &ref->args, ref_scope, ce)) { return 1; } } diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 3874c49ad9ac0..e1f13e54c2262 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -21,6 +21,7 @@ #ifndef ZEND_OPERATORS_H #define ZEND_OPERATORS_H +#include "zend_types.h" #include #include #include @@ -66,18 +67,23 @@ ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zv ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce); ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce); -ZEND_API bool ZEND_FASTCALL instanceof_unpacked_slow(const zend_class_reference *ce_ref, const zend_class_entry *ce, const zend_type_list *args); +ZEND_API bool ZEND_FASTCALL instanceof_ref_slow( + const zend_class_reference *instance_ref, + const zend_class_reference *instance_scope, + const zend_class_reference *ref, + const zend_class_reference *ref_scope); static zend_always_inline bool instanceof_function( const zend_class_entry *instance_ce, const zend_class_entry *ce) { return instance_ce == ce || instanceof_function_slow(instance_ce, ce); } -static zend_always_inline bool instanceof_unpacked( - const zend_class_reference *ce_ref, const zend_class_entry *ce, - const zend_type_list *args) { - return (ce_ref->ce == ce && ce_ref->args.num_types == 0 && args->num_types == 0) - || instanceof_unpacked_slow(ce_ref, ce, args); +static zend_always_inline bool instanceof_ref( + const zend_class_reference *instance_ref, + const zend_class_reference *instance_scope, + const zend_class_reference *ref, + const zend_class_reference *ref_scope) { + return instance_ref == ref || instanceof_ref_slow(instance_ref, instance_scope, ref, ref_scope); } ZEND_API bool zend_string_only_has_ascii_alphanumeric(const zend_string *str); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 910e2eed250fe..ac062b1b77e15 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -634,7 +634,6 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ - typedef enum _zend_known_string_id { #define _ZEND_STR_ID(id, str) id, ZEND_KNOWN_STRINGS(_ZEND_STR_ID) diff --git a/Zend/zend_symbolic_inference.c b/Zend/zend_symbolic_inference.c new file mode 100644 index 0000000000000..dbc2fa0593743 --- /dev/null +++ b/Zend/zend_symbolic_inference.c @@ -0,0 +1,2040 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/* This implements type inference for constructor arguments. + * + * The algorithm is unsound by design. Most notably, it ignores references. + * + * This is implemented as a forward data flow analysis based on zend_inference.c + * + * Types that can not be resolved at compile time, such as the return type of + * function calls (for functions declared in other compilation units) are + * represented as symbols. E.g. the type of the expression `f(1)` is represented + * as `fcall`. Such types can then be resolved at runtime. + * + * Symbolic types may represent an infinity of types and never converge. E.g. + * the variable $a in the following code will successively have the types + * fcall, fcall|int>, and so on: + * + * $a = 1; + * while ($i--) { $a = f($a); } + * + * We handle these by using a variation of the algorithm decribed in: + * Weiss G, Schonberg E. Typefinding recursive structures: A data-flow analysis + * in the presence of infinite type sets. New York University. Courant Institute + * of Mathematical Sciences. Computer Science Department; 1986. + * + * This can be summarized as follows: Uses appearing in the same SCC as an + * opcode defs are considered recursive, and represented with a placeholder. + * + * After a fixpoint is reached we resolve the placeholders. Based on the + * assumption that operations are indempotent (type wise), we can eliminate + * recursivity: fcall with $v1=fcall|int is equivalent to + * fcall|int>. + */ + +#include "zend_arena.h" +#include "zend_closures.h" +#include "zend_compile.h" +#include "zend_operators.h" +#include "zend_portability.h" +#include "zend_string.h" +#include "zend_types.h" +#include "zend_type_tools.h" +#include "zend_type_info.h" +#include "zend_bitset.h" +#include "zend_vm_opcodes.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "zend_constants.h" +#include +#include "zend_symbolic_inference.h" +#include "Optimizer/zend_dump.h" +#include "Optimizer/zend_optimizer.h" +#include "Optimizer/zend_cfg.h" +#include "Optimizer/zend_ssa.h" +#include "Optimizer/zend_scc.h" + +static const zend_op *zend_find_fcall_init_op(const zend_op_array *op_array, const zend_op *opline) +{ + int depth = 1; + + for (opline--; opline >= op_array->opcodes; opline--) { + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth--; + if (depth == 0) { + return opline; + } + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth++; + break; + } + } + + ZEND_UNREACHABLE(); +} + +static zend_always_inline zend_type _const_op_type(const zval *zv) { + uint32_t mask; + if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { + // TODO + mask = MAY_BE_ANY; + } else if (Z_TYPE_P(zv) == IS_PNR) { + mask = MAY_BE_STRING; + } else if (Z_TYPE_P(zv) == IS_ARRAY) { + mask = MAY_BE_ARRAY; + } else { + mask = 1 << Z_TYPE_P(zv); + } + + return (zend_type) ZEND_TYPE_INIT_MASK(mask); +} + +#define _ZEND_TYPE_SSA_VAR_BIT (1 << _ZEND_TYPE_EXTRA_FLAGS_SHIFT) +#define _ZEND_TYPE_SYMBOLIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT+1)) + +#define ZEND_TYPE_IS_SSA_VAR(t) (((t).type_mask & _ZEND_TYPE_SSA_VAR_BIT) != 0) +#define ZEND_TYPE_SSA_VAR(t) ((int)(uintptr_t)(t).ptr) + +#define ZEND_TYPE_IS_SYMBOLIC(t) (((t).type_mask & _ZEND_TYPE_SYMBOLIC_BIT) != 0) + +#define ZEND_TYPE_INIT_SYMBOLIC(list, extra_flags) \ + ZEND_TYPE_INIT_PTR(list, _ZEND_TYPE_SYMBOLIC_BIT | _ZEND_TYPE_LIST_BIT, 0, extra_flags) + +#define SYMTYPE_BINOP 1 +#define SYMTYPE_FCALL 2 +#define SYMTYPE_NS_FCALL 3 +#define SYMTYPE_METHOD_CALL 4 +#define SYMTYPE_STATIC_METHOD_CALL 5 +#define SYMTYPE_NEW 6 +#define SYMTYPE_NOT_NULL 7 +#define SYMTYPE_PROP 8 +#define SYMTYPE_CLASS_CONSTANT 9 +#define SYMTYPE_CONSTANT 10 +#define SYMTYPE_CLASS 11 + +#define BINOP_NUMBER_ARRAY 1 +#define BINOP_NUMBER 2 +#define BINOP_LONG_STRING 3 +#define BINOP_STRING 4 +#define BINOP_LONG 5 + +static zend_type_list *zend_symbolic_alloc(zend_arena **arena, int symtype, uint32_t num_types) +{ + zend_type_list *list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); + list->num_types = num_types; + ZEND_TYPE_FULL_MASK(list->types[0]) = symtype; + return list; +} + +static zend_type zend_symbolic_binop(zend_arena **arena, zend_type op1, zend_type op2, uint32_t type_mask) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_BINOP, 4); + ZEND_TYPE_FULL_MASK(list->types[1]) = type_mask; + list->types[2] = op1; + list->types[3] = op2; + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +static zend_type zend_symbolic_binop_auto(zend_arena **arena, size_t opcode, zend_type op1, zend_type op2) +{ + switch (opcode) { + case ZEND_ADD: + return zend_symbolic_binop(arena, op1, op2, BINOP_NUMBER_ARRAY); + case ZEND_MUL: + case ZEND_DIV: + case ZEND_SUB: + case ZEND_POW: + return zend_symbolic_binop(arena, op1, op2, BINOP_NUMBER); + case ZEND_BW_AND: + case ZEND_BW_OR: + case ZEND_BW_XOR: + case ZEND_BW_NOT: + return zend_symbolic_binop(arena, op1, op2, BINOP_LONG_STRING); + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + return zend_symbolic_binop(arena, op1, op2, BINOP_LONG); + case ZEND_CONCAT: + return zend_symbolic_binop(arena, op1, op2, BINOP_STRING); + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } +} + +#define SYMBOLIC_BINOP_TYPE_MASK(list) ZEND_TYPE_FULL_MASK((list)->types[1]) +#define SYMBOLIC_BINOP_OP1(list) (list)->types[2] +#define SYMBOLIC_BINOP_OP2(list) (list)->types[3] + +static zend_type zend_symbolic_fcall(zend_arena **arena, zend_string *fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_FCALL, nargs + 2); + zend_string_addref(fname); + list->types[1] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_FCALL_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[1]) +#define SYMBOLIC_FCALL_ARGS(list) &(list)->types[2] +#define SYMBOLIC_FCALL_NARGS(list) ((list)->num_types-2) + +static zend_type zend_symbolic_ns_fcall(zend_arena **arena, zend_string *fname, zend_string *ns_fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_NS_FCALL, nargs + 3); + zend_string_addref(fname); + zend_string_addref(ns_fname); + list->types[1] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(ns_fname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_NS_FCALL_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[1]) +#define SYMBOLIC_NS_FCALL_NS_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) +#define SYMBOLIC_NS_FCALL_ARGS(list) &(list)->types[3] +#define SYMBOLIC_NS_FCALL_NARGS(list) ((list)->num_types-3) + +static zend_type zend_symbolic_mcall(zend_arena **arena, zend_type object, zend_string *fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_METHOD_CALL, nargs + 3); + ZEND_TYPE_SET_PTR(list->types[0], 0); + zend_string_addref(fname); + list->types[1] = object; + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_MCALL_FETCH_TYPE(list) ((uintptr_t)(list)->types[0].ptr) +#define SYMBOLIC_MCALL_OBJECT(list) (list)->types[1] +#define SYMBOLIC_MCALL_FNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) +#define SYMBOLIC_MCALL_ARGS(list) &(list)->types[3] +#define SYMBOLIC_MCALL_NARGS(list) ((list)->num_types-3) + +static zend_type zend_symbolic_scall(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname, zend_string *fname, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_STATIC_METHOD_CALL, nargs + 4); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + zend_string_addref(fname); + list->types[1] = object; + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(fname), 0, 0); + if (lcname) { + zend_string_addref(lcname); + list->types[4] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_SCALL_LCNAME(list) (list)->types[4] +#define SYMBOLIC_SCALL_NARGS(list) ((list)->num_types-4) + +static zend_type zend_symbolic_new(zend_arena **arena, zend_type class, zend_type *args, size_t nargs) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_NEW, nargs + 2); + list->types[1] = class; + for (size_t i = 0; i < nargs; i++) { + list->types[i+2] = args[i]; + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_NEW_OBJECT(list) (list)->types[1] +#define SYMBOLIC_NEW_ARGS(list) &(list)->types[2] +#define SYMBOLIC_NEW_NARGS(list) ((list)->num_types-2) + +static zend_type zend_symbolic_coalesce(zend_arena **arena, zend_type type) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_NOT_NULL, 2); + list->types[1] = type; + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_NOT_NULL_OP(list) (list)->types[1] + +static zend_type zend_symbolic_prop(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname, zend_string *pname) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_PROP, 4); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + zend_string_addref(pname); + list->types[1] = object; + if (lcname) { + zend_string_addref(lcname); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } + list->types[3] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(pname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_PROP_OBJECT(list) (list)->types[1] +#define SYMBOLIC_PROP_LCNAME(list) (list)->types[2] +#define SYMBOLIC_PROP_PNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[3]) + +static zend_type zend_symbolic_class_const(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname, zend_string *cname) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_CLASS_CONSTANT, 3); + zend_string_addref(cname); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + list->types[1] = object; + if (lcname) { + zend_string_addref(lcname); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } + list->types[3] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_CCONST_FETCH_TYPE(list) ((uintptr_t)(list)->types[0].ptr) +#define SYMBOLIC_CCONST_OBJECT(list) (list)->types[1] +#define SYMBOLIC_CCONST_LCNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) +#define SYMBOLIC_CCONST_CNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[3]) + +static zend_type zend_symbolic_const(zend_arena **arena, zend_string *key1, zend_string *key2) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_CONSTANT, 3); + zend_string_addref(key1); + list->types[1] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(key1), 0, 0); + if (key2) { + zend_string_addref(key2); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(key2), 0, 0); + } else { + list->types[2] = (zend_type) ZEND_TYPE_INIT_MASK(0); + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_CONST_KEY1(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[1]) +#define SYMBOLIC_CONST_KEY2(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) + +static zend_type zend_symbolic_class(zend_arena **arena, uint32_t fetch_type, zend_type object, zend_string *lcname) +{ + zend_type_list *list = zend_symbolic_alloc(arena, SYMTYPE_CLASS, 3); + ZEND_TYPE_SET_PTR(list->types[0], (void*)(uintptr_t)fetch_type); + list->types[1] = object; + if (lcname) { + zend_string_addref(lcname); + list->types[2] = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(lcname), 0, 0); + } else { + list->types[2] = (zend_type) ZEND_TYPE_INIT_MASK(0); + } + return (zend_type) ZEND_TYPE_INIT_SYMBOLIC(list, _ZEND_TYPE_ARENA_BIT); +} + +#define SYMBOLIC_CLASS_FETCH_TYPE(list) ((uintptr_t)(list)->types[0].ptr) +#define SYMBOLIC_CLASS_OBJECT(list) (list)->types[1] +#define SYMBOLIC_CLASS_LCNAME(list) ZEND_TYPE_PNR_SIMPLE_NAME((list)->types[2]) + +static zend_always_inline zend_type get_ssa_var_info(zend_type *ssa_var_info, int ssa_var_num) +{ + if (ssa_var_num >= 0) { + return ssa_var_info[ssa_var_num]; + } else { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } +} + +static zend_always_inline zend_type get_ssa_recursive_var_info(zend_ssa *ssa, zend_type *ssa_var_info, int embedding_var, int embedded_var) +{ + ZEND_ASSERT(embedding_var >= 0); + + if (embedded_var >= 0) { + zend_ssa_var *var = &ssa->vars[embedded_var]; + if (var->scc && ssa->vars[embedding_var].scc == var->scc) { + return (zend_type) ZEND_TYPE_INIT_PTR((void*)(uintptr_t)embedded_var, _ZEND_TYPE_SSA_VAR_BIT, 0, 0); + } else { + return ssa_var_info[embedded_var]; + } + } else { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } +} + +#define DEFINE_SSA_OP_INFO(opN) \ + static ZEND_ATTRIBUTE_UNUSED zend_always_inline zend_type _ssa_##opN##_info(const zend_op_array *op_array, zend_type *ssa_var_info, const zend_op *opline, const zend_ssa_op *ssa_op) \ + { \ + if (opline->opN##_type == IS_CONST) { \ + return _const_op_type(CRT_CONSTANT(opline->opN)); \ + } else { \ + return get_ssa_var_info(ssa_var_info, ssa_op->opN##_use); \ + } \ + } \ + +#define DEFINE_SSA_OP_REC_INFO(opN) \ + static ZEND_ATTRIBUTE_UNUSED zend_always_inline zend_type _ssa_##opN##_rec_info(const zend_op_array *op_array, zend_ssa *ssa, zend_type *ssa_var_info, int var, const zend_op *opline, const zend_ssa_op *ssa_op) \ + { \ + if (opline->opN##_type == IS_CONST) { \ + return _const_op_type(CRT_CONSTANT(opline->opN)); \ + } else { \ + return get_ssa_recursive_var_info(ssa, ssa_var_info, var, ssa_op->opN##_use); \ + } \ + } \ + +DEFINE_SSA_OP_INFO(op1) +DEFINE_SSA_OP_INFO(op2) +DEFINE_SSA_OP_INFO(result) + +DEFINE_SSA_OP_REC_INFO(op1) +DEFINE_SSA_OP_REC_INFO(op2) +DEFINE_SSA_OP_REC_INFO(result) + +#define OP1_INFO_EX(opline, ssa_op) (_ssa_op1_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP2_INFO_EX(opline, ssa_op) (_ssa_op2_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP1_INFO() (_ssa_op1_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP2_INFO() (_ssa_op2_info(op_array, ssa_var_info, opline, ssa_op)) +#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa_var_info, (opline+1), (ssa_op+1))) +#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa_var_info, (opline+1), (ssa_op+1))) +#define RES_USE_INFO() (_ssa_result_info(op_array, ssa_var_info, opline, ssa_op)) + +/* Recursive-aware variants: Return a placeholder if the variable is in the same + * SCC as result_var */ +#define OP1_REC_INFO_EX(opline, ssa_op, result_var) (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, result_var, opline, ssa_op)) +#define OP2_REC_INFO_EX(opline, ssa_op, result_var) (_ssa_op2_rec_info(op_array, ssa, ssa_var_info, result_var, opline, ssa_op)) +#define OP1_DATA_REC_INFO_EX(opline, ssa_op, result_var) (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, result_var, opline, ssa_op)) +#define OP1_REC_INFO() (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, ssa_op->result_def, opline, ssa_op)) +#define OP2_REC_INFO() (_ssa_op2_rec_info(op_array, ssa, ssa_var_info, ssa_op->result_def, opline, ssa_op)) +#define OP1_DATA_REC_INFO() (_ssa_op1_rec_info(op_array, ssa, ssa_var_info, ssa_op->result_def, (opline+1), (ssa_op+1))) + +#define UPDATE_SSA_TYPE(_type, _var) \ + do { \ + uint32_t __type = (_type); \ + int __var = (_var); \ + if (__var >= 0) { \ + if (ZEND_TYPE_FULL_MASK(ssa_var_info[__var]) != __type) { \ + ZEND_TYPE_FULL_MASK(ssa_var_info[__var]) = __type; \ + add_usages(op_array, ssa, worklist, __var); \ + } \ + /*zend_bitset_excl(worklist, var);*/ \ + } \ + } while (0) + +#define UPDATE_SSA_TYPE_EX(_type, _var) \ + do { \ + zend_type __type = (_type); \ + int __var = (_var); \ + if (__var >= 0) { \ + if (!zend_type_equals(ssa_var_info[__var], __type)) { \ + ssa_var_info[__var] = __type; \ + add_usages(op_array, ssa, worklist, __var); \ + } \ + /*zend_bitset_excl(worklist, var);*/ \ + } \ + } while (0) + +static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var) +{ + if (ssa->vars[var].phi_use_chain) { + zend_ssa_phi *p = ssa->vars[var].phi_use_chain; + do { + zend_bitset_incl(worklist, p->ssa_var); + p = zend_ssa_next_use_phi(ssa, var, p); + } while (p); + } + if (ssa->vars[var].use_chain >= 0) { + int use = ssa->vars[var].use_chain; + zend_ssa_op *op; + + do { + op = ssa->ops + use; + if (op->result_def >= 0) { + zend_bitset_incl(worklist, op->result_def); + } + if (op->op1_def >= 0) { + zend_bitset_incl(worklist, op->op1_def); + } + if (op->op2_def >= 0) { + zend_bitset_incl(worklist, op->op2_def); + } + if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { + op--; + if (op->result_def >= 0) { + zend_bitset_incl(worklist, op->result_def); + } + if (op->op1_def >= 0) { + zend_bitset_incl(worklist, op->op1_def); + } + if (op->op2_def >= 0) { + zend_bitset_incl(worklist, op->op2_def); + } + } else if (use + 1 < op_array->last + && op_array->opcodes[use + 1].opcode == ZEND_OP_DATA) { + op++; + if (op->result_def >= 0) { + zend_bitset_incl(worklist, op->result_def); + } + if (op->op1_def >= 0) { + zend_bitset_incl(worklist, op->op1_def); + } + if (op->op2_def >= 0) { + zend_bitset_incl(worklist, op->op2_def); + } + } + use = zend_ssa_next_use(ssa->ops, var, use); + } while (use >= 0); + } +} + +static void zend_get_fcall_arg_types(zend_arena **arena, zend_ssa *ssa, zend_type *ssa_var_info, const zend_op_array *op_array, const zend_op *opline, int result_var, zend_type *args, int nargs) +{ + int depth = 1; + int arg = 0; + + ZEND_ASSERT(result_var >= 0); + + for (int i = opline - op_array->opcodes + 1; i < op_array->last; i++) { + zend_op *opline = &op_array->opcodes[i]; + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_NEW: + depth++; + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + depth--; + break; + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_USER: + if (depth == 1) { + zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; + args[arg] = OP1_REC_INFO_EX(opline, ssa_op, result_var); + arg++; + } + break; + } + if (depth == 0) { + break; + } + } + + ZEND_ASSERT(arg == nargs); +} + +static bool zend_type_equals(zend_type a, zend_type b) +{ + if (ZEND_TYPE_FULL_MASK(a) != ZEND_TYPE_FULL_MASK(b)) { + return false; + } + + if (!ZEND_TYPE_IS_COMPLEX(a) && !ZEND_TYPE_IS_SSA_VAR(a)) { + return true; + } + + if (a.ptr == b.ptr) { + return true; + } + + if (ZEND_TYPE_IS_SSA_VAR(a)) { + return false; + } + + if (ZEND_TYPE_HAS_PNR(a)) { + zend_packed_name_reference pnr_a = ZEND_TYPE_PNR(a); + zend_packed_name_reference pnr_b = ZEND_TYPE_PNR(b); + if (ZEND_PNR_IS_COMPLEX(pnr_a)) { + if (!ZEND_PNR_IS_COMPLEX(pnr_b)) { + return false; + } + zend_name_reference *ref_a = ZEND_PNR_COMPLEX_GET_REF(pnr_a); + zend_name_reference *ref_b = ZEND_PNR_COMPLEX_GET_REF(pnr_b); + if (!zend_string_equals(ref_a->name, ref_b->name)) { + return false; + } + if (ref_a->args.num_types != ref_b->args.num_types) { + return false; + } + for (uint32_t i = 0; i < ref_a->args.num_types; i++) { + if (!zend_type_equals(ref_a->args.types[i], ref_b->args.types[i])) { + return false; + } + } + return true; + } else { + if (ZEND_PNR_IS_COMPLEX(pnr_b)) { + return false; + } + zend_string *name_a = ZEND_PNR_SIMPLE_GET_NAME(pnr_a); + zend_string *name_b = ZEND_PNR_SIMPLE_GET_NAME(pnr_b); + return zend_string_equals(name_a, name_b); + } + } + + ZEND_ASSERT(ZEND_TYPE_HAS_LIST(a)); + + zend_type_list *a_list = ZEND_TYPE_LIST(a); + zend_type_list *b_list = ZEND_TYPE_LIST(b); + if (a_list->num_types != b_list->num_types) { + return false; + } + for (uint32_t i = 0; i < a_list->num_types; i++) { + if (!zend_type_equals(a_list->types[i], b_list->types[i])) { + return false; + } + } + + return true; +} + +static bool zend_union_type_contains(zend_type container, zend_type t) +{ + if (ZEND_TYPE_IS_UNION(container)) { + zend_type *single_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(container), single_type) { + if (zend_type_equals(*single_type, t)) { + return true; + } + } ZEND_TYPE_LIST_FOREACH_END(); + return false; + } else { + return zend_type_equals(container, t); + } +} + +static zend_result zend_resolve_relative_class_base(zend_arena **arena, + uint32_t *fetch_type, zend_type *object, zend_string **lcname, + const zend_op_array *op_array, const znode_op *op) { + if (!op_array->scope + || (op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + return FAILURE; + } + zend_packed_name_reference pnr; + if (op_array->scope->num_generic_params) { + zend_name_reference *name_ref = zend_arena_alloc( + arena, ZEND_CLASS_REF_SIZE(op_array->scope->num_generic_params)); + name_ref->name = op_array->scope->name; + zend_string_addref(name_ref->name); + for (uint32_t i = 0; i < op_array->scope->num_generic_params; i++) { + name_ref->args.types[i] = (zend_type) ZEND_TYPE_INIT_GENERIC_PARAM(i, 0); + } + name_ref->args.num_types = op_array->scope->num_generic_params; + zend_compile_name_reference_key(name_ref, NULL); + pnr = ZEND_PNR_ENCODE_REF(name_ref); + } else { + zend_string *cname = op_array->scope->name; + zend_string_addref(cname); + pnr = ZEND_PNR_ENCODE_NAME(cname); + } + *lcname = ZEND_CE_TO_REF(op_array->scope->name)->key; + *object = (zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, 0); + *fetch_type = op->num & ZEND_FETCH_CLASS_MASK; + return SUCCESS; +} + +static zend_result zend_update_symbolic_var_type(zend_arena **arena, zend_ssa *ssa, zend_bitset worklist, zend_type *ssa_var_info, const zend_op_array *op_array, const zend_op *opline) +{ + zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; + + if (opline->opcode == ZEND_OP_DATA) { + opline--; + ssa_op--; + } + + zend_type t1 = OP1_INFO(); + zend_type t2 = OP2_INFO(); + +label: + if (0) goto label; + + /* If one of the operands cannot have any type, this means the operand derives from + * unreachable code. Propagate the empty result early, so that that the following + * code may assume that operands have at least one type. */ + if (!t1.type_mask + || !t2.type_mask + || (ssa_op->result_use >= 0 && !RES_USE_INFO().type_mask) + || ((opline->opcode == ZEND_ASSIGN_DIM_OP + || opline->opcode == ZEND_ASSIGN_OBJ_OP + || opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP + || opline->opcode == ZEND_ASSIGN_DIM + || opline->opcode == ZEND_ASSIGN_OBJ) + && !OP1_DATA_INFO().type_mask)) { + if (ssa_op->result_def >= 0) { + UPDATE_SSA_TYPE(0, ssa_op->result_def); + } + if (ssa_op->op1_def >= 0) { + UPDATE_SSA_TYPE(0, ssa_op->op1_def); + } + if (ssa_op->op2_def >= 0) { + UPDATE_SSA_TYPE(0, ssa_op->op2_def); + } + if (opline->opcode == ZEND_ASSIGN_DIM_OP + || opline->opcode == ZEND_ASSIGN_OBJ_OP + || opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP + || opline->opcode == ZEND_ASSIGN_DIM + || opline->opcode == ZEND_ASSIGN_OBJ) { + if ((ssa_op+1)->op1_def >= 0) { + UPDATE_SSA_TYPE(0, (ssa_op+1)->op1_def); + } + } + + return SUCCESS; + } + + switch (opline->opcode) { + case ZEND_ASSIGN: + UPDATE_SSA_TYPE_EX(t2, ssa_op->op1_def); + UPDATE_SSA_TYPE_EX(t2, ssa_op->result_def); + break; + case ZEND_QM_ASSIGN: + case ZEND_COPY_TMP: + UPDATE_SSA_TYPE_EX(t1, ssa_op->result_def); + break; + case ZEND_ASSIGN_DIM: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + zend_type d1 = OP1_DATA_INFO(); + UPDATE_SSA_TYPE_EX(d1, ssa_op->result_def); + break; + } + case ZEND_UNSET_CV: + UPDATE_SSA_TYPE(MAY_BE_NULL, ssa_op->op1_def); + break; + case ZEND_UNSET_DIM: + case ZEND_UNSET_OBJ: + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + break; + case ZEND_INIT_ARRAY: + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_ADD_ARRAY_UNPACK: + UPDATE_SSA_TYPE(MAY_BE_ARRAY, ssa_op->result_def); + break; + case ZEND_ASSIGN_DIM_OP: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + // TODO: ArrayAccess (if op1 is an object, we can do better) + zend_type t = zend_symbolic_binop_auto(arena, + opline->extended_value, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY), + OP1_DATA_REC_INFO_EX(opline+1, ssa_op+1, ssa_op->op1_def)); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_RW: + case ZEND_FETCH_IS: + case ZEND_FETCH_FUNC_ARG: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_FETCH_GLOBALS: + UPDATE_SSA_TYPE(MAY_BE_ARRAY, ssa_op->result_def); + break; + case ZEND_INCLUDE_OR_EVAL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_FETCH_DIM_R: + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_IS: + case ZEND_FETCH_DIM_UNSET: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_LIST_R: + case ZEND_FETCH_LIST_W: + /* TODO: generic arrays */ + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + zend_type d1 = OP1_DATA_INFO(); + UPDATE_SSA_TYPE_EX(d1, ssa_op->result_def); + break; + } + case ZEND_ASSIGN_OBJ_OP: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); // TODO: vivification + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_type d1 = OP1_DATA_REC_INFO(); + zend_type t = zend_symbolic_binop_auto(arena, + opline->extended_value, + zend_symbolic_prop(arena, 0, OP1_REC_INFO(), NULL, + Z_STR_P(CRT_CONSTANT(opline->op2))), + d1); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_OP: + case ZEND_ASSIGN_STATIC_PROP_REF: { + // TODO: intersect prop type and op2 + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + case ZEND_BEGIN_SILENCE: + UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); + break; + case ZEND_BIND_GLOBAL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op1_def); + break; + case ZEND_BIND_INIT_STATIC_OR_JMP: + /* TODO */ + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op1_def); + break; + case ZEND_BIND_LEXICAL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op2_def); + break; + case ZEND_BIND_STATIC: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->op1_def); + break; + case ZEND_CALLABLE_CONVERT: { + zend_string *cname = zend_ce_closure->name; + zend_type t = ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_CAST: + /* TODO: preserve object type */ + UPDATE_SSA_TYPE(1 << opline->extended_value, ssa_op->result_def); + break; + case ZEND_TYPE_CHECK: + UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def); + break; + case ZEND_VERIFY_RETURN_TYPE: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + } else { + UPDATE_SSA_TYPE_EX(t1, ssa_op->result_def); + } + break; + case ZEND_CLONE: + UPDATE_SSA_TYPE_EX(t1, ssa_op->result_def); + break; + case ZEND_COUNT: + case ZEND_STRLEN: + case ZEND_FUNC_NUM_ARGS: + UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); + break; + case ZEND_DECLARE_ANON_CLASS: + /* TODO: intersect parent and interfaces */ + UPDATE_SSA_TYPE(MAY_BE_OBJECT, ssa_op->result_def); + break; + case ZEND_DEFINED: + UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def); + break; + case ZEND_FETCH_CONSTANT: { + zend_type t = zend_symbolic_const(arena, + Z_STR_P(CRT_CONSTANT(opline->op2) + 1), + (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) + ? Z_STR_P(CRT_CONSTANT(opline->op2) + 2) : NULL); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_ASSIGN_OP: { + zend_type t = zend_symbolic_binop_auto(arena, + opline->extended_value, + OP1_REC_INFO_EX(opline, ssa_op, ssa_op->op1_def), + OP2_REC_INFO_EX(opline, ssa_op, ssa_op->op1_def)); + UPDATE_SSA_TYPE_EX(t, ssa_op->op1_def); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_OBJ_R: + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_FETCH_OBJ_FUNC_ARG: { + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op1_type == IS_UNUSED) { + if (zend_resolve_relative_class_base(arena, + &fetch_type, &object, &lcname, op_array, &opline->op1) == FAILURE) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + return SUCCESS; + } + } else { + object = OP1_REC_INFO(); + } + zend_type t = zend_symbolic_prop(arena, fetch_type, OP1_REC_INFO(), + lcname, Z_STR_P(CRT_CONSTANT(opline->op2))); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_STATIC_PROP_R: + case ZEND_FETCH_STATIC_PROP_W: + case ZEND_FETCH_STATIC_PROP_RW: + case ZEND_FETCH_STATIC_PROP_IS: + case ZEND_FETCH_STATIC_PROP_UNSET: + case ZEND_FETCH_STATIC_PROP_FUNC_ARG: { + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op2_type == IS_UNUSED) { + if (zend_resolve_relative_class_base(arena, + &fetch_type, &object, &lcname, op_array, &opline->op2) == FAILURE) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + return SUCCESS; + } + } else if (opline->op2_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op2); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP1_REC_INFO(); + } + zend_type t = zend_symbolic_prop(arena, fetch_type, object, + lcname, Z_STR_P(CRT_CONSTANT(opline->op1))); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FETCH_CLASS_CONSTANT: { + if (opline->op2_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_type object = {0}; + zend_string *lcname = NULL; + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + if (opline->op1_type == IS_UNUSED) { + if (!op_array->scope + || (op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_string *cname = op_array->scope->name; + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + fetch_type = opline->op1.num & ZEND_FETCH_CLASS_MASK; + } else if (opline->op1_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP1_REC_INFO(); + } + zend_type t = zend_symbolic_class_const(arena, fetch_type, object, + lcname, Z_STR_P(CRT_CONSTANT(opline->op2))); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_BOOL_NOT: + case ZEND_BOOL_XOR: + case ZEND_BOOL: + case ZEND_IS_IDENTICAL: + case ZEND_IS_NOT_IDENTICAL: + case ZEND_IS_EQUAL: + case ZEND_IS_NOT_EQUAL: + case ZEND_IS_SMALLER: + case ZEND_IS_SMALLER_OR_EQUAL: + case ZEND_INSTANCEOF: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_CASE: + case ZEND_CASE_STRICT: + case ZEND_ISSET_ISEMPTY_CV: + case ZEND_ISSET_ISEMPTY_VAR: + case ZEND_ISSET_ISEMPTY_DIM_OBJ: + case ZEND_ISSET_ISEMPTY_PROP_OBJ: + case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_ASSERT_CHECK: + case ZEND_IN_ARRAY: + case ZEND_ARRAY_KEY_EXISTS: + UPDATE_SSA_TYPE(MAY_BE_BOOL, ssa_op->result_def); + break; + case ZEND_ADD: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_SUB: + case ZEND_POW: + case ZEND_BW_AND: + case ZEND_BW_OR: + case ZEND_BW_XOR: + case ZEND_BW_NOT: + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + case ZEND_CONCAT: { + zend_type t = zend_symbolic_binop_auto(arena, opline->opcode, + OP1_REC_INFO(), OP2_REC_INFO()); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: { + UPDATE_SSA_TYPE(MAY_BE_LONG | MAY_BE_DOUBLE, ssa_op->result_def); + UPDATE_SSA_TYPE(MAY_BE_LONG | MAY_BE_DOUBLE, ssa_op->op1_def); + break; + } + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_USER_CALL: + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: { + zend_type_list *list = zend_arena_calloc(arena, 1, ZEND_TYPE_LIST_SIZE(opline->extended_value + 1)); + list->num_types = opline->extended_value + 1; + list->types[0] = t1; + zend_type result = ZEND_TYPE_INIT_UNION(list, _ZEND_TYPE_ARENA_BIT); + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + break; + } + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: { + const zend_op *init = zend_find_fcall_init_op(op_array, opline); + + if (init->op2_type != IS_CONST) { + // TODO: invokable + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + + uint32_t nargs = init->extended_value; + zend_type *args; + + zend_type result; + + switch (init->opcode) { + case ZEND_INIT_FCALL_BY_NAME: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + result = zend_symbolic_fcall(arena, fname, nargs); + args = SYMBOLIC_FCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_FCALL: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)); + result = zend_symbolic_fcall(arena, fname, nargs); + args = SYMBOLIC_FCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_NS_FCALL_BY_NAME: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + zend_string *ns_fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+2); + result = zend_symbolic_ns_fcall(arena, fname, ns_fname, nargs); + args = SYMBOLIC_NS_FCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_METHOD_CALL: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + zend_type object = OP1_REC_INFO_EX(init, &ssa->ops[init-op_array->opcodes], ssa_op->result_def); + result = zend_symbolic_mcall(arena, object, fname, nargs); + args = SYMBOLIC_MCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + case ZEND_INIT_STATIC_METHOD_CALL: { + zend_string *fname = Z_STR_P(CRT_CONSTANT_EX(op_array, init, init->op2)+1); + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op1_type == IS_UNUSED) { + if (zend_resolve_relative_class_base(arena, + &fetch_type, &object, &lcname, op_array, + &init->op1) == FAILURE) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + return SUCCESS; + } + } else if (opline->op1_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP1_REC_INFO(); + } + result = zend_symbolic_scall(arena, fetch_type, object, + lcname, fname, nargs); + args = SYMBOLIC_MCALL_ARGS(ZEND_TYPE_LIST(result)); + break; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } + + zend_get_fcall_arg_types(arena, ssa, ssa_var_info, op_array, init, + ssa_op->result_def, args, nargs); + + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + + break; + } + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_REF: + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_UNPACK: { + UPDATE_SSA_TYPE_EX(t1, ssa_op->op1_def); + zend_type result = RES_USE_INFO(); + if (!ZEND_TYPE_IS_UNION(result)) { + const zend_op *init_op = zend_find_fcall_init_op(op_array, opline); + zend_type_list *list = zend_arena_calloc(arena, 1, ZEND_TYPE_LIST_SIZE(init_op->extended_value + 1)); + list->num_types = opline->extended_value + 1; + list->types[opline->extended_value+1] = t1; + result = (zend_type) ZEND_TYPE_INIT_UNION(list, _ZEND_TYPE_ARENA_BIT); + } else { + zend_type_list *list = ZEND_TYPE_LIST(result); + // TODO: wasting arena space + zend_type_list *new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types)); + ZEND_ASSERT(opline->extended_value+1 < list->num_types); + new_list->types[opline->extended_value+1] = t1; + ZEND_TYPE_SET_PTR(result, new_list); + } + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + break; + } + case ZEND_FETCH_THIS: { + if (!op_array->scope) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + // TODO: generic scope + zend_packed_name_reference pnr = ZEND_PNR_ENCODE_NAME(op_array->scope->name); + UPDATE_SSA_TYPE_EX((zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, 0), ssa_op->result_def); + break; + } + case ZEND_FETCH_CLASS: { + uint32_t fetch_type = ZEND_FETCH_CLASS_DEFAULT; + zend_type object = {0}; + zend_string *lcname = NULL; + if (opline->op2_type == IS_UNUSED) { + if (!op_array->scope + || (op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + zend_string *cname = op_array->scope->name; + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK; + } else if (opline->op2_type == IS_CONST) { + zval *zcname = CRT_CONSTANT_EX(op_array, opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zcname) == IS_STRING); + zend_string *cname = Z_STR_P(zcname); + zend_string_addref(cname); + object = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + lcname = Z_STR_P(zcname + 1); + } else { + object = OP2_REC_INFO(); + } + zend_type t = zend_symbolic_class(arena, fetch_type, object, + lcname); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_NEW: { + if (opline->op1_type != IS_CONST) { + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + + zval *zclass = CRT_CONSTANT(opline->op1); + zend_packed_name_reference pnr = Z_PNR_P(zclass); + if (ZEND_PNR_IS_COMPLEX(pnr)) { + UPDATE_SSA_TYPE_EX((zend_type) ZEND_TYPE_INIT_PNR(pnr, 0, 0), ssa_op->result_def); + break; + } + + zval *ztypes = CRT_CONSTANT(opline->op1)+2; + zend_type_list *args; + if (Z_TYPE_P(ztypes) == IS_NULL) { + int nargs = opline->extended_value; + args = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(nargs)); + args->num_types = nargs; + ZVAL_PTR(ztypes, args); + } else { + args = Z_PTR_P(ztypes); + } + + zend_get_fcall_arg_types(arena, ssa, ssa_var_info, op_array, opline, ssa_op->result_def, args->types, args->num_types); + + zend_string *cname = Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op1)); + zend_string_addref(cname); + zend_type class = ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + zend_type result = zend_symbolic_new(arena, class, args->types, args->num_types); + + UPDATE_SSA_TYPE_EX(result, ssa_op->result_def); + break; + } + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + break; + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: { + /* TODO: symbolic type should be "iter_key" or "iter_value" */ + /* TODO: generic arrays */ + /* TODO: generic iterable and Iterator */ + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + case ZEND_JMP_SET: + case ZEND_COALESCE: { + zend_type t = zend_symbolic_coalesce(arena, OP1_REC_INFO()); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + case ZEND_FAST_CONCAT: + case ZEND_ROPE_INIT: + case ZEND_ROPE_ADD: + case ZEND_ROPE_END: + UPDATE_SSA_TYPE(MAY_BE_STRING, ssa_op->result_def); + break; + case ZEND_RECV: + case ZEND_RECV_INIT: { + zend_arg_info *arg_info = &op_array->arg_info[opline->op1.num-1]; + zend_type t = arg_info->type; + if (ZEND_TYPE_IS_SET(t)) { + ZEND_TYPE_FULL_MASK(t) &= _ZEND_TYPE_MASK; + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + UPDATE_SSA_TYPE(MAY_BE_ANY, ssa_op->result_def); + break; + } + case ZEND_RECV_VARIADIC: + UPDATE_SSA_TYPE(MAY_BE_ARRAY, ssa_op->result_def); + break; + case ZEND_DECLARE_LAMBDA_FUNCTION: { + zend_string *cname = zend_ce_closure->name; + zend_type t = ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(cname), 0, 0); + UPDATE_SSA_TYPE_EX(t, ssa_op->result_def); + break; + } + default: + fprintf(stderr, "Unhandled opcode: %d\n", opline->opcode); + break; + } + + return SUCCESS; +} + +static void zend_type_list_copy(zend_type_list *dest, zend_type_list *src, uint32_t num_types) +{ + zend_type *cur = src->types; + zend_type *end = cur+num_types; + zend_type *cur_dest = dest->types; + for (; cur < end; cur++, cur_dest++) { + *cur_dest = *cur; + } + dest->num_types = num_types; +} + +static zend_result zend_resolve_recursive_type_placeholders(zend_arena **arena, zend_ssa *ssa, zend_type *ssa_var_info, zend_type *t, zend_bitset seen) +{ + if (ZEND_TYPE_IS_SSA_VAR(*t)) { + int var = ZEND_TYPE_SSA_VAR(*t); + if (zend_bitset_in(seen, var)) { + return FAILURE; + } + zend_bitset_incl(seen, var); + zend_type tmp = ssa_var_info[var]; + zend_result result = zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen); + zend_bitset_excl(seen, var); + *t = tmp; + return result; + } + + if (ZEND_TYPE_IS_UNION(*t) || ZEND_TYPE_IS_INTERSECTION(*t)) { + zend_type *list_type; + zend_type_list *list = ZEND_TYPE_LIST(*t); + zend_type_list *new_list = NULL; + ZEND_TYPE_LIST_FOREACH(list, list_type) { + zend_type tmp = *list_type; + if (zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen) == FAILURE) { + if (new_list == NULL) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types-1)); + zend_type_list_copy(new_list, list, list_type-list->types); + ZEND_TYPE_SET_LIST(*t, new_list); + } + continue; + } else if (tmp.type_mask != list_type->type_mask + || ((ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) + && tmp.ptr != list_type->ptr)) { + if (new_list == NULL) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + zend_type_list_copy(new_list, list, list_type-list->types); + ZEND_TYPE_SET_LIST(*t, new_list); + } + } + if (new_list) { + new_list->types[new_list->num_types++] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + if (new_list && new_list->num_types == 0) { + if (ZEND_TYPE_PURE_MASK(*t) == 0) { + return FAILURE; + } + ZEND_TYPE_FULL_MASK(*t) = ZEND_TYPE_PURE_MASK(*t); + } + } else if (ZEND_TYPE_HAS_COMPLEX_PNR(*t)) { + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(*t)); + zend_name_reference *new_ref = NULL; + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(&ref->args, list_type) { + zend_type tmp = *list_type; + if (zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen) == FAILURE) { + if (ZEND_TYPE_PURE_MASK(*t) == 0) { + return FAILURE; + } + ZEND_TYPE_FULL_MASK(*t) = ZEND_TYPE_PURE_MASK(*t); + } else if (tmp.type_mask != list_type->type_mask + || ((ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) + && tmp.ptr != list_type->ptr)) { + if (new_ref == NULL) { + new_ref = zend_arena_alloc(arena, ZEND_CLASS_REF_SIZE(ref->args.num_types)); + zend_type_list_copy(&new_ref->args, &ref->args, list_type-ref->args.types); + new_ref->name = ref->name; + ZEND_TYPE_SET_PNR(*t, ZEND_PNR_ENCODE_REF(new_ref)); + } + } + if (new_ref) { + new_ref->args.types[new_ref->args.num_types++] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_IS_SYMBOLIC(*t)) { + zend_type_list *new_list = NULL; + zend_type_list *list = ZEND_TYPE_LIST(*t); + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*t), list_type) { + zend_type tmp = *list_type; + if (zend_resolve_recursive_type_placeholders(arena, ssa, ssa_var_info, &tmp, seen) == FAILURE) { + if (ZEND_TYPE_PURE_MASK(*t) == 0) { + return FAILURE; + } + ZEND_TYPE_FULL_MASK(*t) = ZEND_TYPE_PURE_MASK(*t); + } else if (tmp.type_mask != list_type->type_mask + || ((ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) + && tmp.ptr != list_type->ptr)) { + if (new_list == NULL) { + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(list->num_types)); + zend_type_list_copy(new_list, list, list_type-list->types); + ZEND_TYPE_SET_PTR(*t, new_list); + } + } + if (new_list) { + new_list->types[new_list->num_types++] = tmp; + } + } ZEND_TYPE_LIST_FOREACH_END(); + } + + return SUCCESS; +} + +static void zend_resolve_recursive_placeholders(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa, zend_type *ssa_var_info) +{ + ALLOCA_FLAG(use_heap); + uint32_t seen_len = zend_bitset_len(ssa->vars_count); + zend_bitset seen = ZEND_BITSET_ALLOCA(seen_len, use_heap); + zend_bitset_clear(seen, seen_len); + + for (int i = 0; i < op_array->last; i++) { + const zend_op *opline = &op_array->opcodes[i]; + if (opline->opcode == ZEND_NEW && opline->op1_type == IS_CONST && + ZEND_PNR_IS_SIMPLE(Z_PNR_P(CRT_CONSTANT(opline->op1)))) { + zval *ztypes = CRT_CONSTANT(opline->op1)+2; + if (Z_TYPE_P(ztypes) != IS_NULL) { + zend_type_list *args = Z_PTR_P(ztypes); + for (uintptr_t j = 0; j < args->num_types; j++) { + zend_type tmp = args->types[j]; + if (ZEND_TYPE_IS_SSA_VAR(tmp)) { + tmp = ssa_var_info[ZEND_TYPE_SSA_VAR(tmp)]; + } + zend_result result = zend_resolve_recursive_type_placeholders(arena, + ssa, ssa_var_info, &tmp, seen); + //ZEND_ASSERT(tmp.type_mask != 0); + args->types[j] = tmp; + ZEND_ASSERT(result == SUCCESS); + } + } + } + } + + free_alloca(seen, use_heap); +} + +static zend_result zend_build_type_pnr_keys(zend_type type) +{ + zend_type *type_elem; + ZEND_TYPE_FOREACH(type, type_elem) { + if (ZEND_TYPE_HAS_COMPLEX_PNR(*type_elem)) { + if (ZEND_TYPE_IS_SYMBOLIC(*type_elem)) { + return FAILURE; + } + zend_name_reference *ref = ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(*type_elem)); + for (uint32_t i = 0; i < ref->args.num_types; i++) { + if (zend_build_type_pnr_keys(ref->args.types[i]) == FAILURE) { + return FAILURE; + } + } + zend_string *lcname = zend_string_tolower(ref->name); + zend_compile_name_reference_key(ref, lcname); + zend_string_release(lcname); + } else if (ZEND_TYPE_HAS_LIST(*type_elem)) { + zend_build_type_pnr_keys(*type_elem); + } + } ZEND_TYPE_FOREACH_END(); + + return SUCCESS; +} + +static void zend_build_pnr_keys(const zend_op_array *op_array) +{ + for (int i = 0; i < op_array->last; i++) { + const zend_op *opline = &op_array->opcodes[i]; + if (opline->opcode == ZEND_NEW && opline->op1_type == IS_CONST && + ZEND_PNR_IS_SIMPLE(Z_PNR_P(CRT_CONSTANT(opline->op1)))) { + zval *ztypes = CRT_CONSTANT(opline->op1)+2; + if (Z_TYPE_P(ztypes) != IS_NULL) { + zend_type_list *args = Z_PTR_P(ztypes); + for (uintptr_t j = 0; j < args->num_types; j++) { + zend_build_type_pnr_keys(args->types[j]); + } + } + } + } +} + +void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra); + +/* Infers the symbolic type of ZEND_NEW arguments, at compile time. */ +ZEND_API zend_result zend_symbolic_inference(const zend_op_array *op_array, zend_arena **arena) +{ + zend_ssa *ssa = zend_arena_alloc(arena, sizeof(zend_ssa)); + memset(ssa, 0, sizeof(zend_ssa)); + + zend_build_cfg(arena, op_array, ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg); + + zend_cfg_build_predecessors(arena, &ssa->cfg); + + /* Compute Dominators Tree */ + zend_cfg_compute_dominators_tree(op_array, &ssa->cfg); + + /* Identify reducible and irreducible loops */ + zend_cfg_identify_loops(op_array, &ssa->cfg); + +#if 0 + if (zend_string_equals_cstr(op_array->function_name, "main", 4)) { + zend_dump_op_array(op_array, ZEND_DUMP_CFG, "symbolic inference (CFG)", &ssa->cfg); + } +#endif + + zend_script script = {0}; + uint32_t build_flags = ZEND_SSA_CALL_CHAINS; + if (zend_build_ssa(arena, &script, op_array, build_flags, ssa) == FAILURE) { + return FAILURE; + } + + zend_ssa_compute_use_def_chains(arena, op_array, ssa); + zend_ssa_find_sccs(op_array, ssa); + +#if 0 + if (!op_array->function_name || zend_string_equals_cstr(op_array->function_name, "test", 4)) { + //ssa_verify_integrity((zend_op_array*)op_array, ssa, "symbolic inference"); + zend_dump_op_array(op_array, ZEND_DUMP_SSA, "symbolic inference", ssa); + zend_dump_ssa_variables(op_array, ssa, 0); + } +#endif + + ALLOCA_FLAG(use_heap); + uint32_t worklist_len = zend_bitset_len(ssa->vars_count); + zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap); + memset(worklist, 0, sizeof(zend_ulong) * worklist_len); + for (int i = op_array->last_var; i < ssa->vars_count; i++) { + zend_bitset_incl(worklist, i); + } + + zend_ssa_var *ssa_vars = ssa->vars; + zend_type *ssa_var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(*ssa_var_info)); + for (int i = 0; i < op_array->last_var; i++) { + ZEND_TYPE_FULL_MASK(ssa_var_info[i]) = MAY_BE_UNDEF; + } + + zend_basic_block *blocks = ssa->cfg.blocks; + while (!zend_bitset_empty(worklist, worklist_len)) { + int j = zend_bitset_first(worklist, worklist_len); + zend_bitset_excl(worklist, j); + if (ssa_vars[j].definition_phi) { + zend_ssa_phi *p = ssa_vars[j].definition_phi; + if (p->pi >= 0) { + zend_type tmp = get_ssa_var_info(ssa_var_info, p->sources[0]); + UPDATE_SSA_TYPE_EX(tmp, j); + } else { + zend_type tmp; + ALLOCA_FLAG(use_heap); + zend_type_list *new_list = NULL; + + for (int i = 0; i < blocks[p->block].predecessors_count; i++) { + if (i == 0) { + tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + } else if (!ZEND_TYPE_IS_COMPLEX(tmp) && !ZEND_TYPE_IS_SSA_VAR(tmp)) { + uint32_t mask = ZEND_TYPE_PURE_MASK(tmp); + tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + ZEND_TYPE_FULL_MASK(tmp) |= mask; + } else { + zend_type tmp2 = get_ssa_var_info(ssa_var_info, p->sources[i]); + if (tmp2.type_mask == 0) { + continue; + } + + if (ZEND_TYPE_IS_INTERSECTION(tmp2)) { + if (zend_type_equals(tmp, tmp2)) { + continue; + } + if (!new_list) { + uint32_t num_types = ZEND_TYPE_IS_UNION(tmp) ? ZEND_TYPE_LIST(tmp)->num_types : 1; + for (int j = i; j < blocks[p->block].predecessors_count; j++) { + zend_type tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + if (ZEND_TYPE_IS_UNION(tmp)) { + num_types += ZEND_TYPE_LIST(tmp)->num_types; + } else if (ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) { + num_types++; + } + } + + new_list = do_alloca(ZEND_TYPE_LIST_SIZE(num_types), use_heap); + if (ZEND_TYPE_IS_UNION(tmp)) { + memcpy(new_list, ZEND_TYPE_LIST(tmp), ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(tmp)->num_types)); + } else { + new_list->types[0] = tmp; + new_list->num_types = 1; + } + + ZEND_TYPE_SET_LIST(tmp, new_list); + ZEND_TYPE_FULL_MASK(tmp) = ZEND_TYPE_PURE_MASK(tmp) | _ZEND_TYPE_LIST_BIT | _ZEND_TYPE_UNION_BIT; + } + new_list->types[new_list->num_types++] = tmp2; + continue; + } + + if (!ZEND_TYPE_IS_COMPLEX(tmp2) && !ZEND_TYPE_IS_SSA_VAR(tmp2)) { + ZEND_TYPE_FULL_MASK(tmp) |= ZEND_TYPE_PURE_MASK(tmp2); + continue; + } + + zend_type *tmp2_elem; + ZEND_TYPE_FOREACH_EX(tmp2, tmp2_elem, ~_ZEND_TYPE_SYMBOLIC_BIT) { + if (zend_union_type_contains(tmp, *tmp2_elem)) { + continue; + } + if (!new_list) { + uint32_t num_types = ZEND_TYPE_IS_UNION(tmp) ? ZEND_TYPE_LIST(tmp)->num_types : 1; + for (int j = i; j < blocks[p->block].predecessors_count; j++) { + zend_type tmp = get_ssa_var_info(ssa_var_info, p->sources[i]); + if (ZEND_TYPE_IS_UNION(tmp)) { + num_types += ZEND_TYPE_LIST(tmp)->num_types; + } else if (ZEND_TYPE_IS_COMPLEX(tmp) || ZEND_TYPE_IS_SSA_VAR(tmp)) { + num_types++; + } + } + + new_list = do_alloca(ZEND_TYPE_LIST_SIZE(num_types), use_heap); + if (ZEND_TYPE_IS_UNION(tmp)) { + memcpy(new_list, ZEND_TYPE_LIST(tmp), ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(tmp)->num_types)); + } else { + new_list->types[0] = tmp; + new_list->num_types = 1; + } + + ZEND_TYPE_SET_LIST(tmp, new_list); + ZEND_TYPE_FULL_MASK(tmp) = ZEND_TYPE_PURE_MASK(tmp) | _ZEND_TYPE_LIST_BIT | _ZEND_TYPE_UNION_BIT; + } + new_list->types[new_list->num_types++] = *tmp2_elem; + } ZEND_TYPE_FOREACH_END(); + } + } + + if (new_list) { + size_t size = ZEND_TYPE_LIST_SIZE(new_list->num_types); + zend_type_list *list = zend_arena_alloc(arena, size); + memcpy(list, new_list, size); + ZEND_TYPE_SET_LIST(tmp, list); + free_alloca(new_list, use_heap); + } + + UPDATE_SSA_TYPE_EX(tmp, j); + } + } else if (ssa_vars[j].definition >= 0) { + int i = ssa_vars[j].definition; + if (zend_update_symbolic_var_type(arena, ssa, worklist, ssa_var_info, op_array, op_array->opcodes + i) == FAILURE) { + return FAILURE; + } + } + } + + free_alloca(worklist, use_heap); + + zend_resolve_recursive_placeholders(arena, op_array, ssa, ssa_var_info); + zend_build_pnr_keys(op_array); + + return SUCCESS; +} + +static zend_type zend_resolve_overloaded_type(zend_type type) +{ + zend_type result = ZEND_TYPE_INIT_MASK(0); + zend_type *single_type; + + ZEND_ASSERT(!ZEND_TYPE_IS_SYMBOLIC(type)); + + ZEND_TYPE_FOREACH(type, single_type) { + if (ZEND_TYPE_HAS_PNR(type)) { + zend_string *name = ZEND_TYPE_PNR_NAME(type); + zend_class_entry *ce = zend_fetch_class(name, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); + if (!ce) { + continue; + } + if (ce->default_object_handlers->do_operation) { + // TODO + if (zend_string_equals_cstr(ce->name, "GMP", 3)) { + result = (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(ce->name), 0, 0); + } else { + ZEND_TYPE_FULL_MASK(result) = IS_OBJECT; + } + // TODO: join with existing result + return result; + } + } else if (ZEND_TYPE_IS_INTERSECTION(type)) { + zend_type inner = zend_resolve_overloaded_type(*single_type); + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(inner)); /* TODO */ + ZEND_TYPE_FULL_MASK(result) |= ZEND_TYPE_FULL_MASK(inner); + } + } ZEND_TYPE_FOREACH_END(); + + return result; +} + +zend_type zend_materialize_generic_types(zend_arena **arena, zend_type t, zend_class_reference *scope); + +static zend_result zend_resolve_relative_class(zend_arena **arena, + uint32_t fetch_type, zend_type *object) { + ZEND_ASSERT(ZEND_TYPE_HAS_PNR(*object)); + switch (fetch_type) { + case ZEND_FETCH_CLASS_PARENT: { + zend_class_reference *class_ref = zend_lookup_class_by_pnr( + ZEND_TYPE_PNR(*object), NULL, ZEND_FETCH_CLASS_SILENT); + if (!class_ref || !class_ref->ce->num_parents) { + return FAILURE; + } + zend_class_reference *parent = class_ref->ce->parents[0]; + if (parent->args.num_types == 0) { + ZEND_TYPE_SET_PNR(*object, ZEND_PNR_ENCODE_NAME(parent->ce->name)); + } else { + zend_name_reference *name_ref = zend_arena_alloc( + arena, ZEND_CLASS_REF_SIZE(parent->args.num_types)); + memcpy(name_ref, parent, ZEND_CLASS_REF_SIZE(parent->args.num_types)); + name_ref->name = parent->ce->name; + ZEND_TYPE_SET_PNR(*object, ZEND_PNR_ENCODE_REF(name_ref)); + *object = zend_materialize_generic_types(arena, *object, class_ref); + } + break; + } + case ZEND_FETCH_CLASS_STATIC: + return FAILURE; + case ZEND_FETCH_CLASS_DEFAULT: + case ZEND_FETCH_CLASS_SELF: + break; + default: + ZEND_UNREACHABLE(); + } + + return SUCCESS; +} + +zend_type zend_resolve_symbolic_type_symbolic(zend_arena **arena, zend_type type); + +/* Resolve a symbolic type in the form fcall to a concrete + * zend_type at runtime. Result is cacheable. */ +ZEND_API zend_type zend_resolve_symbolic_type(zend_arena **arena, zend_type type) +{ + if (ZEND_TYPE_IS_UNION(type)) { + zend_type_list *list = ZEND_TYPE_LIST(type); + zend_type *single_type; + uint32_t num_types = 0; + ZEND_TYPE_LIST_FOREACH(list, single_type) { + zend_type t = zend_resolve_symbolic_type(arena, *single_type); + if (!ZEND_TYPE_IS_COMPLEX(t)) { + ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(t); + } else { + list->types[num_types++] = t; + } + } ZEND_TYPE_LIST_FOREACH_END(); + list->num_types = num_types; + if (num_types == 0) { + ZEND_TYPE_FULL_MASK(type) = ZEND_TYPE_PURE_MASK(type); + } + return type; + } + + if (ZEND_TYPE_IS_SYMBOLIC(type)) { + uint32_t mask = ZEND_TYPE_PURE_MASK(type); + type = zend_resolve_symbolic_type_symbolic(arena, type); + ZEND_TYPE_FULL_MASK(type) |= mask; + } + + return type; +} + +zend_type zend_resolve_symbolic_type_symbolic(zend_arena **arena, zend_type type) +{ + zend_type_list *list = ZEND_TYPE_LIST(type); + uint32_t symtype = ZEND_TYPE_FULL_MASK(list->types[0]); + switch (symtype) { + case SYMTYPE_BINOP: { + zend_type a = zend_resolve_symbolic_type(arena, SYMBOLIC_BINOP_OP1(list)); + zend_type b = zend_resolve_symbolic_type(arena, SYMBOLIC_BINOP_OP2(list)); + uint32_t mask = ZEND_TYPE_FULL_MASK(a) | ZEND_TYPE_FULL_MASK(b); + switch (SYMBOLIC_BINOP_TYPE_MASK(list)) { + case BINOP_NUMBER: { + if (!(mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_BOOL | MAY_BE_NULL))) { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + } + break; + } + case BINOP_LONG: { + if (!(mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | + MAY_BE_BOOL | MAY_BE_NULL))) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + } + break; + } + case BINOP_NUMBER_ARRAY: { + switch (mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | + MAY_BE_BOOL | MAY_BE_NULL)) { + case 0: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + case MAY_BE_ARRAY: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY); + } + break; + } + case BINOP_LONG_STRING: { + switch (mask & + ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_BOOL | MAY_BE_NULL)) { + case 0: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + case MAY_BE_STRING: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_STRING); + } + break; + } + case BINOP_STRING: { + if (!(mask & ~(MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | + MAY_BE_BOOL | MAY_BE_NULL))) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_STRING); + } + break; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } + + /* Operator may be overloaded */ + a = zend_resolve_overloaded_type(a); + if (ZEND_TYPE_FULL_MASK(a)) { + return a; + } + b = zend_resolve_overloaded_type(b); + if (ZEND_TYPE_FULL_MASK(b)) { + return b; + } + + /* We could not prove the operator is overloaded: fallback to + * optimistic defaults */ + switch (SYMBOLIC_BINOP_TYPE_MASK(list)) { + case BINOP_NUMBER: + case BINOP_NUMBER_ARRAY: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG | MAY_BE_DOUBLE); + case BINOP_LONG: + case BINOP_LONG_STRING: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + case BINOP_STRING: + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_STRING); + EMPTY_SWITCH_DEFAULT_CASE(); + } + + ZEND_UNREACHABLE(); + } + case SYMTYPE_FCALL: { + // TODO: generic functions + zend_string *fname = SYMBOLIC_FCALL_FNAME(list); + zval *zfunc = zend_hash_find_known_hash(EG(function_table), fname); + if (!zfunc) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_function *func = Z_FUNC_P(zfunc); + if (!func->common.arg_info) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + return (func->common.arg_info - 1)->type; + } + case SYMTYPE_METHOD_CALL: + case SYMTYPE_STATIC_METHOD_CALL: { + zend_type object = SYMBOLIC_MCALL_OBJECT(list); + if (ZEND_TYPE_HAS_PNR(object) && zend_resolve_relative_class( + arena, SYMBOLIC_MCALL_FETCH_TYPE(list), + &object) == FAILURE) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_type result = {0}; + zend_type *object_elem; + ZEND_TYPE_FOREACH_EX(SYMBOLIC_MCALL_OBJECT(list), object_elem, ~(_ZEND_TYPE_INTERSECTION_BIT|_ZEND_TYPE_SYMBOLIC_BIT)) { + zend_type resolved = zend_resolve_symbolic_type(arena, *object_elem); + zend_type inner = {0}; + ZEND_TYPE_FOREACH(resolved, object_elem) { + if (!ZEND_TYPE_HAS_PNR(*object_elem)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_class_reference *class_ref; + + if (ZEND_TYPE_HAS_COMPLEX_PNR(resolved)) { + class_ref = zend_fetch_generic_class_by_ref(ZEND_PNR_COMPLEX_GET_REF(ZEND_TYPE_PNR(resolved)), NULL, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); + } else { + zend_string *cname = ZEND_TYPE_PNR_SIMPLE_NAME(resolved); + zend_class_entry *ce = zend_lookup_class_ex( + cname, NULL, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); + if (!ce) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + class_ref = ZEND_CE_TO_REF(ce); + } + if (!class_ref) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_string *fname = SYMBOLIC_MCALL_FNAME(list); + zend_function *func = zend_hash_find_ptr(&class_ref->ce->function_table, fname); + if (!func) { + if (symtype == SYMTYPE_METHOD_CALL && class_ref->ce->__call) { + func = class_ref->ce->__call; + } else if (symtype == SYMTYPE_STATIC_METHOD_CALL && class_ref->ce->__callstatic) { + func = class_ref->ce->__callstatic; + } + } + zend_type t; + if (!func || !func->common.arg_info) { + t = (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } else { + // TODO: scope/visibility? + t = (func->common.arg_info - 1)->type; + } + if (class_ref->ce->num_generic_params) { + t = zend_materialize_generic_types(arena, t, class_ref); + } + if (ZEND_TYPE_IS_INTERSECTION(resolved)) { + inner = zend_type_intersect(arena, inner, t); + } else { + inner = zend_type_union(arena, inner, t); + } + } ZEND_TYPE_FOREACH_END(); + if (ZEND_TYPE_IS_INTERSECTION(SYMBOLIC_MCALL_OBJECT(list))) { + result = zend_type_intersect(arena, result, inner); + } else { + result = zend_type_union(arena, result, inner); + } + } ZEND_TYPE_FOREACH_END(); + + return result; + } + case SYMTYPE_NEW: { + // TODO: generic classes + // TODO: relative class + zend_type object = zend_resolve_symbolic_type(arena, SYMBOLIC_NEW_OBJECT(list)); + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + return object; + } + case SYMTYPE_PROP: { + zend_type object = SYMBOLIC_PROP_OBJECT(list); + if (ZEND_TYPE_HAS_PNR(object) && zend_resolve_relative_class( + arena, SYMBOLIC_MCALL_FETCH_TYPE(list), + &object) == FAILURE) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + object = zend_resolve_symbolic_type(arena, object); + // TODO compound types + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_class_reference *class_ref = zend_lookup_class_by_pnr( + ZEND_TYPE_PNR(object), NULL, ZEND_FETCH_CLASS_SILENT); + if (!class_ref) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_string *pname = SYMBOLIC_PROP_PNAME(list); + zend_class_entry *prev_scope = EG(fake_scope); + EG(fake_scope) = class_ref->ce; // TODO + zend_property_info *prop_info = zend_get_property_info( + class_ref->ce, pname, 1); + EG(fake_scope) = prev_scope; + if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) { + if (ZEND_TYPE_IS_SET(prop_info->type)) { + // TODO: materialize is not always required + // (e.g. for $this->prop) + return zend_materialize_generic_types(arena, + prop_info->type, class_ref); + } + } + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + case SYMTYPE_CLASS: { + zend_type object = zend_resolve_symbolic_type(arena, SYMBOLIC_CLASS_OBJECT(list)); + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + switch (SYMBOLIC_CCONST_FETCH_TYPE(list)) { + case ZEND_FETCH_CLASS_DEFAULT: + case ZEND_FETCH_CLASS_SELF: + return object; + case ZEND_FETCH_CLASS_PARENT: { + zend_string *cname = ZEND_TYPE_PNR_SIMPLE_NAME(object); + zend_string *lcname = SYMBOLIC_CLASS_LCNAME(list); + zend_class_entry *ce = zend_lookup_class_ex(cname, lcname, + ZEND_FETCH_CLASS_SILENT); + if (!ce || !ce->num_parents) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + ce = ce->parents[0]->ce; + return (zend_type) ZEND_TYPE_INIT_PNR(ZEND_PNR_ENCODE_NAME(ce->name), 0, 0); + } + case ZEND_FETCH_CLASS_STATIC: + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + default: + ZEND_UNREACHABLE(); + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + } + case SYMTYPE_CLASS_CONSTANT: { + zend_type object = zend_resolve_symbolic_type(arena, SYMBOLIC_CCONST_OBJECT(list)); + if (!ZEND_TYPE_HAS_PNR(object)) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + zend_string *cname = ZEND_TYPE_PNR_SIMPLE_NAME(object); + zend_string *lcname = SYMBOLIC_CCONST_LCNAME(list); + // TODO: $this call + // TODO: relative call + // TODO: ::class + // TODO: AST + // TODO: enum + zend_class_entry *ce = zend_lookup_class_ex(cname, lcname, + ZEND_FETCH_CLASS_SILENT); + if (!ce) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + switch (SYMBOLIC_CCONST_FETCH_TYPE(list)) { + case ZEND_FETCH_CLASS_DEFAULT: + break; + case ZEND_FETCH_CLASS_SELF: + break; + case ZEND_FETCH_CLASS_PARENT: { + + if (!ce->num_parents) { + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + ce = ce->parents[0]->ce; + break; + } + case ZEND_FETCH_CLASS_STATIC: + // TODO: maybe conservatively use highest parent + // declaring the constant, if typed? + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + EMPTY_SWITCH_DEFAULT_CASE(); + } + + zend_string *constant_name = SYMBOLIC_CCONST_CNAME(list); + zval *zv = zend_hash_find_known_hash(CE_CONSTANTS_TABLE(ce), + constant_name); + if (!zv) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + zend_class_constant *constant = Z_PTR_P(zv); + if (ZEND_TYPE_IS_SET(constant->type)) { + return constant->type; + } + return _const_op_type(&constant->value); + } + case SYMTYPE_CONSTANT: { + zval *zv = zend_hash_find_known_hash(EG(zend_constants), + SYMBOLIC_CONST_KEY1(list)); + zend_constant *constant = NULL; + if (zv) { + constant = (zend_constant*)Z_PTR_P(zv); + } else if (SYMBOLIC_CONST_KEY2(list)) { + zv = zend_hash_find_known_hash(EG(zend_constants), SYMBOLIC_CONST_KEY2(list)); + if (zv) { + constant = (zend_constant*)Z_PTR_P(zv); + } + } + + if (!constant) { + return (zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + return _const_op_type(&constant->value); + } + case SYMTYPE_NOT_NULL: { + type = zend_resolve_symbolic_type(arena, SYMBOLIC_NOT_NULL_OP(list)); + ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; + return type; + } + EMPTY_SWITCH_DEFAULT_CASE(); + } +} diff --git a/Zend/zend_symbolic_inference.h b/Zend/zend_symbolic_inference.h new file mode 100644 index 0000000000000..8a5961977cd68 --- /dev/null +++ b/Zend/zend_symbolic_inference.h @@ -0,0 +1,13 @@ + +#ifndef ZEND_SYMBOLIC_INFERENCE_H +#define ZEND_SYMBOLIC_INFERENCE_H + +#include +#include +#include +#include + +ZEND_API zend_result zend_symbolic_inference(const zend_op_array *op_array, zend_arena **arena); +ZEND_API zend_type zend_resolve_symbolic_type(zend_arena **arena, zend_type type); + +#endif /* ZEND_SYMBOLIC_INFERENCE_H */ diff --git a/Zend/zend_type_tools.c b/Zend/zend_type_tools.c new file mode 100644 index 0000000000000..4061a182bf563 --- /dev/null +++ b/Zend/zend_type_tools.c @@ -0,0 +1,334 @@ + +#include "zend.h" +#include "zend_API.h" +#include "zend_alloc.h" +#include "zend_arena.h" +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_string.h" +#include "zend_types.h" +#include "zend_type_info.h" +#include "zend_bitset.h" + +void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); + +// TODO: dedup this function +static void zend_type_list_copy_ctor( + zend_type *const parent_type, + bool use_arena, + bool persistent +) { + const zend_type_list *const old_list = ZEND_TYPE_LIST(*parent_type); + size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); + zend_type_list *new_list = use_arena + ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); + + memcpy(new_list, old_list, size); + ZEND_TYPE_SET_LIST(*parent_type, new_list); + if (use_arena) { + ZEND_TYPE_FULL_MASK(*parent_type) |= _ZEND_TYPE_ARENA_BIT; + } + + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(new_list, list_type) { + zend_type_copy_ctor(list_type, use_arena, persistent); + } ZEND_TYPE_LIST_FOREACH_END(); +} + +// TODO: dedup this function +void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent) { + if (ZEND_TYPE_HAS_LIST(*type)) { + zend_type_list_copy_ctor(type, use_arena, persistent); + } else if (ZEND_TYPE_HAS_PNR(*type)) { + // TODO: Duplicate name reference... + zend_string_addref(ZEND_TYPE_PNR_NAME(*type)); + } +} + +#define REALLOC_INTERSECT_LIST() do { \ + if (!new_list) { \ + zend_type_list *old_list = ZEND_TYPE_IS_INTERSECTION(dest) ? ZEND_TYPE_LIST(dest) : NULL; \ + /* Worse case */ \ + size_t num_types = (old_list ? old_list->num_types : 1) \ + + (ZEND_TYPE_IS_INTERSECTION(src) ? ZEND_TYPE_LIST(src)->num_types : 1); \ + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); \ + if (old_list) { \ + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); \ + } else { \ + new_list->types[0] = dest; \ + new_list->num_types = 1; \ + } \ + dest.ptr = new_list; \ + dest.type_mask = ZEND_TYPE_PURE_MASK(dest) | _ZEND_TYPE_LIST_BIT|_ZEND_TYPE_INTERSECTION_BIT|_ZEND_TYPE_ARENA_BIT; \ + dest_elems = new_list->types; \ + } \ +} while (0) + +zend_type zend_type_intersect(zend_arena **arena, zend_type dest, zend_type src) +{ + if (ZEND_TYPE_PURE_MASK(dest) || !ZEND_TYPE_IS_COMPLEX(src)) { + return dest; + } + + if (ZEND_TYPE_IS_UNION(dest) || ZEND_TYPE_IS_UNION(src)) { + zend_type_list *new_list = NULL; + zend_type *src_elem; + ZEND_TYPE_FOREACH_EX(src, src_elem, ~_ZEND_TYPE_INTERSECTION_BIT) { + zend_type *dest_elems = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->types : &dest; + uint32_t num_types = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->num_types : 1; + for (uint32_t i = 0; i < num_types; i++) { + zend_type *dest_elem = &dest_elems[i]; + zend_type intersection = zend_type_intersect(arena, *dest_elem, *src_elem); + if (dest_elem->type_mask != intersection.type_mask + || (ZEND_TYPE_IS_COMPLEX(*dest_elem) && dest_elem->ptr != intersection.ptr)) { + bool add = true; + for (uint32_t j = 0; j < i; j++) { + if (zend_type_accepts(&intersection, NULL, &dest_elems[j], NULL, NULL)) { + add = false; + break; + } + } + if (add) { + if (!new_list) { + zend_type_list *old_list = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest) : NULL; + /* Worse case */ + size_t num_types = (old_list ? old_list->num_types : 1) + * (ZEND_TYPE_IS_UNION(src) ? ZEND_TYPE_LIST(src)->num_types : 1); + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); + if (old_list) { + memcpy(new_list, old_list, sizeof(zend_type)*i); + new_list->num_types = i; + } else { + new_list->num_types = 0; + } + dest.ptr = new_list; + dest.type_mask = ZEND_TYPE_PURE_MASK(dest) | _ZEND_TYPE_LIST_BIT|_ZEND_TYPE_UNION_BIT|_ZEND_TYPE_ARENA_BIT; + } + new_list->types[new_list->num_types++] = intersection; + } + } + } + } ZEND_TYPE_FOREACH_END(); + + return dest; + } + + zend_type_list *new_list = NULL; + zend_type *src_elem; + ZEND_TYPE_FOREACH(src, src_elem) { + bool add = true; + zend_type *dest_elems = ZEND_TYPE_HAS_LIST(dest) ? ZEND_TYPE_LIST(dest)->types : &dest; + uint32_t num_types = ZEND_TYPE_HAS_LIST(dest) ? ZEND_TYPE_LIST(dest)->num_types : 1; + for (uint32_t i = 0; i < num_types; i++) { + zend_type *dest_elem = &dest_elems[i]; + if (zend_type_accepts(src_elem, NULL, dest_elem, NULL, NULL)) { + REALLOC_INTERSECT_LIST(); + *dest_elem = *src_elem; + add = false; + } else if (zend_type_accepts(dest_elem, NULL, src_elem, NULL, NULL)) { + add = false; + break; + } + } + if (add) { + REALLOC_INTERSECT_LIST(); + new_list->types[new_list->num_types++] = *src_elem; + } + } ZEND_TYPE_FOREACH_END(); + + return dest; +} + +#define REALLOC_UNION_LIST() do { \ + if (!new_list) { \ + zend_type_list *old_list = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest) : NULL; \ + /* Worse case */ \ + size_t num_types = (old_list ? old_list->num_types : 1) \ + + (ZEND_TYPE_IS_UNION(src) ? ZEND_TYPE_LIST(src)->num_types : 1); \ + new_list = zend_arena_alloc(arena, ZEND_TYPE_LIST_SIZE(num_types)); \ + if (old_list) { \ + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); \ + } else { \ + new_list->types[0] = dest; \ + new_list->num_types = 1; \ + } \ + dest.ptr = new_list; \ + dest.type_mask = ZEND_TYPE_PURE_MASK(dest) | _ZEND_TYPE_LIST_BIT|_ZEND_TYPE_UNION_BIT|_ZEND_TYPE_ARENA_BIT; \ + dest_elems = new_list->types; \ + } \ +} while (0) + +zend_type zend_type_union(zend_arena **arena, zend_type dest, zend_type src) +{ + if (!ZEND_TYPE_IS_COMPLEX(dest) && !ZEND_TYPE_IS_COMPLEX(src)) { + ZEND_TYPE_FULL_MASK(dest) |= ZEND_TYPE_PURE_MASK(src); + return dest; + } + + if (!ZEND_TYPE_IS_COMPLEX(dest)) { + ZEND_ASSERT(ZEND_TYPE_IS_COMPLEX(src)); + uint32_t mask = ZEND_TYPE_PURE_MASK(dest); + dest = src; + ZEND_TYPE_FULL_MASK(dest) |= mask; + return dest; + } + + zend_type_list *new_list = NULL; + zend_type *src_elem; + ZEND_TYPE_FOREACH_EX(src, src_elem, ~_ZEND_TYPE_INTERSECTION_BIT) { + bool add = true; + zend_type *dest_elems = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->types : &dest; + uint32_t num_types = ZEND_TYPE_IS_UNION(dest) ? ZEND_TYPE_LIST(dest)->num_types : 1; + for (uint32_t i = 0; i < num_types; i++) { + zend_type *dest_elem = &dest_elems[i]; + if (zend_type_accepts(src_elem, NULL, dest_elem, NULL, NULL)) { + REALLOC_UNION_LIST(); + *dest_elem = *src_elem; + add = false; + } else if (zend_type_accepts(dest_elem, NULL, src_elem, NULL, NULL)) { + add = false; + break; + } + } + if (add) { + REALLOC_UNION_LIST(); + if (!ZEND_TYPE_IS_COMPLEX(*src_elem)) { + ZEND_TYPE_FULL_MASK(dest) |= ZEND_TYPE_PURE_MASK(*src_elem); + } else { + new_list->types[new_list->num_types++] = *src_elem; + } + } + } ZEND_TYPE_FOREACH_END(); + + if (new_list && new_list->num_types == 0) { + ZEND_TYPE_SET_PTR(dest, NULL); + ZEND_TYPE_FULL_MASK(dest) = ZEND_TYPE_PURE_MASK(dest); + } + + return dest; +} + +zend_type zend_type_normalize_union_in_place(zend_type t) +{ + ZEND_ASSERT(ZEND_TYPE_IS_UNION(t)); + + zend_type_list *list = ZEND_TYPE_LIST(t); + zend_type *a = list->types; + zend_type *b; + zend_type *last = a + list->num_types - 1; + + while (a <= last) { +restart: + if (!ZEND_TYPE_IS_COMPLEX(*a)) { + ZEND_TYPE_FULL_MASK(t) |= ZEND_TYPE_PURE_MASK(*a); + // Remove a + if (a != last) { + *a = *last; + } + last--; + continue; + } + b = &list->types[0]; + while (b <= last && b < a) { + if (zend_type_accepts(b, NULL, a, NULL, NULL)) { + // Remove a + if (a != last) { + *a = *last; + last--; + goto restart; + } else { + last--; + break; + } + } else if (zend_type_accepts(a, NULL, b, NULL, NULL)) { + // Remove b + if (b != last) { + *b = *last; + } + if (a == last) { + b++; + } + last--; + continue; + } + b++; + } + a++; + } + + uint32_t num_types = last+1-list->types; + if (num_types == 0) { + ZEND_TYPE_FULL_MASK(t) &= ~(_ZEND_TYPE_LIST_BIT | _ZEND_TYPE_UNION_BIT | _ZEND_TYPE_ARENA_BIT); + } else if (num_types == 1) { + uint32_t mask = ZEND_TYPE_PURE_MASK(t); + t = list->types[0]; + ZEND_TYPE_FULL_MASK(t) |= mask; + } else { + list->num_types = num_types; + } + + return t; +} + +zend_type zend_type_normalize_intersection_in_place(zend_type t) +{ + ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(t)); + + zend_type_list *list = ZEND_TYPE_LIST(t); + zend_type *a = list->types; + zend_type *b; + zend_type *last = a + list->num_types - 1; + + while (a <= last) { +restart: + if (!ZEND_TYPE_IS_COMPLEX(*a)) { + ZEND_TYPE_FULL_MASK(t) |= ZEND_TYPE_PURE_MASK(*a); + // Remove a + if (a != last) { + *a = *last; + } + last--; + continue; + } + b = &list->types[0]; + while (b <= last && b < a) { + if (zend_type_accepts(a, NULL, b, NULL, NULL)) { + // Remove a + if (a != last) { + *a = *last; + last--; + goto restart; + } else { + last--; + break; + } + } else if (zend_type_accepts(b, NULL, a, NULL, NULL)) { + // Remove b + if (b != last) { + *b = *last; + } + if (a == last) { + b++; + } + last--; + continue; + } + b++; + } + a++; + } + + uint32_t num_types = last+1-list->types; + if (num_types == 0) { + ZEND_TYPE_FULL_MASK(t) &= ~(_ZEND_TYPE_LIST_BIT | _ZEND_TYPE_INTERSECTION_BIT | _ZEND_TYPE_ARENA_BIT); + } else if (num_types == 1) { + uint32_t mask = ZEND_TYPE_PURE_MASK(t); + t = list->types[0]; + ZEND_TYPE_FULL_MASK(t) |= mask; + } else { + list->num_types = num_types; + } + + return t; +} + diff --git a/Zend/zend_type_tools.h b/Zend/zend_type_tools.h new file mode 100644 index 0000000000000..138b8fb139819 --- /dev/null +++ b/Zend/zend_type_tools.h @@ -0,0 +1,14 @@ +#ifndef _ZEND_TYPE_TOOLS_H_ +#define _ZEND_TYPE_TOOLS_H_ + +#include "zend.h" +#include "zend_arena.h" +#include "zend_types.h" + +void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); +zend_type zend_type_intersect(zend_arena **arena, zend_type dest, zend_type src); +zend_type zend_type_union(zend_arena **arena, zend_type dest, zend_type src); +zend_type zend_type_normalize_union_in_place(zend_type t); +zend_type zend_type_normalize_intersection_in_place(zend_type t); + +#endif /* _ZEND_TYPE_TOOLS_H_ */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 228e2ab82df54..340526a1b8501 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -214,9 +214,6 @@ typedef struct { #define ZEND_TYPE_USES_ARENA(t) \ ((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0) -#define ZEND_TYPE_IS_ONLY_MASK(t) \ - (ZEND_TYPE_IS_SET(t) && (t).ptr == NULL) - #define ZEND_TYPE_PNR(t) \ ((zend_packed_name_reference) (t).ptr) @@ -288,6 +285,23 @@ typedef struct { } while (++_cur < _end); \ } while (0) +/* Same as ZEND_TYPE_FOREACH, but visit the list only if + * (type.type_mask & mask) is non zero. */ +#define ZEND_TYPE_FOREACH_EX(type, type_ptr, mask) do { \ + zend_type *_cur, *_end; \ + uint32_t _mask = (mask); \ + zend_type _type = (type); \ + if (ZEND_TYPE_HAS_LIST(_type) && !(ZEND_TYPE_FULL_MASK(_type) & ~_mask)) { \ + zend_type_list *_list = ZEND_TYPE_LIST(_type); \ + _cur = _list->types; \ + _end = _cur + _list->num_types; \ + } else { \ + _cur = &_type; \ + _end = _cur + 1; \ + } \ + do { \ + type_ptr = _cur; + #define ZEND_TYPE_SET_PTR(t, _ptr) \ ((t).ptr = (_ptr)) @@ -776,7 +790,7 @@ struct _zend_ast_ref { #define IS_INDIRECT 12 #define IS_PTR 13 #define IS_ALIAS_PTR 14 -#define IS_PNR 15 +#define IS_PNR 15 /* TODO: reuse same bit as _ZEND_TYPE_PNR_BIT? */ #define _IS_ERROR 16 /* used for casts */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f3fedaddf6d6f..0eca57c0e141c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5785,15 +5785,24 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N HANDLE_EXCEPTION(); } if (UNEXPECTED(ce->num_generic_params != 0)) { - zend_name_reference ref = { - .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), - .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), - }; - class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference( + ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), + types, Z_TYPE(EX(This)) == IS_OBJECT ? Z_OBJCR(EX(This)) : NULL); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + efree(ref); /* members are owned by class_ref */ } else { class_ref = ZEND_CE_TO_REF(ce); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ea81dcc355463..9f8c5b7790771 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -10536,15 +10536,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( HANDLE_EXCEPTION(); } if (UNEXPECTED(ce->num_generic_params != 0)) { - zend_name_reference ref = { - .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), - .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), - }; - class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference(ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), types); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + efree(ref); /* members are owned by class_ref */ } else { class_ref = ZEND_CE_TO_REF(ce); } @@ -29717,15 +29724,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE HANDLE_EXCEPTION(); } if (UNEXPECTED(ce->num_generic_params != 0)) { - zend_name_reference ref = { - .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), - .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), - }; - class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference(ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), types); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + efree(ref); /* members are owned by class_ref */ } else { class_ref = ZEND_CE_TO_REF(ce); } @@ -36908,15 +36922,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER HANDLE_EXCEPTION(); } if (UNEXPECTED(ce->num_generic_params != 0)) { - zend_name_reference ref = { - .name = ZEND_PNR_SIMPLE_GET_NAME(pnr), - .key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), - }; - class_ref = zend_fetch_generic_class_by_ref(&ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + zval *ztypes = RT_CONSTANT(opline, opline->op1) + 2; + ZEND_ASSERT(Z_TYPE_P(ztypes) != IS_NULL); + zend_type_list *types = Z_PTR_P(ztypes); + zend_name_reference *ref = zend_infer_instantiation_name_reference(ce, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), types); + if (UNEXPECTED(EG(exception))) { + zend_name_reference_release(ref, false, false); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } + class_ref = zend_fetch_generic_class_by_ref(ref, Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(class_ref == NULL)) { + zend_name_reference_release(ref, false, false); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); } + efree(ref); /* members are owned by class_ref */ } else { class_ref = ZEND_CE_TO_REF(ce); } diff --git a/configure.ac b/configure.ac index 66bdf0db3f2a9..b4027157e8f24 100644 --- a/configure.ac +++ b/configure.ac @@ -1746,6 +1746,8 @@ PHP_ADD_SOURCES(Zend, \ zend_observer.c zend_system_id.c zend_enum.c zend_fibers.c zend_atomic.c \ zend_max_execution_timer.c \ zend_hrtime.c \ + zend_type_tools.c \ + zend_symbolic_inference.c \ Optimizer/zend_optimizer.c \ Optimizer/pass1.c \ Optimizer/pass3.c \ @@ -1767,6 +1769,7 @@ PHP_ADD_SOURCES(Zend, \ Optimizer/escape_analysis.c \ Optimizer/compact_vars.c \ Optimizer/zend_dump.c \ + Optimizer/zend_symbolic_inference.c \ Optimizer/zend_scc.c \ , -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index ff2b37ae09f0b..de9d2d435ede8 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -323,7 +323,7 @@ static HashTable *gmp_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */ { gmp_object *old_object = GET_GMP_OBJECT_FROM_OBJ(obj); - gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(obj->ce)); + gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(OBJ_CE(obj))); zend_objects_clone_members( &new_object->std, &old_object->std); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 85c2349976503..51284d7744c00 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -18,6 +18,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_types.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -3087,7 +3088,8 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin) } /* Treat "static" as a class type for the purposes of reflection. */ - RETVAL_BOOL(ZEND_TYPE_IS_ONLY_MASK(param->type) + RETVAL_BOOL(!ZEND_TYPE_IS_COMPLEX(param->type) + && ZEND_TYPE_FULL_MASK(param->type) && !(ZEND_TYPE_FULL_MASK(param->type) & MAY_BE_STATIC)); } /* }}} */ diff --git a/ext/standard/var.c b/ext/standard/var.c index 79b300aae3dbe..b922e1db0cbe5 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -23,11 +23,14 @@ #include "php.h" #include "php_string.h" #include "php_var.h" +#include "zend_compile.h" #include "zend_smart_str.h" #include "basic_functions.h" #include "php_incomplete_class.h" #include "zend_enum.h" #include "zend_exceptions.h" +#include "zend_string.h" +#include "zend_types.h" /* }}} */ struct php_serialize_data { @@ -163,8 +166,27 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG); class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc)); - php_printf("%sobject(%s)#%d (%d) {\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0); + + zend_string *types = zend_empty_string; + if (Z_OBJ_P(struc)->cr->args.num_types) { + zend_type_list *args = &Z_OBJ_P(struc)->cr->args; + smart_str str = {0}; + smart_str_appendc(&str, '<'); + for (uint32_t i = 0; i < args->num_types; i++) { + if (i > 0) { + smart_str_appendc(&str, ','); + } + zend_string *t = zend_type_to_string(args->types[i], NULL); + smart_str_append(&str, t); + zend_string_release(t); + } + smart_str_appendc(&str, '>'); + types = smart_str_extract(&str); + } + + php_printf("%sobject(%s%s)#%d (%d) {\n", COMMON, ZSTR_VAL(class_name), ZSTR_VAL(types), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0); zend_string_release_ex(class_name, 0); + zend_string_release(types); if (myht) { zend_ulong num;