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) {