diff --git a/Zend/tests/type_declarations/typed_properties_001.phpt b/Zend/tests/type_declarations/typed_properties_001.phpt new file mode 100644 index 0000000000000..5a346a4c3e80a --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +Test typed properties basic operation +--FILE-- +int = $int; + $this->float = $float; + $this->bool = $bool; + $this->array = $array; + $this->std = $std; + } +}); +?> +--EXPECTF-- +object(class@anonymous)#%d (5) { + ["int"]=> + int(1) + ["float"]=> + float(2.2) + ["bool"]=> + bool(true) + ["array"]=> + array(1) { + [0]=> + string(4) "four" + } + ["std"]=> + object(stdClass)#%d (0) { + } +} + diff --git a/Zend/tests/type_declarations/typed_properties_002.phpt b/Zend/tests/type_declarations/typed_properties_002.phpt new file mode 100644 index 0000000000000..facaee077e0b4 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_002.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test typed properties error condition (read uninitialized) +--FILE-- +int); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$int must not be accessed before initialization in %s:6 +Stack trace: +#0 {main} + thrown in %s on line 6 + + diff --git a/Zend/tests/type_declarations/typed_properties_003.phpt b/Zend/tests/type_declarations/typed_properties_003.phpt new file mode 100644 index 0000000000000..cf85fc4093120 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test typed properties error condition (fetch reference) +--FILE-- +int; +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$int must not be referenced in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d + diff --git a/Zend/tests/type_declarations/typed_properties_004.phpt b/Zend/tests/type_declarations/typed_properties_004.phpt new file mode 100644 index 0000000000000..1d9e6400e00fd --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_004.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test typed properties error condition (type mismatch) +--FILE-- +int = $string; + } +}; +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$int must be integer, string used in %s:6 +Stack trace: +#0 %s(2): class@anonymous->__construct('PHP 7 is better...') +#1 {main} + thrown in %s on line 6 + + diff --git a/Zend/tests/type_declarations/typed_properties_005.phpt b/Zend/tests/type_declarations/typed_properties_005.phpt new file mode 100644 index 0000000000000..b521963000d52 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_005.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test typed properties error condition (type mismatch object) +--FILE-- +std = $dummy; + } +}; +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$std must be an instance of stdClass, Dummy used in %s:8 +Stack trace: +#0 %s(4): class@anonymous->__construct(Object(Dummy)) +#1 {main} + thrown in %s on line 8 diff --git a/Zend/tests/type_declarations/typed_properties_006.phpt b/Zend/tests/type_declarations/typed_properties_006.phpt new file mode 100644 index 0000000000000..2e775395c43e7 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_006.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test typed properties inheritance (scalar) +--FILE-- + +--EXPECTF-- +Fatal error: Type of Bar::$qux must be integer (as in class Foo) in %s on line 8 + diff --git a/Zend/tests/type_declarations/typed_properties_007.phpt b/Zend/tests/type_declarations/typed_properties_007.phpt new file mode 100644 index 0000000000000..a0cd72268d11a --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_007.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test typed properties inheritance +--FILE-- + +--EXPECTF-- +Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 11 + + diff --git a/Zend/tests/type_declarations/typed_properties_008.phpt b/Zend/tests/type_declarations/typed_properties_008.phpt new file mode 100644 index 0000000000000..0efff906fa836 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_008.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test typed properties inheritance (missing info) +--FILE-- + +--EXPECTF-- +Fatal error: Type of Bar::$qux must be integer (as in class Foo) in %s on line 8 + + diff --git a/Zend/tests/type_declarations/typed_properties_009.phpt b/Zend/tests/type_declarations/typed_properties_009.phpt new file mode 100644 index 0000000000000..83ccfcf515e4c --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_009.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test typed properties unset leaves properties in an uninitialized state +--FILE-- +bar); + +var_dump($foo->bar); +?> +--EXPECTF-- +string(3) "bar" +int(0) diff --git a/Zend/tests/type_declarations/typed_properties_010.phpt b/Zend/tests/type_declarations/typed_properties_010.phpt new file mode 100644 index 0000000000000..3ef8eceb5f15f --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_010.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test typed properties disallow fetch reference for func arg +--FILE-- +bar); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property Foo::$bar must not be referenced in %s:11 +Stack trace: +#0 {main} + thrown in %s on line 11 + + diff --git a/Zend/tests/type_declarations/typed_properties_011.phpt b/Zend/tests/type_declarations/typed_properties_011.phpt new file mode 100644 index 0000000000000..5f3dbdd52ae44 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_011.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test typed properties disallow fetch reference for init array +--FILE-- +bar]; +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property Foo::$bar must not be referenced in %s:8 +Stack trace: +#0 {main} + thrown in %s on line 8 + + diff --git a/Zend/tests/type_declarations/typed_properties_012.phpt b/Zend/tests/type_declarations/typed_properties_012.phpt new file mode 100644 index 0000000000000..185dc3e961469 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_012.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test typed properties disallow fetch reference for foreach +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed properties exist in Foo: foreach by reference is disallowed in %s:7 +Stack trace: +#0 {main} + thrown in %s on line 7 + + diff --git a/Zend/tests/type_declarations/typed_properties_013.phpt b/Zend/tests/type_declarations/typed_properties_013.phpt new file mode 100644 index 0000000000000..177200ada7e39 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_013.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test typed properties disallow incorrect type initial value (scalar) +--FILE-- + +--EXPECTF-- +Fatal error: Default value for properties with integer type can only be integer in %s on line 3 + + + + diff --git a/Zend/tests/type_declarations/typed_properties_014.phpt b/Zend/tests/type_declarations/typed_properties_014.phpt new file mode 100644 index 0000000000000..48486078afb60 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_014.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test typed properties disallow incorrect type initial value (array) +--FILE-- + +--EXPECTF-- +Fatal error: Default value for properties with array type can only be an array in %s on line 3 + + + diff --git a/Zend/tests/type_declarations/typed_properties_015.phpt b/Zend/tests/type_declarations/typed_properties_015.phpt new file mode 100644 index 0000000000000..db880ceed0c78 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_015.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test typed properties disallow incorrect type initial value (object) +--FILE-- + +--EXPECTF-- +Fatal error: Default value for properties with class type are disallowed in %s on line 3 + + + + diff --git a/Zend/tests/type_declarations/typed_properties_016.phpt b/Zend/tests/type_declarations/typed_properties_016.phpt new file mode 100644 index 0000000000000..28cd239d0434f --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_016.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test typed properties initial values +--FILE-- + +--EXPECT-- +ok + + + diff --git a/Zend/tests/type_declarations/typed_properties_017.phpt b/Zend/tests/type_declarations/typed_properties_017.phpt new file mode 100644 index 0000000000000..23ddb4adf571a --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_017.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test typed properties disallow void +--FILE-- + +--EXPECTF-- +Fatal error: Typed property Foo::$int must not be void in %s on line 3 + + + + diff --git a/Zend/tests/type_declarations/typed_properties_018.phpt b/Zend/tests/type_declarations/typed_properties_018.phpt new file mode 100644 index 0000000000000..54fd38dc91648 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_018.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test typed properties type applies to all props in group +--FILE-- +getProperty("qux"); + +var_dump((string) $prop->getType()); +?> +--EXPECTF-- +string(3) "int" + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_019.phpt b/Zend/tests/type_declarations/typed_properties_019.phpt new file mode 100644 index 0000000000000..e9149e1c1c79f --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_019.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test typed properties int must not be allowed to overflow +--FILE-- +bar; + } +} + +$foo = new Foo(); + +var_dump($foo->inc()); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property Foo::$bar must be integer, float used in %s:6 +Stack trace: +#0 %s(12): Foo->inc() +#1 {main} + thrown in %s on line 6 + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_020.phpt b/Zend/tests/type_declarations/typed_properties_020.phpt new file mode 100644 index 0000000000000..03fdd714b536b --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_020.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test typed properties binary assign op helper test +--FILE-- +bar += 2; + } +} + +$foo = new Foo(); + +var_dump($foo->bar); +?> +--EXPECT-- +int(2) + + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_021.phpt b/Zend/tests/type_declarations/typed_properties_021.phpt new file mode 100644 index 0000000000000..79a5d70098e41 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_021.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test typed properties delay type check on constant +--FILE-- + +--EXPECTF-- +Fatal error: Class 'BAR' not found in %s on line 6 + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_022.phpt b/Zend/tests/type_declarations/typed_properties_022.phpt new file mode 100644 index 0000000000000..1aa23f301fdfd --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_022.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test typed properties delay type check on ast +--FILE-- + +--EXPECTF-- +Fatal error: Class 'BAR' not found in %s on line 6 + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_023.phpt b/Zend/tests/type_declarations/typed_properties_023.phpt new file mode 100644 index 0000000000000..ce6aa53c13876 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_023.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test typed properties disallow static +--FILE-- + +--EXPECTF-- +Fatal error: Typed property Foo::$thing must not be static in %s on line 3 + + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_024.phpt b/Zend/tests/type_declarations/typed_properties_024.phpt new file mode 100644 index 0000000000000..d38fe249534b2 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_024.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test typed properties ignore private props during inheritance +--FILE-- + +--EXPECT-- +ok + + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_025.phpt b/Zend/tests/type_declarations/typed_properties_025.phpt new file mode 100644 index 0000000000000..70156de35b8f6 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_025.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test typed properties type must preceed first declaration in group +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected 'int' (T_STRING), expecting variable (T_VARIABLE) in %s on line 4 + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_026.phpt b/Zend/tests/type_declarations/typed_properties_026.phpt new file mode 100644 index 0000000000000..250ea060508c3 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_026.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test typed properties inherit traits with typed properties +--FILE-- +baz; + } +} + +var_dump((new Baz)->get()); +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property Baz::$baz must not be accessed before initialization in %s:10 +Stack trace: +#0 %s(14): Baz->get() +#1 {main} + thrown in %s on line 10 + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_027.phpt b/Zend/tests/type_declarations/typed_properties_027.phpt new file mode 100644 index 0000000000000..03ce2f15893e5 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_027.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test typed properties float widen at runtime +--FILE-- +bar = 10; + +var_dump($foo->bar); +?> +--EXPECTF-- +float(10) + + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_028.phpt b/Zend/tests/type_declarations/typed_properties_028.phpt new file mode 100644 index 0000000000000..de1b3f660232a --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_028.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test typed properties respect strict types (off) +--FILE-- +bar = "1"; + +var_dump($foo->bar); +?> +--EXPECT-- +int(1) diff --git a/Zend/tests/type_declarations/typed_properties_029.phpt b/Zend/tests/type_declarations/typed_properties_029.phpt new file mode 100644 index 0000000000000..9e27d74d97bd6 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_029.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test typed properties respect strict types (on) +--FILE-- +bar = "1"; +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property Foo::$bar must be integer, string used in %s:9 +Stack trace: +#0 {main} + thrown in %s on line 9 diff --git a/Zend/tests/type_declarations/typed_properties_030.phpt b/Zend/tests/type_declarations/typed_properties_030.phpt new file mode 100644 index 0000000000000..2749948a6839b --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_030.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test typed properties unset __get magical magic +--FILE-- +bar = "1"; # ok + +unset($foo->bar); # ok + +var_dump($foo->bar); # not okay, __get is nasty +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property Foo::$bar must be integer, string used in %s:16 +Stack trace: +#0 {main} + thrown in %s on line 16 diff --git a/Zend/tests/type_declarations/typed_properties_031.phpt b/Zend/tests/type_declarations/typed_properties_031.phpt new file mode 100644 index 0000000000000..8022d2ef72bca --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_031.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test typed properties coerce int to float even in strict mode +--FILE-- +bar = $value; + } +} + +$bar = new Bar(); + +$bar->setBar(100); + +var_dump($bar->bar); +--EXPECT-- +float(100) diff --git a/Zend/tests/type_declarations/typed_properties_032.phpt b/Zend/tests/type_declarations/typed_properties_032.phpt new file mode 100644 index 0000000000000..84b2823fb1cb6 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_032.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test typed properties return by ref is disallowed +--FILE-- +bar; + } +}; + +var_dump($foo->method()); +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$bar must not be referenced in %s:7 +Stack trace: +#0 %s(11): class@anonymous->method() +#1 {main} + thrown in %s on line 7 + diff --git a/Zend/tests/type_declarations/typed_properties_033.phpt b/Zend/tests/type_declarations/typed_properties_033.phpt new file mode 100644 index 0000000000000..11eaa1ac8bd1e --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_033.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test typed properties yield reference guard +--FILE-- +bar]; + } +}; + +foreach ($foo->fetch() as $prop); +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$bar must not be referenced in %s:6 +Stack trace: +#0 %s(10): class@anonymous->fetch() +#1 {main} + thrown in %s on line 6 diff --git a/Zend/tests/type_declarations/typed_properties_034.phpt b/Zend/tests/type_declarations/typed_properties_034.phpt new file mode 100644 index 0000000000000..dc6d58170d000 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_034.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test typed properties add array element guard +--FILE-- +bar] as $item) + { + yield $item; + } + } +}; + +foreach($foo->getIterator() as $item); +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property class@anonymous::$bar must not be referenced in %s:8 +Stack trace: +#0 %s(15): class@anonymous->getIterator() +#1 {main} + thrown in %s on line 8 diff --git a/Zend/tests/type_declarations/typed_properties_035.phpt b/Zend/tests/type_declarations/typed_properties_035.phpt new file mode 100644 index 0000000000000..3ad55e2bcf43e --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_035.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test typed properties inheritance must not change type +--FILE-- + $bar) { + var_dump($key, $bar); +} +?> +--EXPECTF-- +string(3) "bar" +int(10) diff --git a/Zend/tests/type_declarations/typed_properties_037.phpt b/Zend/tests/type_declarations/typed_properties_037.phpt new file mode 100644 index 0000000000000..8bdd6748b2e96 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_037.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test typed properties var_dump uninitialized +--FILE-- + + int(10) + ["qux"]=> + uninitialized(integer) +} + diff --git a/Zend/tests/type_declarations/typed_properties_038.phpt b/Zend/tests/type_declarations/typed_properties_038.phpt new file mode 100644 index 0000000000000..d25d2fc1a9061 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_038.phpt @@ -0,0 +1,61 @@ +--TEST-- +Test typed properties overflowing +--FILE-- +bar++; +} catch(TypeError $t) { + var_dump($t->getMessage()); +} + +var_dump($foo); + +try { + $foo->bar += 1; +} catch(TypeError $t) { + var_dump($t->getMessage()); +} + +var_dump($foo); + +try { + ++$foo->bar; +} catch(TypeError $t) { + var_dump($t->getMessage()); +} + +var_dump($foo); + +try { + $foo->bar = $foo->bar + 1; +} catch(TypeError $t) { + var_dump($t->getMessage()); +} + +var_dump($foo); +--EXPECTF-- +string(64) "Typed property class@anonymous::$bar must be integer, float used" +object(class@anonymous)#%d (%d) { + ["bar"]=> + int(%d) +} +string(64) "Typed property class@anonymous::$bar must be integer, float used" +object(class@anonymous)#%d (%d) { + ["bar"]=> + int(%d) +} +string(64) "Typed property class@anonymous::$bar must be integer, float used" +object(class@anonymous)#%d (%d) { + ["bar"]=> + int(%d) +} +string(64) "Typed property class@anonymous::$bar must be integer, float used" +object(class@anonymous)#%d (%d) { + ["bar"]=> + int(%d) +} diff --git a/Zend/tests/type_declarations/typed_properties_039.phpt b/Zend/tests/type_declarations/typed_properties_039.phpt new file mode 100644 index 0000000000000..2b2513bd925b8 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_039.phpt @@ -0,0 +1,31 @@ +--TEST-- +Repeated assign of a variable to mismatched property type must not succeed +--FILE-- +foo = $v; + $v = new A; + $obj = new B; + $obj->foo = $v; +} + +var_dump($objs); + +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Typed property A::$foo must be integer, A used in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/type_declarations/typed_properties_040.phpt b/Zend/tests/type_declarations/typed_properties_040.phpt new file mode 100644 index 0000000000000..075fbb1eacfa5 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_040.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test __get on unset typed property must fail properly +--FILE-- +bar); +?> +--EXPECTF-- +string(3) "bar" + +Fatal error: Uncaught TypeError: Typed property Foo::$bar must be integer, null used in %s:14 +Stack trace: +#0 {main} + thrown in %s on line 14 + diff --git a/Zend/tests/type_declarations/typed_properties_041.phpt b/Zend/tests/type_declarations/typed_properties_041.phpt new file mode 100644 index 0000000000000..8e8c856ee3099 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_041.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test typed properties weak conversion of strings +--FILE-- +bar = "10"; + +var_dump($foo->bar); +?> +--EXPECTF-- +int(10) + + + + + + + diff --git a/Zend/tests/type_declarations/typed_properties_042.phpt b/Zend/tests/type_declarations/typed_properties_042.phpt new file mode 100644 index 0000000000000..0d1f45943aac5 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_042.phpt @@ -0,0 +1,25 @@ +--TEST-- +Proper source duplication on assignment to typed property +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.protect_memory=1 +--FILE-- +bar = "5"; + var_dump($foo->bar); +} +?> +--EXPECT-- +int(5) +int(5) +int(5) +int(5) +int(5) diff --git a/Zend/tests/type_declarations/typed_properties_043.phpt b/Zend/tests/type_declarations/typed_properties_043.phpt new file mode 100644 index 0000000000000..cd4e680ef641a --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_043.phpt @@ -0,0 +1,24 @@ +--TEST-- +Proper source duplication on assignment to typed property +--INI-- +opcache.enable=0 +opcache.enable_cli=0 +--FILE-- +bar = "5"; + var_dump($foo->bar); +} +?> +--EXPECT-- +int(5) +int(5) +int(5) +int(5) +int(5) diff --git a/Zend/tests/type_declarations/typed_properties_044.phpt b/Zend/tests/type_declarations/typed_properties_044.phpt new file mode 100644 index 0000000000000..3dc96e25166c7 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_044.phpt @@ -0,0 +1,27 @@ +--TEST-- +Error message should be the same independently of called function +--FILE-- +bar); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} +try { + $f($foo->bar); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} +--EXPECT-- +Typed property Foo::$bar must not be referenced +Typed property Foo::$bar must not be referenced diff --git a/Zend/tests/type_declarations/typed_properties_045.phpt b/Zend/tests/type_declarations/typed_properties_045.phpt new file mode 100644 index 0000000000000..6218564bf88b3 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_045.phpt @@ -0,0 +1,15 @@ +--TEST-- +foreach() leaks on object with typed properties +--FILE-- +getMessage() . "\n"; +} +--EXPECT-- +Typed properties exist in Foo: foreach by reference is disallowed diff --git a/Zend/tests/type_declarations/typed_properties_046.phpt b/Zend/tests/type_declarations/typed_properties_046.phpt new file mode 100644 index 0000000000000..8c160a98e75b9 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_046.phpt @@ -0,0 +1,29 @@ +--TEST-- +Memory leaks on wrong assignment to typed roperty +--FILE-- +{bar()} = str_repeat("a", 3); + } catch (Throwable $e) { + echo $e->getMessage() . "\n"; + } +} +--EXPECT-- +Typed property Foo::$bbb must be integer, string used +Typed property Foo::$bbb must be integer, string used +Typed property Foo::$bbb must be integer, string used +Typed property Foo::$bbb must be integer, string used +Typed property Foo::$bbb must be integer, string used diff --git a/Zend/tests/type_declarations/typed_properties_047.phpt b/Zend/tests/type_declarations/typed_properties_047.phpt new file mode 100644 index 0000000000000..fdd547333a6ab --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_047.phpt @@ -0,0 +1,60 @@ +--TEST-- +Nullable typed property +--FILE-- +foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = 5; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = null; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + unset($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = "ops"; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECTF-- +object(Foo)#1 (1) { + ["foo"]=> + NULL +} +NULL +int(5) +NULL +Typed property Foo::$foo must not be accessed before initialization +Typed property Foo::$foo must be integer, string used diff --git a/Zend/tests/type_declarations/typed_properties_048.phpt b/Zend/tests/type_declarations/typed_properties_048.phpt new file mode 100644 index 0000000000000..59ca7e3bd427c --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_048.phpt @@ -0,0 +1,60 @@ +--TEST-- +Nullable typed property +--FILE-- +foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = 5; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = null; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + unset($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = "ops"; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECTF-- +object(Foo)#1 (1) { + ["foo"]=> + NULL +} +NULL +int(5) +NULL +Typed property Foo::$foo must not be accessed before initialization +Typed property Foo::$foo must be integer, string used diff --git a/Zend/tests/type_declarations/typed_properties_049.phpt b/Zend/tests/type_declarations/typed_properties_049.phpt new file mode 100644 index 0000000000000..2e0b068f3beef --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_049.phpt @@ -0,0 +1,52 @@ +--TEST-- +Nullable typed property +--FILE-- +foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = 5; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = null; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + unset($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + var_dump($x->foo); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +try { + $x->foo = "ops"; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECTF-- +Fatal error: Default value for properties with integer type can only be integer in %styped_properties_049.php on line 3 diff --git a/Zend/tests/type_declarations/typed_properties_050.phpt b/Zend/tests/type_declarations/typed_properties_050.phpt new file mode 100644 index 0000000000000..3fa826083e131 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_050.phpt @@ -0,0 +1,19 @@ +--TEST-- +Weak casts must not overwrite source variables +--FILE-- +a = $b; +var_dump($o, $a); + +?> +--EXPECT-- +object(A)#1 (1) { + ["a"]=> + int(1) +} +string(1) "1" diff --git a/Zend/tests/type_declarations/typed_properties_051.phpt b/Zend/tests/type_declarations/typed_properties_051.phpt new file mode 100644 index 0000000000000..1c8673d0d3d44 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_051.phpt @@ -0,0 +1,27 @@ +--TEST-- +Weak casts must not leak +--FILE-- +a = new B; +var_dump($o->a); +try { + $o->a = new C; +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECT-- +string(4) "okok" +Typed property A::$a must be string, C used diff --git a/Zend/tests/type_declarations/typed_properties_052.phpt b/Zend/tests/type_declarations/typed_properties_052.phpt new file mode 100644 index 0000000000000..83c1428f7f719 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_052.phpt @@ -0,0 +1,34 @@ +--TEST-- +Class properties declared in eval() must not leak +--FILE-- + +--EXPECT-- +object(A)#1 (3) { + ["a1"]=> + uninitialized(A) + ["b1"]=> + uninitialized(B) + ["c1"]=> + uninitialized(Foo\C) + ["a2"]=> + NULL + ["b2"]=> + NULL + ["c2"]=> + NULL +} diff --git a/Zend/tests/type_declarations/typed_properties_053.phpt b/Zend/tests/type_declarations/typed_properties_053.phpt new file mode 100644 index 0000000000000..45255ef9e9627 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_053.phpt @@ -0,0 +1,18 @@ +--TEST-- +Default values of callable properties +--FILE-- + +--EXPECT-- +object(A)#1 (1) { + ["a1"]=> + uninitialized(callable) + ["a2"]=> + NULL +} diff --git a/Zend/tests/type_declarations/typed_properties_054.phpt b/Zend/tests/type_declarations/typed_properties_054.phpt new file mode 100644 index 0000000000000..e6fbf355ff0f0 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_054.phpt @@ -0,0 +1,12 @@ +--TEST-- +Default values of callable properties +--FILE-- + +--EXPECTF-- +Fatal error: Default value for properties with callable type can only be null in %styped_properties_054.php on line 3 diff --git a/Zend/tests/type_declarations/typed_properties_055.phpt b/Zend/tests/type_declarations/typed_properties_055.phpt new file mode 100644 index 0000000000000..16c958acf33dc --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_055.phpt @@ -0,0 +1,27 @@ +--TEST-- +Default values of callable properties +--FILE-- +a = new A; +$f($o->a->foo); +$f($o->a->bar); +?> +--EXPECTF-- +int(1) + +Fatal error: Uncaught TypeError: Typed property A::$bar must not be referenced in %styped_properties_055.php:16 +Stack trace: +#0 {main} + thrown in %styped_properties_055.php on line 16 diff --git a/Zend/tests/type_declarations/typed_properties_056.phpt b/Zend/tests/type_declarations/typed_properties_056.phpt new file mode 100644 index 0000000000000..4fc7b014b2a93 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_056.phpt @@ -0,0 +1,23 @@ +--TEST-- +Type change in assign_op (use-after-free) +--FILE-- +foo = "1" . str_repeat("0", 2); +try { + $o->foo += 5; +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} +var_dump($o->foo); +unset($o); +?> +--EXPECT-- +Typed property A::$foo must be string, integer used +string(3) "100" diff --git a/Zend/tests/type_declarations/typed_properties_057.phpt b/Zend/tests/type_declarations/typed_properties_057.phpt new file mode 100644 index 0000000000000..fefe8d0738d74 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_057.phpt @@ -0,0 +1,31 @@ +--TEST-- +Type change in pre/post-increment (use-after-free) +--FILE-- +foo = "1" . str_repeat("0", 2); +try { + $x = ++$o->foo; +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} +var_dump($o->foo); +try { + $x = $o->foo++; +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} +var_dump($o->foo); +unset($o); +?> +--EXPECT-- +Typed property A::$foo must be string, integer used +string(3) "100" +Typed property A::$foo must be string, integer used +string(3) "100" diff --git a/Zend/tests/type_declarations/typed_properties_058.phpt b/Zend/tests/type_declarations/typed_properties_058.phpt new file mode 100644 index 0000000000000..2be5aa7aba8df --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_058.phpt @@ -0,0 +1,37 @@ +--TEST-- +Constants in default values of properties +--FILE-- +foo); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} + +for ($i = 0; $i < 2; $i++) { + try { + $o = new B(); + var_dump($o->foo); + } catch (Throwable $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +int(5) +Typed property B::$foo must be string, integer used +Typed property B::$foo must be string, integer used + diff --git a/Zend/tests/type_declarations/typed_properties_059.phpt b/Zend/tests/type_declarations/typed_properties_059.phpt new file mode 100644 index 0000000000000..e0c6d4d5ad271 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_059.phpt @@ -0,0 +1,29 @@ +--TEST-- +Nullable typed properties in traits +--FILE-- + +--EXPECT-- +object(A)#1 (2) { + ["a2"]=> + uninitialized(integer) + ["b2"]=> + NULL + ["a1"]=> + uninitialized(integer) + ["b1"]=> + NULL +} diff --git a/Zend/tests/type_declarations/typed_properties_060.phpt b/Zend/tests/type_declarations/typed_properties_060.phpt new file mode 100644 index 0000000000000..b1d16bd3c6bec --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_060.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test typed properties work fine with simple inheritance +--FILE-- +a); +$o->a = "a"; + +?> +--EXPECTF-- +int(1) + +Fatal error: Uncaught TypeError: Typed property A::$a must be integer, string used in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d + diff --git a/Zend/tests/type_declarations/types_in_ast.phpt b/Zend/tests/type_declarations/types_in_ast.phpt new file mode 100644 index 0000000000000..61268fdf91f33 --- /dev/null +++ b/Zend/tests/type_declarations/types_in_ast.phpt @@ -0,0 +1,23 @@ +--TEST-- +AST pretty-peinter +--INI-- +zend.assertions=1 +assert.exception=0 +--FILE-- + +--EXPECTF-- +Warning: assert(): assert(0 && ($a = function (int $a, ?int $b, int $c = null): ?int { + $x = new class { + public $a; + public int $b; + public ?int $c; + }; +})) failed in %stypes_in_ast.php on line 2 diff --git a/Zend/zend.c b/Zend/zend.c index f2fa4b12d9269..af43cb1f23a98 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -774,6 +774,30 @@ void zend_register_standard_ini_entries(void) /* {{{ */ } /* }}} */ +static void zend_resolve_property_types(void) /* {{{ */ +{ + zend_class_entry *ce; + zend_property_info *prop_info; + + ZEND_HASH_FOREACH_PTR(CG(class_table), ce) { + if (UNEXPECTED(ce->type == ZEND_INTERNAL_CLASS) + && UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) { + if (prop_info->type == IS_OBJECT && prop_info->type_name) { + zend_string *type_name = zend_string_tolower(prop_info->type_name); + zend_class_entry *prop_ce = + (zend_class_entry*)zend_hash_find_ptr(CG(class_table), type_name); + + assert(prop_ce && prop_ce->type == ZEND_INTERNAL_CLASS); + prop_info->type_ce = prop_ce; + zend_string_release(type_name); + } + }ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); +} /* }}} */ + /* Unlink the global (r/o) copies of the class, function and constant tables, * and use a fresh r/w copy for the startup thread */ @@ -785,6 +809,8 @@ void zend_post_startup(void) /* {{{ */ zend_compiler_globals *compiler_globals = ts_resource(compiler_globals_id); zend_executor_globals *executor_globals = ts_resource(executor_globals_id); + zend_resolve_property_types(); + *GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table; *GLOBAL_CLASS_TABLE = *compiler_globals->class_table; *GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants; @@ -809,6 +835,7 @@ void zend_post_startup(void) /* {{{ */ global_persistent_list = &EG(persistent_list); zend_copy_ini_directives(); #else + zend_resolve_property_types(); virtual_cwd_deactivate(); #endif } diff --git a/Zend/zend_API.c b/Zend/zend_API.c index eb76c671fc0f5..289b1b31b3bc0 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1087,6 +1087,7 @@ ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */ if (class_type->parent) { if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) { + class_type->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; return FAILURE; } } @@ -1144,7 +1145,23 @@ ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */ } ZVAL_DEREF(val); if (Z_CONSTANT_P(val)) { - if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) { + if (prop_info->type) { + zval tmp, tmp2, *v; + + ZVAL_COPY(&tmp, val); + if (UNEXPECTED(zval_update_constant_ex(&tmp, ce) != SUCCESS)) { + return FAILURE; + } + v = zend_verify_property_type(prop_info, &tmp, &tmp2, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); + if (UNEXPECTED(!v)) { + zend_verify_property_type_error(prop_info, prop_info->name, &tmp); + class_type->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + zval_ptr_dtor(&tmp); + return FAILURE; + } + zval_ptr_dtor(val); + ZVAL_COPY_VALUE(val, v); + } else if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) { return FAILURE; } } @@ -1193,7 +1210,19 @@ ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properti property_info && (property_info->flags & ZEND_ACC_STATIC) == 0) { zval *slot = OBJ_PROP(object, property_info->offset); - ZVAL_COPY_VALUE(slot, prop); + + if (UNEXPECTED(property_info->type)) { + zval tmp, *val; + + val = zend_verify_property_type(property_info, prop, &tmp, 0); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(property_info, key, prop); + continue; + } + ZVAL_COPY_VALUE(slot, val); + } else { + ZVAL_COPY_VALUE(slot, prop); + } ZVAL_INDIRECT(prop, slot); } } ZEND_HASH_FOREACH_END(); @@ -3598,10 +3627,22 @@ ZEND_API const char *zend_get_module_version(const char *module_name) /* {{{ */ } /* }}} */ -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_uchar optional_type, zend_string *optional_type_name, zend_bool allow_null) /* {{{ */ { zend_property_info *property_info, *property_info_ptr; + if (optional_type) { + if (access_type & ZEND_ACC_STATIC) { + zend_error(ce->type == ZEND_USER_CLASS ? E_COMPILE_ERROR : E_CORE_ERROR, + "Typed property %s::$%s must not be static", + ZSTR_VAL(ce->name), + ZSTR_VAL(name)); + return FAILURE; + } + + ce->ce_flags |= ZEND_ACC_HAS_TYPE_HINTS; + } + if (ce->type == ZEND_INTERNAL_CLASS) { property_info = pemalloc(sizeof(zend_property_info), 1); if ((access_type & ZEND_ACC_STATIC) || Z_CONSTANT_P(property)) { @@ -3668,12 +3709,24 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z property_info->flags = access_type; property_info->doc_comment = doc_comment; property_info->ce = ce; + property_info->type = optional_type; + property_info->allow_null = allow_null; + property_info->type_name = optional_type_name ? + zend_new_interned_string(optional_type_name) : NULL; + property_info->type_ce = NULL; + zend_hash_update_ptr(&ce->properties_info, name, property_info); return SUCCESS; } /* }}} */ +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ +{ + return zend_declare_typed_property(ce, name, property, access_type, doc_comment, 0, NULL, 0); +} +/* }}} */ + ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type) /* {{{ */ { zend_string *key = zend_string_init(name, name_length, ce->type & ZEND_INTERNAL_CLASS); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index ff93a53139ffe..a5327fe3552ab 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -313,6 +313,9 @@ ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, zend_strin ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name); ZEND_API const char *zend_get_module_version(const char *module_name); ZEND_API int zend_get_module_started(const char *module_name); + +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_uchar type, zend_string *type_name, zend_bool allow_null); + ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment); ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type); ZEND_API int zend_declare_property_null(zend_class_entry *ce, const char *name, size_t name_length, int access_type); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 236d0783fc960..a0f6c19165845 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1061,6 +1061,9 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int zend_ast_export_ex(str, decl->child[1], 0, indent); if (decl->child[3]) { smart_str_appends(str, ": "); + if (decl->child[3]->attr & ZEND_TYPE_NULLABLE) { + smart_str_appendc(str, '?'); + } zend_ast_export_ns_name(str, decl->child[3], 0, indent); } if (decl->child[2]) { @@ -1128,7 +1131,12 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int zend_ast_export_var_list(str, (zend_ast_list*)ast, indent); smart_str_appendc(str, ')'); break; - case ZEND_AST_PROP_DECL: + case ZEND_AST_PROP_GROUP: { + zend_ast *type_ast = ast->child[0]; + zend_ast *prop_ast = ast->child[1]; + + ast = prop_ast; + if (ast->attr & ZEND_ACC_PUBLIC) { smart_str_appends(str, "public "); } else if (ast->attr & ZEND_ACC_PROTECTED) { @@ -1139,7 +1147,19 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int if (ast->attr & ZEND_ACC_STATIC) { smart_str_appends(str, "static "); } + + if (type_ast) { + if (type_ast->attr & ZEND_TYPE_NULLABLE) { + smart_str_appendc(str, '?'); + } + zend_ast_export_ns_name( + str, type_ast, 0, indent); + smart_str_appendc(str, ' '); + } + goto simple_list; + } + case ZEND_AST_CONST_DECL: case ZEND_AST_CLASS_CONST_DECL: smart_str_appends(str, "const "); @@ -1595,6 +1615,9 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int break; case ZEND_AST_PARAM: if (ast->child[0]) { + if (ast->child[0]->attr & ZEND_TYPE_NULLABLE) { + smart_str_appendc(str, '?'); + } zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appendc(str, ' '); } diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 886b10622873c..77259abe2a833 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -130,6 +130,7 @@ enum _zend_ast_kind { ZEND_AST_USE_ELEM, ZEND_AST_TRAIT_ALIAS, ZEND_AST_GROUP_USE, + ZEND_AST_PROP_GROUP, /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ce8224652d028..70a65a492a9c6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -70,12 +70,18 @@ static inline void zend_alloc_cache_slot(uint32_t literal) { op_array->cache_size += sizeof(void*); } -#define POLYMORPHIC_CACHE_SLOT_SIZE 2 +#define POLYMORPHIC_CACHE_SLOTS_COUNT 1 /* only one class */ static inline void zend_alloc_polymorphic_cache_slot(uint32_t literal) { zend_op_array *op_array = CG(active_op_array); Z_CACHE_SLOT(op_array->literals[literal]) = op_array->cache_size; - op_array->cache_size += POLYMORPHIC_CACHE_SLOT_SIZE * sizeof(void*); + op_array->cache_size += POLYMORPHIC_CACHE_SLOTS_COUNT * sizeof(void*) * 2; +} + +static inline void zend_alloc_polymorphic_cache_slots(uint32_t literal, int count) { + zend_op_array *op_array = CG(active_op_array); + Z_CACHE_SLOT(op_array->literals[literal]) = op_array->cache_size; + op_array->cache_size += POLYMORPHIC_CACHE_SLOTS_COUNT * sizeof(void*) * (1 + count); } ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type); @@ -2595,7 +2601,7 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t } /* }}} */ -void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type); +void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, zend_bool by_ref); void zend_compile_assign(znode *result, zend_ast *ast); static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style); @@ -2620,7 +2626,7 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t znode var_node, dim_node; - zend_delayed_compile_var(&var_node, var_ast, type); + zend_delayed_compile_var(&var_node, var_ast, type, 0); zend_separate_if_call_and_write(&var_node, var_ast, type); if (dim_ast == NULL) { @@ -2677,7 +2683,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t if (is_this_fetch(obj_ast)) { obj_node.op_type = IS_UNUSED; } else { - zend_delayed_compile_var(&obj_node, obj_ast, type); + zend_delayed_compile_var(&obj_node, obj_ast, type, 0); zend_separate_if_call_and_write(&obj_node, obj_ast, type); } zend_compile_expr(&prop_node, prop_ast); @@ -2685,7 +2691,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t opline = zend_delayed_emit_op(result, ZEND_FETCH_OBJ_R, &obj_node, &prop_node); if (opline->op2_type == IS_CONST) { convert_to_string(CT_CONSTANT(opline->op2)); - zend_alloc_polymorphic_cache_slot(opline->op2.constant); + zend_alloc_polymorphic_cache_slots(opline->op2.constant, 2); } return opline; @@ -2700,10 +2706,13 @@ static zend_op *zend_compile_prop_common(znode *result, zend_ast *ast, uint32_t } /* }}} */ -void zend_compile_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ +void zend_compile_prop(znode *result, zend_ast *ast, uint32_t type, int by_ref) /* {{{ */ { zend_op *opline = zend_compile_prop_common(result, ast, type); zend_adjust_for_fetch_type(opline, type); + if (by_ref) { + opline->extended_value |= ZEND_FETCH_REF; + } } /* }}} */ @@ -2954,7 +2963,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_VAR: case ZEND_AST_STATIC_PROP: offset = zend_delayed_compile_begin(); - zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W); + zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0); zend_compile_expr(&expr_node, expr_ast); zend_delayed_compile_end(offset); zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node); @@ -3015,8 +3024,8 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */ zend_ensure_writable_variable(target_ast); offset = zend_delayed_compile_begin(); - zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W); - zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W); + zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1); + zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W, 1); zend_delayed_compile_end(offset); if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) { @@ -3055,7 +3064,7 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_VAR: case ZEND_AST_STATIC_PROP: offset = zend_delayed_compile_begin(); - zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW); + zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0); zend_compile_expr(&expr_node, expr_ast); zend_delayed_compile_end(offset); zend_emit_op(result, opcode, &var_node, &expr_node); @@ -3121,7 +3130,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ arg_count++; if (zend_is_variable(arg)) { if (zend_is_call(arg)) { - zend_compile_var(&arg_node, arg, BP_VAR_R); + zend_compile_var(&arg_node, arg, BP_VAR_R, 0); if (arg_node.op_type & (IS_CONST|IS_TMP_VAR)) { /* Function call was converted into builtin instruction */ opcode = ZEND_SEND_VAL; @@ -3140,15 +3149,15 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ } } else if (fbc) { if (ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) { - zend_compile_var(&arg_node, arg, BP_VAR_W); + zend_compile_var(&arg_node, arg, BP_VAR_W, 1); opcode = ZEND_SEND_REF; } else { - zend_compile_var(&arg_node, arg, BP_VAR_R); + zend_compile_var(&arg_node, arg, BP_VAR_R, 0); opcode = ZEND_SEND_VAR; } } else { zend_compile_var(&arg_node, arg, - BP_VAR_FUNC_ARG | (arg_num << BP_VAR_SHIFT)); + BP_VAR_FUNC_ARG | (arg_num << BP_VAR_SHIFT), 1); opcode = ZEND_SEND_VAR_EX; } } else { @@ -3516,7 +3525,7 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam zend_bool send_user = 0; if (zend_is_variable(arg_ast) && !zend_is_call(arg_ast)) { - zend_compile_var(&arg_node, arg_ast, BP_VAR_FUNC_ARG | (i << BP_VAR_SHIFT)); + zend_compile_var(&arg_node, arg_ast, BP_VAR_FUNC_ARG | (i << BP_VAR_SHIFT), 1); send_user = 1; } else { zend_compile_expr(&arg_node, arg_ast); @@ -4069,7 +4078,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */ expr_node.op_type = IS_CONST; ZVAL_NULL(&expr_node.u.constant); } else if (by_ref && zend_is_variable(expr_ast) && !zend_is_call(expr_ast)) { - zend_compile_var(&expr_node, expr_ast, BP_VAR_W); + zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); } else { zend_compile_expr(&expr_node, expr_ast); } @@ -4389,7 +4398,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ } if (by_ref && is_variable) { - zend_compile_var(&expr_node, expr_ast, BP_VAR_W); + zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 0); } else { zend_compile_expr(&expr_node, expr_ast); } @@ -5502,7 +5511,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_compile_prop_decl(zend_ast *ast) /* {{{ */ +void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); uint32_t flags = list->attr; @@ -5525,6 +5534,59 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */ zend_string *name = zend_ast_get_str(name_ast); zend_string *doc_comment = NULL; zval value_zv; + zend_uchar optional_type = 0; + zend_bool allow_null = 0; + zend_string *optional_type_name = NULL; + + if (type_ast) { + if (flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, + "Typed property %s::$%s must not be static", + ZSTR_VAL(ce->name), + ZSTR_VAL(name)); + } + + if (type_ast->kind == ZEND_AST_TYPE) { + optional_type = type_ast->attr; + } else { + zend_string *class_name = zend_ast_get_str(type_ast); + zend_uchar type = zend_lookup_builtin_type_by_name(class_name); + + if (type_ast->attr & ZEND_TYPE_NULLABLE) { + allow_null = 1; + type_ast->attr &= ~ZEND_TYPE_NULLABLE; + } + if (type != 0) { + if (type_ast->attr != ZEND_NAME_NOT_FQ) { + zend_error_noreturn(E_COMPILE_ERROR, + "Scalar type declaration '%s' must be unqualified", + ZSTR_VAL(zend_string_tolower(class_name))); + } + + if (type == IS_VOID) { + zend_error_noreturn(E_COMPILE_ERROR, + "Typed property %s::$%s must not be void", + ZSTR_VAL(ce->name), + ZSTR_VAL(name)); + } + + optional_type = type; + } else { + uint32_t fetch_type = zend_get_class_fetch_type_ast(type_ast); + + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { + class_name = zend_resolve_class_name_ast(type_ast); + zend_assert_valid_class_name(class_name); + } else { + zend_ensure_valid_class_fetch_type(fetch_type); + zend_string_addref(class_name); + } + + optional_type = IS_OBJECT; + optional_type_name = class_name; + } + } + } /* Doc comment has been appended as last element in ZEND_AST_PROP_ELEM ast */ if (doc_comment_ast) { @@ -5544,16 +5606,51 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */ if (value_ast) { zend_const_expr_to_zval(&value_zv, value_ast); - } else { + + if (optional_type && !Z_CONSTANT(value_zv)) { + if (allow_null && Z_TYPE(value_zv) == IS_NULL) { + /* pass */ + } else if (optional_type == IS_ARRAY) { + if (Z_TYPE(value_zv) != IS_ARRAY) { + zend_error_noreturn(E_COMPILE_ERROR, + "Default value for properties with array type can only be an array"); + } + } else if (optional_type == IS_CALLABLE) { + if (Z_TYPE(value_zv) != IS_NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Default value for properties with callable type can only be null"); + } + } else if (optional_type == IS_OBJECT) { + zend_error_noreturn(E_COMPILE_ERROR, + "Default value for properties with class type are disallowed"); + } else if (!ZEND_SAME_FAKE_TYPE(optional_type, Z_TYPE(value_zv))) { + zend_error_noreturn(E_COMPILE_ERROR, + "Default value for properties with %s type can only be %s", + zend_get_type_by_const(optional_type), + zend_get_type_by_const(optional_type)); + } + } + } else if (!optional_type || allow_null) { ZVAL_NULL(&value_zv); + } else { + ZVAL_UNDEF(&value_zv); } name = zend_new_interned_string_safe(name); - zend_declare_property_ex(ce, name, &value_zv, flags, doc_comment); + + zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, optional_type, optional_type_name, allow_null); } } /* }}} */ +void zend_compile_prop_group(zend_ast *list) /* {{{ */ +{ + zend_ast *type_ast = list->child[0]; + zend_ast *prop_ast = list->child[1]; + + zend_compile_prop_decl(prop_ast, type_ast); +} /* }}} */ + void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); @@ -6713,7 +6810,7 @@ void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */ zend_make_tmp_result(result, opline); } else { znode var_node; - zend_compile_var(&var_node, var_ast, BP_VAR_RW); + zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0); zend_emit_op_tmp(result, ast->kind == ZEND_AST_POST_INC ? ZEND_POST_INC : ZEND_POST_DEC, &var_node, NULL); } @@ -6732,7 +6829,7 @@ void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */ opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_OBJ : ZEND_PRE_DEC_OBJ; } else { znode var_node; - zend_compile_var(&var_node, var_ast, BP_VAR_RW); + zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0); zend_emit_op(result, ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC : ZEND_PRE_DEC, &var_node, NULL); } @@ -6822,7 +6919,7 @@ void zend_compile_coalesce(znode *result, zend_ast *ast) /* {{{ */ zend_op *opline; uint32_t opnum; - zend_compile_var(&expr_node, expr_ast, BP_VAR_IS); + zend_compile_var(&expr_node, expr_ast, BP_VAR_IS, 0); opnum = get_next_op_number(CG(active_op_array)); zend_emit_op_tmp(result, ZEND_COALESCE, &expr_node, NULL); @@ -6889,7 +6986,7 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */ if (value_ast) { if (returns_by_ref && zend_is_variable(value_ast) && !zend_is_call(value_ast)) { - zend_compile_var(&value_node, value_ast, BP_VAR_W); + zend_compile_var(&value_node, value_ast, BP_VAR_W, 1); } else { zend_compile_expr(&value_node, value_ast); } @@ -7097,7 +7194,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ if (by_ref) { zend_ensure_writable_variable(value_ast); - zend_compile_var(&value_node, value_ast, BP_VAR_W); + zend_compile_var(&value_node, value_ast, BP_VAR_W, 1); } else { zend_compile_expr(&value_node, value_ast); } @@ -7676,8 +7773,8 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ case ZEND_AST_METHOD: zend_compile_func_decl(NULL, ast); break; - case ZEND_AST_PROP_DECL: - zend_compile_prop_decl(ast); + case ZEND_AST_PROP_GROUP: + zend_compile_prop_group(ast); break; case ZEND_AST_CLASS_CONST_DECL: zend_compile_class_const_decl(ast); @@ -7737,7 +7834,7 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_CALL: case ZEND_AST_METHOD_CALL: case ZEND_AST_STATIC_CALL: - zend_compile_var(result, ast, BP_VAR_R); + zend_compile_var(result, ast, BP_VAR_R, 0); return; case ZEND_AST_ASSIGN: zend_compile_assign(result, ast); @@ -7841,7 +7938,7 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ +void zend_compile_var(znode *result, zend_ast *ast, uint32_t type, int by_ref) /* {{{ */ { switch (ast->kind) { case ZEND_AST_VAR: @@ -7851,7 +7948,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ zend_compile_dim(result, ast, type); return; case ZEND_AST_PROP: - zend_compile_prop(result, ast, type); + zend_compile_prop(result, ast, type, by_ref); return; case ZEND_AST_STATIC_PROP: zend_compile_static_prop(result, ast, type, 0); @@ -7880,7 +7977,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ } /* }}} */ -void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ +void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, zend_bool by_ref) /* {{{ */ { zend_op *opline; switch (ast->kind) { @@ -7894,12 +7991,15 @@ void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{ case ZEND_AST_PROP: opline = zend_delayed_compile_prop(result, ast, type); zend_adjust_for_fetch_type(opline, type); + if (by_ref) { + opline->extended_value |= ZEND_FETCH_REF; + } return; case ZEND_AST_STATIC_PROP: zend_compile_static_prop(result, ast, type, 1); return; default: - zend_compile_var(result, ast, type); + zend_compile_var(result, ast, type, 0); return; } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index fda7fe6014bd8..e54c940f94373 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -132,7 +132,7 @@ typedef union _zend_parser_stack_elem { void zend_compile_top_stmt(zend_ast *ast); void zend_compile_stmt(zend_ast *ast); void zend_compile_expr(znode *node, zend_ast *ast); -void zend_compile_var(znode *node, zend_ast *ast, uint32_t type); +void zend_compile_var(znode *node, zend_ast *ast, uint32_t type, int by_ref); void zend_eval_const_expr(zend_ast **ast_ptr); void zend_const_expr_to_zval(zval *result, zend_ast *ast); @@ -298,6 +298,10 @@ typedef struct _zend_property_info { zend_string *name; zend_string *doc_comment; zend_class_entry *ce; + zend_uchar type; + zend_bool allow_null; + zend_string *type_name; + zend_class_entry *type_ce; } zend_property_info; #define OBJ_PROP(obj, offset) \ @@ -890,6 +894,7 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name); #define ZEND_ISEMPTY 0x01000000 #define ZEND_ISSET_ISEMPTY_MASK (ZEND_ISSET | ZEND_ISEMPTY) #define ZEND_QUICK_SET 0x00800000 +#define ZEND_FETCH_REF 0x00400000 #define ZEND_FETCH_ARG_MASK 0x000fffff diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 8d97845bad579..b10f96922c038 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -663,7 +663,7 @@ static int is_null_constant(zend_class_entry *scope, zval *default_value) return 0; } -static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg) +static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg, zval *ret) { switch (type_hint) { case _IS_BOOL: { @@ -673,7 +673,7 @@ static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *a return 0; } zval_ptr_dtor(arg); - ZVAL_BOOL(arg, dest); + ZVAL_BOOL(ret, dest); return 1; } case IS_LONG: { @@ -683,7 +683,7 @@ static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *a return 0; } zval_ptr_dtor(arg); - ZVAL_LONG(arg, dest); + ZVAL_LONG(ret, dest); return 1; } case IS_DOUBLE: { @@ -693,14 +693,24 @@ static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *a return 0; } zval_ptr_dtor(arg); - ZVAL_DOUBLE(arg, dest); + ZVAL_DOUBLE(ret, dest); return 1; } case IS_STRING: { zend_string *dest; - /* on success "arg" is converted to IS_STRING */ - if (!zend_parse_arg_str_weak(arg, &dest)) { + if (arg != ret) { + ZVAL_COPY(ret, arg); + /* on success "ret" is converted to IS_STRING */ + if (!zend_parse_arg_str_weak(ret, &dest)) { + zval_ptr_dtor(ret); + return 0; + } + zval_ptr_dtor(arg); + return 1; + } + /* on success "ret" is converted to IS_STRING */ + if (!zend_parse_arg_str_weak(ret, &dest)) { return 0; } return 1; @@ -710,7 +720,7 @@ static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *a } } -static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict) +static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zval *ret, zend_bool strict) { if (UNEXPECTED(strict)) { /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ @@ -721,7 +731,7 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z /* NULL may be accepted only by nullable hints (this is already checked) */ return 0; } - return zend_verify_weak_scalar_type_hint(type_hint, arg); + return zend_verify_weak_scalar_type_hint(type_hint, arg, ret); } static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg) @@ -761,7 +771,7 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv } else if (cur_arg_info->type_hint == _IS_BOOL && EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { /* pass */ - } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) { + } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) { zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), ""); return 0; } @@ -770,6 +780,86 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv return 1; } +ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_info *info, zend_string *name, zval *property) +{ + zend_string *resolved = zend_resolve_property_type(info->type_name, info->ce); + + /* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */ + if (EG(exception)) { + return; + } + + if (info->type == IS_OBJECT) { + zend_throw_exception_ex(zend_ce_type_error, info->type, + "Typed property %s::$%s must be an instance of %s, %s used", + ZSTR_VAL(info->ce->name), + ZSTR_VAL(name), + ZSTR_VAL(resolved), + Z_TYPE_P(property) == IS_OBJECT ? + ZSTR_VAL(Z_OBJCE_P(property)->name) : + zend_get_type_by_const(Z_TYPE_P(property))); + } else { + zend_throw_exception_ex(zend_ce_type_error, info->type, + "Typed property %s::$%s must be %s, %s used", + ZSTR_VAL(info->ce->name), + ZSTR_VAL(name), + zend_get_type_by_const(info->type), + Z_TYPE_P(property) == IS_OBJECT ? + ZSTR_VAL(Z_OBJCE_P(property)->name) : + zend_get_type_by_const(Z_TYPE_P(property))); + } +} + +static zend_always_inline zval* i_zend_verify_property_type(zend_property_info *info, zval *property, zval *tmp, zend_bool strict) +{ + if (EXPECTED(info->type == Z_TYPE_P(property))) { + if (info->type_name) { + if (!info->type_ce) { + if (zend_string_equals_literal_ci(info->type_name, "self")) { + info->type_ce = info->ce; + } else if (zend_string_equals_literal_ci(info->type_name, "parent")) { + if (UNEXPECTED(!info->ce->parent)) { + zend_throw_error(NULL, "Cannot access parent:: when current class scope has no parent"); + return NULL; + } + info->type_ce = info->ce->parent; + } else { + info->type_ce = zend_lookup_class(info->type_name); + if (!info->type_ce) { + return NULL; + } + } + } + + if (!instanceof_function(Z_OBJCE_P(property), info->type_ce)) { + return NULL; + } + } + return property; + } else if (info->allow_null && Z_TYPE_P(property) == IS_NULL) { + return property; + } else if (EXPECTED(info->type == IS_CALLABLE)) { + if (Z_TYPE_P(property) == IS_OBJECT) { + return instanceof_function(zend_ce_closure, Z_OBJCE_P(property)) ? + property : NULL; + } else { + return zend_is_callable(property, IS_CALLABLE_CHECK_SILENT, NULL) ? + property : NULL; + } + } else if (info->type == _IS_BOOL && + EXPECTED(Z_TYPE_P(property) == IS_FALSE || Z_TYPE_P(property) == IS_TRUE)) { + return property; + } else { + return zend_verify_scalar_type_hint(info->type, property, tmp, strict) ? + tmp : NULL; + } +} + +zval* zend_verify_property_type(zend_property_info *info, zval *property, zval *tmp, zend_bool strict) +{ + return i_zend_verify_property_type(info, property, tmp, strict); +} + static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call) { uint32_t i; @@ -852,7 +942,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a } else if (cur_arg_info->type_hint == _IS_BOOL && EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { /* pass */ - } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) { + } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, arg, ZEND_ARG_USES_STRICT_TYPES()))) { zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), ""); return 0; } @@ -1069,7 +1159,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval * * that bans `return ...;` within a void function. Thus we can skip * this part of the runtime check for non-internal functions. */ - } else if (UNEXPECTED(!zend_verify_scalar_type_hint(ret_info->type_hint, ret, ZEND_RET_USES_STRICT_TYPES()))) { + } else if (UNEXPECTED(!zend_verify_scalar_type_hint(ret_info->type_hint, ret, ret, ZEND_RET_USES_STRICT_TYPES()))) { zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), ""); } } @@ -1427,6 +1517,7 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { zval *z, obj; + zend_property_info *prop_info; ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); @@ -1445,6 +1536,28 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, } ZVAL_COPY_VALUE(z, value); } + + prop_info = zend_object_fetch_property_type_info(Z_OBJCE(obj), Z_STR_P(property), NULL); + + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy; + + ZVAL_DUP(&z_copy, z); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + if (UNEXPECTED(result)) { + ZVAL_COPY(result, &z_copy); + } + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); + OBJ_RELEASE(Z_OBJ(obj)); + zval_ptr_dtor(&z_copy); + return; + } + ZVAL_DEREF(z); SEPARATE_ZVAL_NOREF(z); if (inc) { @@ -1452,10 +1565,10 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, } else { decrement_function(z); } - if (UNEXPECTED(result)) { + Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); + if (UNEXPECTED(result) && EXPECTED(!EG(exception))) { ZVAL_COPY(result, z); } - Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); OBJ_RELEASE(Z_OBJ(obj)); zval_ptr_dtor(z); } else { @@ -1471,6 +1584,7 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z zval *z; zval rv, obj; zval *zptr; + zend_property_info *prop_info; ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); @@ -1489,6 +1603,24 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z } ZVAL_COPY_VALUE(z, value); } + + prop_info = zend_object_fetch_property_type_info(Z_OBJCE(obj), Z_STR_P(property), NULL); + + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy; + + ZVAL_DUP(&z_copy, z); + binary_op(&z_copy, &z_copy, value); + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); + if (UNEXPECTED(result) && EXPECTED(!EG(exception))) { + ZVAL_COPY(result, z); + } + zval_ptr_dtor(&z_copy); + OBJ_RELEASE(Z_OBJ(obj)); + return; + } + zptr = z; ZVAL_DEREF(z); SEPARATE_ZVAL_NOREF(z); @@ -1952,8 +2084,10 @@ ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zva } -static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type) +static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t by_ref) { + zval *ptr; + if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { do { if (container_op_type == IS_VAR && UNEXPECTED(Z_ISERROR_P(container))) { @@ -1985,12 +2119,29 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c EXPECTED(Z_OBJCE_P(container) == CACHED_PTR_EX(cache_slot))) { uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(container); - zval *retval; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { - retval = OBJ_PROP(zobj, prop_offset); - if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) { - ZVAL_INDIRECT(result, retval); + ptr = OBJ_PROP(zobj, prop_offset); + if (EXPECTED(Z_TYPE_P(ptr) != IS_UNDEF)) { +return_indirect: + ZVAL_INDIRECT(result, ptr); + if ((by_ref & ZEND_FETCH_REF) + && (prop_op_type == IS_CONST + || EXPECTED(Z_TYPE_P(prop_ptr) == IS_STRING))) { + zend_property_info *prop_info; + + if (prop_op_type == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); + } else { + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(container), Z_STR_P(prop_ptr), NULL); + } + if (UNEXPECTED(prop_info)) { + zend_throw_exception_ex( + zend_ce_type_error, prop_info->type, + "Typed property %s::$%s must not be referenced", + ZSTR_VAL(prop_info->ce->name), Z_STRVAL_P(prop_ptr)); + } + } return; } } else if (EXPECTED(zobj->properties != NULL)) { @@ -2000,20 +2151,20 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } zobj->properties = zend_array_dup(zobj->properties); } - retval = zend_hash_find(zobj->properties, Z_STR_P(prop_ptr)); - if (EXPECTED(retval)) { - ZVAL_INDIRECT(result, retval); + ptr = zend_hash_find(zobj->properties, Z_STR_P(prop_ptr)); + if (EXPECTED(ptr)) { + ZVAL_INDIRECT(result, ptr); return; } } } if (EXPECTED(Z_OBJ_HT_P(container)->get_property_ptr_ptr)) { - zval *ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, type, cache_slot); + ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, type, cache_slot); if (NULL == ptr) { if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result); if (ptr != result) { - ZVAL_INDIRECT(result, ptr); + goto return_indirect; } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { ZVAL_UNREF(ptr); } @@ -2022,12 +2173,12 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c ZVAL_ERROR(result); } } else { - ZVAL_INDIRECT(result, ptr); + goto return_indirect; } } else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result); if (ptr != result) { - ZVAL_INDIRECT(result, ptr); + goto return_indirect; } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { ZVAL_UNREF(ptr); } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index e70465fdc6686..37ad85df0ad3a 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -363,6 +363,35 @@ void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t } \ } while (0) +#define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS) + +static zend_always_inline zend_property_info* zend_object_fetch_property_type_info(zend_class_entry *ce, zend_string *property, void **cache_slot) +{ + zend_property_info *info; + + /* if we have a cache_slot, let's assume it's valid. Callers task to ensure validity! */ + if (EXPECTED(cache_slot)) { + return (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + } + + if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + return NULL; + } + + info = zend_get_property_info(ce, property, 1); + + if (EXPECTED(info) + && UNEXPECTED(info != ZEND_WRONG_PROPERTY_INFO) + && UNEXPECTED(info->type)) { + return info; + } + + return NULL; +} + +zval* zend_verify_property_type(zend_property_info *info, zval *property, zval *tmp, zend_bool strict); +ZEND_COLD void zend_verify_property_type_error(zend_property_info *info, zend_string *name, zval *property); + END_EXTERN_C() #endif /* ZEND_EXECUTE_H */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 44abfb6ffba07..09ef7ed129c5c 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -41,6 +41,11 @@ static zend_property_info *zend_duplicate_property_info(zend_property_info *prop if (new_property_info->doc_comment) { zend_string_addref(new_property_info->doc_comment); } + if (new_property_info->type_name) { + zend_string_addref(new_property_info->type_name); + new_property_info->type_ce = NULL; + } + return new_property_info; } /* }}} */ @@ -50,6 +55,11 @@ static zend_property_info *zend_duplicate_property_info_internal(zend_property_i zend_property_info* new_property_info = pemalloc(sizeof(zend_property_info), 1); memcpy(new_property_info, property_info, sizeof(zend_property_info)); zend_string_addref(new_property_info->name); + if (new_property_info->type_name) { + zend_string_addref(new_property_info->type_name); + new_property_info->type_ce = NULL; + } + return new_property_info; } /* }}} */ @@ -640,6 +650,29 @@ static zend_function *do_inherit_method(zend_string *key, zend_function *parent, } /* }}} */ +zend_string* zend_resolve_property_type(zend_string *name, zend_class_entry *scope) /* {{{ */ +{ + zend_string *type = name; + + if (!type) { + return NULL; + } + + if (zend_string_equals_literal_ci(type, "parent")) { + if (scope && scope->parent) { + return scope->parent->name; + } + } + + if (zend_string_equals_literal_ci(type, "self")) { + if (scope) { + return scope->name; + } + } + + return type; +} /* }}} */ + static void do_inherit_property(zend_property_info *parent_info, zend_string *key, zend_class_entry *ce) /* {{{ */ { zval *child = zend_hash_find(&ce->properties_info, key); @@ -673,6 +706,38 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke child_info->offset = parent_info->offset; } } + + if (UNEXPECTED(parent_info->type && !(parent_info->flags & ZEND_ACC_PRIVATE))) { + if (parent_info->type == IS_OBJECT) { + if (child_info->type != IS_OBJECT || + child_info->allow_null != parent_info->allow_null || + !zend_string_equals_ci(zend_resolve_property_type(parent_info->type_name, parent_info->ce), + zend_resolve_property_type(child_info->type_name, child_info->ce))) { + zend_error_noreturn(E_COMPILE_ERROR, + "Type of %s::$%s must be %s%s (as in class %s)", + ZSTR_VAL(ce->name), + ZSTR_VAL(key), + parent_info->allow_null ? "?" : "", + ZSTR_VAL(zend_resolve_property_type(parent_info->type_name, parent_info->ce)), + ZSTR_VAL(ce->parent->name)); + } + } else if (parent_info->type != child_info->type || + parent_info->allow_null != child_info->allow_null) { + zend_error_noreturn(E_COMPILE_ERROR, + "Type of %s::$%s must be %s%s (as in class %s)", + ZSTR_VAL(ce->name), + ZSTR_VAL(key), + parent_info->allow_null ? "?" : "", + zend_get_type_by_const(parent_info->type), + ZSTR_VAL(ce->parent->name)); + } + } else if (UNEXPECTED(child_info->type && !parent_info->type)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Type of %s::$%s must not be defined (as in class %s)", + ZSTR_VAL(ce->name), + ZSTR_VAL(key), + ZSTR_VAL(ce->parent->name)); + } } else { if (UNEXPECTED(parent_info->flags & (ZEND_ACC_PRIVATE|ZEND_ACC_SHADOW))) { if (UNEXPECTED(ce->type & ZEND_INTERNAL_CLASS)) { @@ -1590,9 +1655,11 @@ static void zend_do_traits_property_binding(zend_class_entry *ce) /* {{{ */ if (Z_REFCOUNTED_P(prop_value)) Z_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; - zend_declare_property_ex(ce, prop_name, - prop_value, flags, - doc_comment); + if (property_info->type) { + zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type, property_info->type_name, property_info->allow_null); + } else { + zend_declare_property_ex(ce, prop_name, prop_value, flags, doc_comment); + } zend_string_release(prop_name); } ZEND_HASH_FOREACH_END(); } diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index bbe43b0ccda8b..0a3e380e0e608 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -35,6 +35,8 @@ void zend_do_early_binding(void); void zend_check_deprecated_constructor(const zend_class_entry *ce); +zend_string* zend_resolve_property_type(zend_string *name, zend_class_entry *scope); + END_EXTERN_C() #endif diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 44c5cfb12b506..4939051d0941e 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -711,8 +711,9 @@ class_statement_list: class_statement: - variable_modifiers property_list ';' - { $$ = $2; $$->attr = $1; } + variable_modifiers optional_type property_list ';' + { $3->attr = $1; + $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3); } | method_modifiers T_CONST class_const_list ';' { $$ = $3; $$->attr = $1; } | T_USE name_list trait_adaptations diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index d95f5d9a3346e..227871c115f2a 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -395,6 +395,7 @@ static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce exit_dynamic: if (cache_slot) { CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET); + CACHE_PTR_EX(cache_slot + 2, NULL); } return ZEND_DYNAMIC_PROPERTY_OFFSET; } else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) { @@ -408,6 +409,9 @@ static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce exit: if (cache_slot) { CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)property_info->offset); + if (property_info->type) { + CACHE_PTR_EX(cache_slot + 2, property_info); + } } return property_info->offset; } @@ -587,6 +591,7 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_ zval tmp_member; zval *retval; uint32_t property_offset; + zend_property_info *prop_info; zobj = Z_OBJ_P(object); @@ -669,6 +674,16 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_ retval = &EG(uninitialized_zval); } zval_ptr_dtor(&tmp_object); + + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(zobj->ce) && + (prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(member), cache_slot)))) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, retval, &tmp, (zobj->ce->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(member), retval); + } + } goto exit; } else { if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) { @@ -679,7 +694,16 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_ } } if ((type != BP_VAR_IS)) { - zend_error(E_NOTICE,"Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member)); + if (UNEXPECTED(prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(member), cache_slot))) { + if (UNEXPECTED(!prop_info->allow_null || Z_TYPE_P(retval) == IS_UNDEF)) { + zend_throw_exception_ex(zend_ce_type_error, prop_info->type, + "Typed property %s::$%s must not be accessed before initialization", + ZSTR_VAL(prop_info->ce->name), + Z_STRVAL_P(member)); + } + } else { + zend_error(E_NOTICE,"Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member)); + } } retval = &EG(uninitialized_zval); @@ -714,7 +738,20 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) { variable_ptr = OBJ_PROP(zobj, property_offset); if (Z_TYPE_P(variable_ptr) != IS_UNDEF) { - goto found; + zend_property_info *prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(member), cache_slot); + zval tmp, *val; + + if (UNEXPECTED(prop_info)) { + val = zend_verify_property_type(prop_info, value, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(member), value); + goto exit; + } + value = val; + } +found: + zend_assign_to_variable(variable_ptr, value, IS_CV); + goto exit; } } else if (EXPECTED(zobj->properties != NULL)) { if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { @@ -724,9 +761,7 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v zobj->properties = zend_array_dup(zobj->properties); } if ((variable_ptr = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) { -found: - zend_assign_to_variable(variable_ptr, value, IS_CV); - goto exit; + goto found; } } } else if (UNEXPECTED(EG(exception))) { @@ -769,6 +804,18 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v } } if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) { + zend_property_info *prop_info; + zval tmp, *val; + + if (UNEXPECTED(prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(member), cache_slot))) { + val = zend_verify_property_type(prop_info, value, &tmp, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(member), value); + zval_ptr_dtor(value); + goto exit; + } + value = val; + } ZVAL_COPY_VALUE(OBJ_PROP(zobj, property_offset), value); } else { if (!zobj->properties) { diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 91787aaad7cbf..5792c4ea7c6f6 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -282,6 +282,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (prop_info->doc_comment) { zend_string_release(prop_info->doc_comment); } + if (prop_info->type_name) { + zend_string_release(prop_info->type_name); + } } } ZEND_HASH_FOREACH_END(); zend_hash_destroy(&ce->properties_info); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2ec5c80bec1f8..6a794377beb65 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -707,6 +707,8 @@ ZEND_VM_HELPER(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW); @@ -735,8 +737,9 @@ ZEND_VM_HELPER(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, } /* here we are sure we are dealing with an object */ + cache_slot = (OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -745,13 +748,35 @@ ZEND_VM_HELPER(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -1144,6 +1169,8 @@ ZEND_VM_HELPER(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW); @@ -1169,8 +1196,9 @@ ZEND_VM_HELPER(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, } /* here we are sure we are dealing with an object */ + cache_slot = (OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -1182,22 +1210,67 @@ ZEND_VM_HELPER(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if (OP2_TYPE == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -1223,6 +1296,8 @@ ZEND_VM_HELPER(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW); @@ -1246,9 +1321,9 @@ ZEND_VM_HELPER(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, } /* here we are sure we are dealing with an object */ - + cache_slot = (OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -1259,19 +1334,64 @@ ZEND_VM_HELPER(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + if (OP2_TYPE == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } + } else { + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -1944,7 +2064,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_no_object): ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV) +ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, FETCH_REF) { USE_OPLINE zend_free_op free_op1, free_op2; @@ -1961,7 +2081,7 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV) HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); FREE_OP2(); if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -1986,7 +2106,7 @@ ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV) FREE_OP2(); HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); FREE_OP2(); if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -2068,7 +2188,7 @@ ZEND_VM_C_LABEL(fetch_obj_is_no_object): ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, NUM) +ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, NUM|FETCH_REF) { USE_OPLINE zval *container; @@ -2093,7 +2213,7 @@ ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|THIS|CV, CONST FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); FREE_OP2(); if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -2122,7 +2242,7 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV) property = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); FREE_OP2(); if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -2209,13 +2329,33 @@ ZEND_VM_HANDLER(136, ZEND_ASSIGN_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, SPEC( if (OP2_TYPE == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + FREE_OP_DATA(); + FREE_OP2(); + FREE_OP1_VAR_PTR(); + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (OP_DATA_TYPE == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + ZEND_VM_C_LABEL(fast_assign_obj): value = zend_assign_to_variable(property, value, OP_DATA_TYPE); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -5876,6 +6016,20 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR) FREE_OP1_VAR_PTR(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { +ZEND_VM_C_LABEL(fe_reset_rw_typed): + zend_throw_exception_ex( + zend_ce_type_error, 0, + "Typed properties exist in %s: foreach by reference is disallowed", + ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + HANDLE_EXCEPTION(); + } + if (!Z_OBJCE_P(array_ptr)->get_iterator) { if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { if (array_ptr == array_ref) { @@ -5912,9 +6066,15 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_object_iterator *iter; zend_bool is_empty; + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + ZEND_VM_C_GOTO(fe_reset_rw_typed); + } + + iter = ce->get_iterator(ce, array_ptr, 1); + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f7bc9fb454070..7095bb958eb05 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3572,6 +3572,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { +fe_reset_rw_typed: + zend_throw_exception_ex( + zend_ce_type_error, 0, + "Typed properties exist in %s: foreach by reference is disallowed", + ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); + if (IS_CONST == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } + if (!Z_OBJCE_P(array_ptr)->get_iterator) { if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { if (array_ptr == array_ref) { @@ -3607,9 +3621,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_object_iterator *iter; zend_bool is_empty; + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + goto fe_reset_rw_typed; + } + + iter = ce->get_iterator(ce, array_ptr, 1); + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { if (IS_CONST == IS_VAR) { @@ -5072,7 +5092,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -8891,7 +8911,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -10798,7 +10818,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -12635,6 +12655,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { +fe_reset_rw_typed: + zend_throw_exception_ex( + zend_ce_type_error, 0, + "Typed properties exist in %s: foreach by reference is disallowed", + ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } + if (!Z_OBJCE_P(array_ptr)->get_iterator) { if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { if (array_ptr == array_ref) { @@ -12670,9 +12704,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_object_iterator *iter; zend_bool is_empty; + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + goto fe_reset_rw_typed; + } + + iter = ce->get_iterator(ce, array_ptr, 1); + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { if (IS_TMP_VAR == IS_VAR) { @@ -13173,7 +13213,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CO HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -14391,7 +14431,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CV HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -14913,7 +14953,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TM HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -16205,6 +16245,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { +fe_reset_rw_typed: + zend_throw_exception_ex( + zend_ce_type_error, 0, + "Typed properties exist in %s: foreach by reference is disallowed", + ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); + if (IS_VAR == IS_VAR) { + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } + if (!Z_OBJCE_P(array_ptr)->get_iterator) { if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { if (array_ptr == array_ref) { @@ -16241,9 +16295,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_object_iterator *iter; zend_bool is_empty; + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + goto fe_reset_rw_typed; + } + + iter = ce->get_iterator(ce, array_ptr, 1); + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { if (IS_VAR == IS_VAR) { if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; @@ -16980,6 +17040,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -17008,8 +17070,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -17018,13 +17081,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -17895,6 +17980,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -17920,8 +18007,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -17933,22 +18021,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if (IS_CONST == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -17973,6 +18106,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -17996,9 +18131,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -18009,19 +18144,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -18219,7 +18399,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CONST_HAN HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -18244,7 +18424,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CONST_HA HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -18278,7 +18458,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CO if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -18307,7 +18487,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CONST property = EX_CONSTANT(opline->op2); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -18381,13 +18561,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -18552,13 +18752,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -18723,13 +18943,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -18894,13 +19134,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -21946,6 +22206,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -21974,8 +22236,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -21984,13 +22247,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -22861,6 +23146,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -22886,8 +23173,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -22899,22 +23187,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if (IS_CV == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -22939,6 +23272,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -22962,9 +23297,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -22975,19 +23310,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -23185,7 +23565,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLE HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -23210,7 +23590,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CV_HANDL HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -23244,7 +23624,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CV if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -23273,7 +23653,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HA property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -23347,13 +23727,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -23518,13 +23918,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -23689,13 +24109,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -23860,13 +24300,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -25035,6 +25495,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -25063,8 +25525,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -25073,13 +25536,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -25952,6 +26437,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -25977,8 +26464,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -25990,22 +26478,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -26031,6 +26564,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -26054,9 +26589,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -26067,19 +26602,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } + } else { + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -26279,7 +26859,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HA HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -26304,7 +26884,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_H zval_ptr_dtor_nogc(free_op2); HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); zval_ptr_dtor_nogc(free_op2); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -26338,7 +26918,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TM if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -26367,7 +26947,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVA property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); zval_ptr_dtor_nogc(free_op2); if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -26441,13 +27021,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + zval_ptr_dtor_nogc(free_op2); + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -26612,13 +27212,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + zval_ptr_dtor_nogc(free_op2); + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -26783,13 +27403,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + zval_ptr_dtor_nogc(free_op2); + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -26954,13 +27594,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + zval_ptr_dtor_nogc(free_op2); + if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -28055,6 +28715,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -28083,8 +28745,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -28093,13 +28756,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -28700,6 +29385,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -28725,8 +29412,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -28738,22 +29426,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if (IS_CONST == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -28778,6 +29511,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -28801,9 +29536,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -28814,19 +29549,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -28933,7 +29713,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_ HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -28958,7 +29738,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CONST HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -29064,7 +29844,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -29093,7 +29873,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CO property = EX_CONSTANT(opline->op2); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -29167,13 +29947,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -29338,13 +30138,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -29509,13 +30329,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -29680,13 +30520,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -31710,6 +32570,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -31738,8 +32600,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -31748,13 +32611,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -32355,6 +33240,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -32380,8 +33267,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -32393,22 +33281,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -32433,6 +33366,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -32456,9 +33391,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -32469,19 +33404,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -32588,7 +33568,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HAN HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -32613,7 +33593,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CV_HA HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -32719,7 +33699,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -32748,7 +33728,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -32822,13 +33802,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -32993,13 +33993,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -33164,13 +34184,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -33335,13 +34375,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -34248,6 +35308,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -34276,8 +35338,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -34286,13 +35349,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -34894,6 +35979,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -34919,8 +36006,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -34932,22 +36020,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -34973,6 +36106,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_obj_zval_ptr_unused(execute_data); @@ -34996,9 +36131,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -35009,19 +36144,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } + } else { + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -35130,7 +36310,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -35155,7 +36335,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVA zval_ptr_dtor_nogc(free_op2); HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); zval_ptr_dtor_nogc(free_op2); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -35262,7 +36442,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -35291,7 +36471,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TM property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); zval_ptr_dtor_nogc(free_op2); if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -35365,13 +36545,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -35536,13 +36736,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -35707,13 +36927,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -35878,13 +37118,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -37962,6 +39222,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { +fe_reset_rw_typed: + zend_throw_exception_ex( + zend_ce_type_error, 0, + "Typed properties exist in %s: foreach by reference is disallowed", + ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); + if (IS_CV == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } + if (!Z_OBJCE_P(array_ptr)->get_iterator) { if (IS_CV == IS_VAR || IS_CV == IS_CV) { if (array_ptr == array_ref) { @@ -37997,9 +39271,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_object_iterator *iter; zend_bool is_empty; + if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) { + goto fe_reset_rw_typed; + } + + iter = ce->get_iterator(ce, array_ptr, 1); + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { if (IS_CV == IS_VAR) { @@ -39034,6 +40314,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -39062,8 +40344,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -39072,13 +40355,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -39949,6 +41254,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -39974,8 +41281,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -39987,22 +41295,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if (IS_CONST == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -40027,6 +41380,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -40050,9 +41405,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -40063,19 +41418,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + if (IS_CONST == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } + } else { + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -40462,7 +41862,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HAND HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -40487,7 +41887,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CONST_HAN HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -40593,7 +41993,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CON HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -40622,7 +42022,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_ property = EX_CONSTANT(opline->op2); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -40709,13 +42109,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -40880,13 +42300,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -41051,13 +42491,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -41222,13 +42682,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA if (IS_CONST == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -46186,6 +47666,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -46214,8 +47696,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -46224,13 +47707,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -47101,6 +48606,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -47126,8 +48633,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -47139,22 +48647,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -47179,6 +48732,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -47202,9 +48757,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -47215,19 +48770,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } } else { - decrement_function(zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -47475,7 +49075,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -47500,7 +49100,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CV_HANDLE HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -47606,7 +49206,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CV_ HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -47635,7 +49235,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HAN property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -47722,13 +49322,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -47893,13 +49513,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -48064,13 +49704,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -48235,13 +49895,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ if (IS_CV == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -50348,6 +52028,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zval *property; zval *value; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -50376,8 +52058,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP } /* here we are sure we are dealing with an object */ + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -50386,13 +52069,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_DEREF(zptr); SEPARATE_ZVAL_NOREF(zptr); - binary_op(zptr, zptr, value); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)){ + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + binary_op(&z_copy, &z_copy, value); + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + binary_op(zptr, zptr, value); + } + + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_assign_op_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_assign_op_overloaded_property(object, property, cache_slot, value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -51265,6 +52970,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -51290,8 +52997,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } /* here we are sure we are dealing with an object */ + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); @@ -51303,22 +53011,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); - if (inc) { - increment_function(zptr); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, val); + } + } else { + SEPARATE_ZVAL_NOREF(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } } else { - zend_pre_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + zend_pre_incdec_overloaded_property(object, property, cache_slot, inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -51344,6 +53097,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP zval *object; zval *property; zval *zptr; + void **cache_slot; + zend_property_info *prop_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var); @@ -51367,9 +53122,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } /* here we are sure we are dealing with an object */ - + cache_slot = ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL; if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) - && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { + && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) { if (UNEXPECTED(Z_ISERROR_P(zptr))) { ZVAL_NULL(EX_VAR(opline->result.var)); } else { @@ -51380,19 +53135,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP } else { fast_long_decrement_function(zptr); } + if (UNEXPECTED(Z_TYPE_P(zptr) != IS_LONG)) { + 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_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + zval tmp, *val; + + val = zend_verify_property_type(prop_info, zptr, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), zptr); + if (inc) { + ZVAL_LONG(zptr, ZEND_LONG_MAX); + } else { + ZVAL_LONG(zptr, ZEND_LONG_MIN); + } + } + } + } } else { ZVAL_DEREF(zptr); ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - if (inc) { - increment_function(zptr); + + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2); } else { - decrement_function(zptr); + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(object), Z_STR_P(property), NULL); + } + if (UNEXPECTED(prop_info)) { + /* special case for typed properties */ + zval z_copy, tmp, *val; + + ZVAL_DUP(&z_copy, zptr); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + val = zend_verify_property_type(prop_info, &z_copy, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property), &z_copy); + zval_ptr_dtor(&z_copy); + } else { + ZVAL_COPY_VALUE(zptr, val); + } + } else { + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } } } } else { - zend_post_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); + zend_post_incdec_overloaded_property(object, property, cache_slot, inc, EX_VAR(opline->result.var)); } } while (0); @@ -51642,7 +53442,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HAN HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -51667,7 +53467,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HA zval_ptr_dtor_nogc(free_op2); HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW, 0); zval_ptr_dtor_nogc(free_op2); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -51774,7 +53574,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMP HANDLE_EXCEPTION(); } - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W, opline->extended_value); zval_ptr_dtor_nogc(free_op2); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -51803,7 +53603,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); + zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET, 0); zval_ptr_dtor_nogc(free_op2); if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) { EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var)); @@ -51890,13 +53690,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CONST == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CONST); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -52061,13 +53881,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_TMP_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_TMP_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -52232,13 +54072,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + zval_ptr_dtor_nogc(free_op_data); + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_VAR == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_VAR); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -52403,13 +54263,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*)); + void **cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(property_name)); + uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(object); zval *property; if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { property = OBJ_PROP(zobj, prop_offset); if (Z_TYPE_P(property) != IS_UNDEF) { + zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2); + zval tmp, *val; + + if (UNEXPECTED(prop_info != NULL)) { + val = i_zend_verify_property_type(prop_info, value, &tmp, EX_USES_STRICT_TYPES()); + if (UNEXPECTED(!val)) { + zend_verify_property_type_error(prop_info, Z_STR_P(property_name), value); + + zval_ptr_dtor_nogc(free_op2); + + HANDLE_EXCEPTION(); + } + /* will remain valid, thus no need to check prop_info in future here */ + if (IS_CV == IS_CONST && val == value) { + CACHE_PTR_EX(cache_slot + 2, NULL); + } + value = val; + } + fast_assign_obj: value = zend_assign_to_variable(property, value, IS_CV); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index e1041d24c1e26..14718ed65002f 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -74,9 +74,10 @@ "ZEND_VM_EXT_ARG_NUM" => 1<<18, "ZEND_VM_EXT_ARRAY_INIT" => 1<<19, "ZEND_VM_EXT_REF" => 1<<20, + "ZEND_VM_EXT_FETCH_REF" => 1<<21, "ZEND_VM_EXT_MASK" => 0x0f000000, "ZEND_VM_EXT_NUM" => 0x01000000, - // unused 0x2000000 + // unused 0x02000000, "ZEND_VM_EXT_JMP_ADDR" => 0x03000000, "ZEND_VM_EXT_DIM_OBJ" => 0x04000000, "ZEND_VM_EXT_CLASS_FETCH" => 0x05000000, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 100106cdc51b1..d4711ca129d72 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -294,7 +294,7 @@ static uint32_t zend_vm_opcodes_flags[184] = { 0x00000753, 0x00010107, 0x00006701, - 0x00000751, + 0x00200751, 0x00010107, 0x00006701, 0x00000751, @@ -303,7 +303,7 @@ static uint32_t zend_vm_opcodes_flags[184] = { 0x00000757, 0x00050107, 0x01006703, - 0x01000753, + 0x01200753, 0x00010107, 0x00000701, 0x00000751, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 1eb54f0f005e6..552d7951fd38a 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -46,6 +46,7 @@ #define ZEND_VM_EXT_ARG_NUM 0x00040000 #define ZEND_VM_EXT_ARRAY_INIT 0x00080000 #define ZEND_VM_EXT_REF 0x00100000 +#define ZEND_VM_EXT_FETCH_REF 0x00200000 #define ZEND_VM_EXT_MASK 0x0f000000 #define ZEND_VM_EXT_NUM 0x01000000 #define ZEND_VM_EXT_JMP_ADDR 0x03000000 diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index 76c89fe828c3e..ace0e8714b59e 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -212,7 +212,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->op2_type, opline->op2, opline->op1.constant, - LITERAL_STATIC_PROPERTY, 2, 1, + LITERAL_STATIC_PROPERTY, 3, 1, op_array); } break; @@ -248,7 +248,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->op1_type, opline->op1, opline->op2.constant, - LITERAL_PROPERTY, 2, 1, + LITERAL_PROPERTY, 3, 1, op_array); } break; @@ -271,7 +271,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->op1_type, opline->op1, opline->op2.constant, - LITERAL_PROPERTY, 2, 1, + LITERAL_PROPERTY, 3, 1, op_array); } else { LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1); diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 7c225bbbe8618..9203801ac4af9 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -542,6 +542,11 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * fprintf(stderr, " (ref)"); } } + if (ZEND_VM_EXT_FETCH_REF & flags) { + if (opline->extended_value & ZEND_FETCH_REF) { + fprintf(stderr, " (ref)"); + } + } } if (opline->op1_type == IS_CONST) { diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 35dff98362585..c370dc5ff0670 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -189,7 +189,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, case ZEND_FETCH_STATIC_PROP_FUNC_ARG: TO_STRING_NOWARN(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); - alloc_cache_slots_op1(op_array, opline, 2); + alloc_cache_slots_op1(op_array, opline, 3); break; case ZEND_CONCAT: case ZEND_FAST_CONCAT: @@ -293,7 +293,7 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, case ZEND_ISSET_ISEMPTY_PROP_OBJ: TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - alloc_cache_slots_op2(op_array, opline, 2); + alloc_cache_slots_op2(op_array, opline, 3); break; case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: @@ -310,7 +310,7 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, if (opline->extended_value == ZEND_ASSIGN_OBJ) { TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - alloc_cache_slots_op2(op_array, opline, 2); + alloc_cache_slots_op2(op_array, opline, 3); } else { opline->op2.constant = zend_optimizer_add_literal(op_array, val); } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 25a504575d264..6192ad1c2e08c 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -603,6 +603,12 @@ static void zend_persist_property_info(zval *zv) prop->doc_comment = NULL; } } + + if (prop->type_name) { + zend_accel_store_interned_string(prop->type_name); + + prop->type_ce = NULL; + } } static void zend_persist_class_constant(zval *zv) diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 22e74d6b36f12..3559125a25d3e 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -288,6 +288,9 @@ static void zend_persist_property_info_calc(zval *zv) zend_shared_alloc_register_xlat_entry(prop, prop); ADD_ARENA_SIZE(sizeof(zend_property_info)); ADD_INTERNED_STRING(prop->name, 0); + if (prop->type_name) { + ADD_INTERNED_STRING(prop->type_name, 0); + } if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 13003cae845b7..6fc340f448436 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -203,10 +203,22 @@ typedef struct _parameter_reference { zend_function *fptr; } parameter_reference; +typedef enum { + REF_INFO_ARG, + REF_INFO_PROP +} reflection_info_type_t; + /* Struct for type hints */ typedef struct _type_reference { - struct _zend_arg_info *arg_info; - zend_function *fptr; + reflection_info_type_t type; + union { + zend_function *fptr; + zend_class_entry *ce; + } scope; + union { + zend_arg_info *arg; + zend_property_info *prop; + } info; } type_reference; typedef enum { @@ -320,7 +332,9 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ break; case REF_TYPE_TYPE: typ_reference = (type_reference*)intern->ptr; - _free_function(typ_reference->fptr); + if (typ_reference->type == REF_INFO_ARG) { + _free_function(typ_reference->scope.fptr); + } efree(intern->ptr); break; case REF_TYPE_FUNCTION: @@ -1261,8 +1275,8 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje } /* }}} */ -/* {{{ reflection_type_factory */ -static void reflection_type_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, zval *object) +/* {{{ reflection_arg_type_factory */ +static void reflection_arg_type_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, zval *object) { reflection_object *intern; type_reference *reference; @@ -1270,11 +1284,36 @@ static void reflection_type_factory(zend_function *fptr, zval *closure_object, s reflection_instantiate(reflection_type_ptr, object); intern = Z_REFLECTION_P(object); reference = (type_reference*) emalloc(sizeof(type_reference)); - reference->arg_info = arg_info; - reference->fptr = fptr; + reference->type = REF_INFO_ARG; + reference->info.arg = arg_info; + reference->scope.fptr = fptr; intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; intern->ce = fptr->common.scope; + + if (closure_object) { + Z_ADDREF_P(closure_object); + ZVAL_COPY_VALUE(&intern->obj, closure_object); + } +} +/* }}} */ + +/* {{{ reflection_prop_type_factory */ +static void reflection_prop_type_factory(zend_class_entry *ce, zval *closure_object, zend_property_info *prop_info, zval *object) +{ + reflection_object *intern; + type_reference *reference; + + reflection_instantiate(reflection_type_ptr, object); + intern = Z_REFLECTION_P(object); + reference = (type_reference*) emalloc(sizeof(type_reference)); + reference->type = REF_INFO_PROP; + reference->info.prop = prop_info; + reference->scope.ce = ce; + intern->ptr = reference; + intern->ref_type = REF_TYPE_TYPE; + intern->ce = ce; + if (closure_object) { Z_ADDREF_P(closure_object); ZVAL_COPY_VALUE(&intern->obj, closure_object); @@ -2724,7 +2763,7 @@ ZEND_METHOD(reflection_parameter, getType) { RETURN_NULL(); } - reflection_type_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value); + reflection_arg_type_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value); } /* }}} */ @@ -2971,7 +3010,14 @@ ZEND_METHOD(reflection_type, allowsNull) } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_BOOL(param->arg_info->allow_null); + switch (param->type) { + case REF_INFO_ARG: + RETVAL_BOOL(param->info.arg->allow_null); + break; + case REF_INFO_PROP: + RETVAL_BOOL(param->info.prop->allow_null); + break; + } } /* }}} */ @@ -2987,7 +3033,14 @@ ZEND_METHOD(reflection_type, isBuiltin) } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_BOOL(param->arg_info->type_hint != IS_OBJECT); + switch (param->type) { + case REF_INFO_ARG: + RETVAL_BOOL(param->info.arg->type_hint != IS_OBJECT); + break; + case REF_INFO_PROP: + RETVAL_BOOL(param->info.prop->type != IS_OBJECT); + break; + } } /* }}} */ @@ -2997,21 +3050,40 @@ ZEND_METHOD(reflection_type, __toString) { reflection_object *intern; type_reference *param; + zend_uchar type = IS_UNDEF; if (zend_parse_parameters_none() == FAILURE) { return; } + GET_REFLECTION_OBJECT_PTR(param); - switch (param->arg_info->type_hint) { + switch (param->type) { + case REF_INFO_ARG: + type = param->info.arg->type_hint; + if (type == IS_OBJECT) { + zend_function *fptr = param->scope.fptr; + + if (fptr->type == ZEND_INTERNAL_FUNCTION && + !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { + RETURN_STRING(((zend_internal_arg_info*) param->info.arg)->class_name); + } else { + RETURN_STR_COPY(param->info.arg->class_name); + } + } + break; + + case REF_INFO_PROP: + type = param->info.prop->type; + if (type == IS_OBJECT) { + RETURN_STR_COPY(param->info.prop->type_name); + } + break; + } + + switch (type) { case IS_ARRAY: RETURN_STRINGL("array", sizeof("array") - 1); case IS_CALLABLE: RETURN_STRINGL("callable", sizeof("callable") - 1); - case IS_OBJECT: - if (param->fptr->type == ZEND_INTERNAL_FUNCTION && - !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - RETURN_STRING(((zend_internal_arg_info*)param->arg_info)->class_name); - } - RETURN_STR_COPY(param->arg_info->class_name); case IS_STRING: RETURN_STRINGL("string", sizeof("string") - 1); case _IS_BOOL: RETURN_STRINGL("bool", sizeof("bool") - 1); case IS_LONG: RETURN_STRINGL("int", sizeof("int") - 1); @@ -3562,7 +3634,7 @@ ZEND_METHOD(reflection_function, getReturnType) RETURN_NULL(); } - reflection_type_factory(_copy_function(fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, &fptr->common.arg_info[-1], return_value); + reflection_arg_type_factory(_copy_function(fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, &fptr->common.arg_info[-1], return_value); } /* }}} */ @@ -5803,6 +5875,44 @@ ZEND_METHOD(reflection_property, setAccessible) } /* }}} */ +/* {{{ proto public ReflectionType ReflectionProperty::getType() + Returns the type associated with the property */ +ZEND_METHOD(reflection_property, getType) +{ + reflection_object *intern; + property_reference *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + GET_REFLECTION_OBJECT_PTR(ref); + + if (!ref->prop.type) { + RETURN_NULL(); + } + + reflection_prop_type_factory(ref->prop.ce, Z_ISUNDEF(intern->obj) ? NULL : &intern->obj, &ref->prop, return_value); +} +/* }}} */ + +/* {{{ proto public bool ReflectionProperty::hasType() + Returns whether property has a type */ +ZEND_METHOD(reflection_property, hasType) +{ + reflection_object *intern; + property_reference *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + GET_REFLECTION_OBJECT_PTR(ref); + + RETVAL_BOOL(ref->prop.type != 0); +} +/* }}} */ + /* {{{ proto public static mixed ReflectionExtension::export(string name [, bool return]) throws ReflectionException Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */ ZEND_METHOD(reflection_extension, export) @@ -6672,6 +6782,8 @@ static const zend_function_entry reflection_property_functions[] = { ZEND_ME(reflection_property, getDeclaringClass, arginfo_reflection__void, 0) ZEND_ME(reflection_property, getDocComment, arginfo_reflection__void, 0) ZEND_ME(reflection_property, setAccessible, arginfo_reflection_property_setAccessible, 0) + ZEND_ME(reflection_property, getType, arginfo_reflection__void, 0) + ZEND_ME(reflection_property, hasType, arginfo_reflection__void, 0) PHP_FE_END }; diff --git a/ext/reflection/tests/ReflectionType_001.phpt b/ext/reflection/tests/ReflectionType_001.phpt index f764cf1519d92..158ecb6b5ef62 100644 --- a/ext/reflection/tests/ReflectionType_001.phpt +++ b/ext/reflection/tests/ReflectionType_001.phpt @@ -73,6 +73,29 @@ foreach ([ var_dump((string)$ra); } } + +echo "\n*** property types\n"; + +class PropTypeTest { + public int $int; + public string $string; + public array $arr; + public callable $callable; + public stdClass $std; + public OtherThing $other; + public $mixed; +} + +$reflector = new ReflectionClass(PropTypeTest::class); + +foreach ($reflector->getProperties() as $name => $property) { + if ($property->hasType()) { + printf("public %s $%s;\n", + $property->getType(), $property->getName()); + } else printf("public $%s;\n", $property->getName()); +} + +echo "** property types\n"; --EXPECT-- *** functions ** Function 0 - Parameter 0 @@ -183,3 +206,13 @@ bool(true) bool(false) bool(false) string(4) "Test" + +*** property types +public int $int; +public string $string; +public array $arr; +public callable $callable; +public stdClass $std; +public OtherThing $other; +public $mixed; +** property types diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index 4afefbe95aef9..7185767d1b2f7 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -48,6 +48,7 @@ struct php_unserialize_data { void *last; void *first_dtor; void *last_dtor; + HashTable *refs; }; typedef struct php_serialize_data *php_serialize_data_t; diff --git a/ext/standard/tests/serialize/typed_property_refs.phpt b/ext/standard/tests/serialize/typed_property_refs.phpt new file mode 100644 index 0000000000000..a05a8e53af51b --- /dev/null +++ b/ext/standard/tests/serialize/typed_property_refs.phpt @@ -0,0 +1,29 @@ +--TEST-- +unserialize with references to typed properties shall skip the references or fail +--FILE-- + +--EXPECTF-- +object(A)#1 (2) { + ["a"]=> + int(1) + ["b"]=> + int(1) +} + +Notice: unserialize(): Error at offset 35 of 36 bytes in %s on line %d +bool(false) diff --git a/ext/standard/var.c b/ext/standard/var.c index 16b9fffba1109..e46c7db4847f6 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -48,7 +48,7 @@ static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, } /* }}} */ -static void php_object_property_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */ +static void php_object_property_dump(zend_property_info *prop_info, zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */ { const char *prop_name, *class_name; @@ -71,7 +71,22 @@ static void php_object_property_dump(zval *zv, zend_ulong index, zend_string *ke } ZEND_PUTS("]=>\n"); } - php_var_dump(zv, level + 2); + + if (Z_TYPE_P(zv) == IS_UNDEF) { + if (prop_info->type) { + php_printf("%*cuninitialized(%s%s)\n", + level + 1, ' ', + prop_info->allow_null ? "?" : "", + (prop_info->type == IS_OBJECT) ? + ZSTR_VAL(prop_info->type_name) : + zend_get_type_by_const(prop_info->type)); + } else { + php_printf("%*cuninitialized\n", + level + 1, ' '); + } + } else { + php_var_dump(zv, level + 2); + } } /* }}} */ @@ -155,8 +170,20 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ zend_string *key; zval *val; - ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) { - php_object_property_dump(val, num, key, level); + ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) { + zend_property_info *prop_info = NULL; + + if (Z_TYPE_P(val) == IS_INDIRECT) { + val = Z_INDIRECT_P(val); + } + + if (key) { + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(struc), key, NULL); + } + + if (!Z_ISUNDEF_P(val) || prop_info) { + php_object_property_dump(prop_info, val, num, key, level); + } } ZEND_HASH_FOREACH_END(); if (is_temp) { zend_hash_destroy(myht); @@ -220,7 +247,7 @@ static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key } /* }}} */ -static void zval_object_property_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */ +static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */ { const char *prop_name, *class_name; @@ -241,7 +268,21 @@ static void zval_object_property_dump(zval *zv, zend_ulong index, zend_string *k } ZEND_PUTS("]=>\n"); } - php_debug_zval_dump(zv, level + 2); + if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) { + if (prop_info->type) { + php_printf("%*cuninitialized(%s%s)\n", + level + 1, ' ', + prop_info->allow_null ? "?" : "", + (prop_info->type == IS_OBJECT) ? + ZSTR_VAL(prop_info->type_name) : + zend_get_type_by_const(prop_info->type)); + } else { + php_printf("%*cuninitialized\n", + level + 1, ' '); + } + } else { + php_debug_zval_dump(zv, level + 2); + } } /* }}} */ @@ -320,8 +361,20 @@ PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */ php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc)); zend_string_release(class_name); if (myht) { - ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { - zval_object_property_dump(val, index, key, level); + ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) { + zend_property_info *prop_info = NULL; + + if (Z_TYPE_P(val) == IS_INDIRECT) { + val = Z_INDIRECT_P(val); + } + + if (key) { + prop_info = zend_object_fetch_property_type_info(Z_OBJCE_P(struc), key, NULL); + } + + if (!Z_ISUNDEF_P(val) || prop_info) { + zval_object_property_dump(prop_info, val, index, key, level); + } } ZEND_HASH_FOREACH_END(); myht->u.v.nApplyCount--; if (is_temp) { diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 3fc074dd6ae2c..edb122ab2711d 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -164,6 +164,10 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) efree_size(var_dtor_hash, sizeof(var_dtor_entries)); var_dtor_hash = next; } + + if ((*var_hashx)->refs) { + zend_array_destroy((*var_hashx)->refs); + } } /* }}} */ @@ -238,7 +242,7 @@ static inline int unserialize_allowed_class(zend_string *class_name, HashTable * #define YYMARKER marker -#line 246 "ext/standard/var_unserializer.re" +#line 250 "ext/standard/var_unserializer.re" @@ -301,11 +305,12 @@ static inline size_t parse_uiv(const unsigned char *p) #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes -static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) +static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce) { while (elements-- > 0) { - zval key, *data, d, *old_data; + zval key, *data, d, *old_data, tmp; zend_ulong idx; + zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@ -317,7 +322,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab data = NULL; ZVAL_UNDEF(&d); - if (!objprops) { + if (!ce) { if (Z_TYPE(key) == IS_LONG) { idx = Z_LVAL(key); numeric_key: @@ -349,9 +354,40 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) { if (Z_TYPE_P(old_data) == IS_INDIRECT) { old_data = Z_INDIRECT_P(old_data); + if (Z_STRVAL(key)[0] == 0) { + zend_string *member, *class; + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex(Z_STR(key), &class_name, &prop_name, &prop_name_len); + member = zend_string_init(prop_name, prop_name_len, 0); + class = zend_string_init(class_name, strlen(class_name), 0); + zend_str_tolower(ZSTR_VAL(class), ZSTR_LEN(class)); + EG(fake_scope) = class_name[0] == '*' ? ce : zend_hash_find_ptr(EG(class_table), class); + info = zend_get_property_info(EG(fake_scope), member, 1); + EG(fake_scope) = NULL; + zend_string_release(member); + zend_string_release(class); + } else { + info = zend_get_property_info(ce, Z_STR(key), 1); + } + var_push_dtor(var_hash, old_data); + old_data = zend_hash_update_ind(ht, Z_STR(key), &d); + + if (EXPECTED(!info->type)) { + info = NULL; + data = old_data; + } else { + /* little hack to disallow references */ + if (!(*var_hash)->refs) { + (*var_hash)->refs = emalloc(sizeof(HashTable)); + zend_hash_init((*var_hash)->refs, 8, NULL, ZVAL_PTR_DTOR, 0); + } + data = zend_hash_next_index_insert((*var_hash)->refs, &d); + } + } else { + var_push_dtor(var_hash, old_data); + data = zend_hash_update_ind(ht, Z_STR(key), &d); } - var_push_dtor(var_hash, old_data); - data = zend_hash_update_ind(ht, Z_STR(key), &d); } else { data = zend_hash_add_new(ht, Z_STR(key), &d); } @@ -370,6 +406,15 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab return 0; } + if (UNEXPECTED(info)) { + data = zend_verify_property_type(info, data, &tmp, 1); + if (UNEXPECTED(!data)) { + zval_dtor(&key); + return 0; + } + ZVAL_COPY(old_data, data); + } + if (UNEXPECTED(Z_ISUNDEF_P(data))) { if (Z_TYPE(key) == IS_LONG) { zend_hash_index_del(ht, Z_LVAL(key)); @@ -462,7 +507,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJCE_P(rval))) { return 0; } @@ -514,7 +559,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) start = cursor; -#line 518 "ext/standard/var_unserializer.c" +#line 563 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -574,9 +619,9 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 884 "ext/standard/var_unserializer.re" +#line 929 "ext/standard/var_unserializer.re" { return 0; } -#line 580 "ext/standard/var_unserializer.c" +#line 625 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -619,13 +664,13 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) goto yy3; yy14: ++YYCURSOR; -#line 878 "ext/standard/var_unserializer.re" +#line 923 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 629 "ext/standard/var_unserializer.c" +#line 674 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -655,7 +700,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 733 "ext/standard/var_unserializer.re" +#line 778 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; zend_long elements; @@ -800,7 +845,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 804 "ext/standard/var_unserializer.c" +#line 849 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -825,14 +870,14 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 726 "ext/standard/var_unserializer.re" +#line 771 "ext/standard/var_unserializer.re" { if (!var_hash) return 0; return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 836 "ext/standard/var_unserializer.c" +#line 881 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -853,7 +898,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 702 "ext/standard/var_unserializer.re" +#line 747 "ext/standard/var_unserializer.re" { zend_long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -877,7 +922,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 881 "ext/standard/var_unserializer.c" +#line 926 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -898,7 +943,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 668 "ext/standard/var_unserializer.re" +#line 713 "ext/standard/var_unserializer.re" { size_t len, maxlen; zend_string *str; @@ -932,7 +977,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) ZVAL_STR(rval, str); return 1; } -#line 936 "ext/standard/var_unserializer.c" +#line 981 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -953,7 +998,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 636 "ext/standard/var_unserializer.re" +#line 681 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -985,7 +1030,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(rval, str, len); return 1; } -#line 989 "ext/standard/var_unserializer.c" +#line 1034 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1073,7 +1118,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) } yy63: ++YYCURSOR; -#line 627 "ext/standard/var_unserializer.re" +#line 672 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 use_double: @@ -1082,7 +1127,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1086 "ext/standard/var_unserializer.c" +#line 1131 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1141,7 +1186,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 611 "ext/standard/var_unserializer.re" +#line 656 "ext/standard/var_unserializer.re" { *p = YYCURSOR; @@ -1157,7 +1202,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) return 1; } -#line 1161 "ext/standard/var_unserializer.c" +#line 1206 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1184,7 +1229,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 585 "ext/standard/var_unserializer.re" +#line 630 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1210,7 +1255,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) ZVAL_LONG(rval, parse_iv(start + 2)); return 1; } -#line 1214 "ext/standard/var_unserializer.c" +#line 1259 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1218,22 +1263,22 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 579 "ext/standard/var_unserializer.re" +#line 624 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_BOOL(rval, parse_iv(start + 2)); return 1; } -#line 1228 "ext/standard/var_unserializer.c" +#line 1273 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 573 "ext/standard/var_unserializer.re" +#line 618 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_NULL(rval); return 1; } -#line 1237 "ext/standard/var_unserializer.c" +#line 1282 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1256,7 +1301,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 548 "ext/standard/var_unserializer.re" +#line 593 "ext/standard/var_unserializer.re" { zend_long id; @@ -1281,7 +1326,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) return 1; } -#line 1285 "ext/standard/var_unserializer.c" +#line 1330 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1304,7 +1349,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 522 "ext/standard/var_unserializer.re" +#line 567 "ext/standard/var_unserializer.re" { zend_long id; @@ -1330,9 +1375,9 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) return 1; } -#line 1334 "ext/standard/var_unserializer.c" +#line 1379 "ext/standard/var_unserializer.c" } -#line 886 "ext/standard/var_unserializer.re" +#line 931 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 81cc26db9d11a..d65ddb948e8f5 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -162,6 +162,10 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) efree_size(var_dtor_hash, sizeof(var_dtor_entries)); var_dtor_hash = next; } + + if ((*var_hashx)->refs) { + zend_array_destroy((*var_hashx)->refs); + } } /* }}} */ @@ -305,11 +309,12 @@ static inline size_t parse_uiv(const unsigned char *p) #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes -static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) +static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce) { while (elements-- > 0) { - zval key, *data, d, *old_data; + zval key, *data, d, *old_data, tmp; zend_ulong idx; + zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@ -321,7 +326,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab data = NULL; ZVAL_UNDEF(&d); - if (!objprops) { + if (!ce) { if (Z_TYPE(key) == IS_LONG) { idx = Z_LVAL(key); numeric_key: @@ -353,9 +358,40 @@ string_key: if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) { if (Z_TYPE_P(old_data) == IS_INDIRECT) { old_data = Z_INDIRECT_P(old_data); + if (Z_STRVAL(key)[0] == 0) { + zend_string *member, *class; + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex(Z_STR(key), &class_name, &prop_name, &prop_name_len); + member = zend_string_init(prop_name, prop_name_len, 0); + class = zend_string_init(class_name, strlen(class_name), 0); + zend_str_tolower(ZSTR_VAL(class), ZSTR_LEN(class)); + EG(fake_scope) = class_name[0] == '*' ? ce : zend_hash_find_ptr(EG(class_table), class); + info = zend_get_property_info(EG(fake_scope), member, 1); + EG(fake_scope) = NULL; + zend_string_release(member); + zend_string_release(class); + } else { + info = zend_get_property_info(ce, Z_STR(key), 1); + } + var_push_dtor(var_hash, old_data); + old_data = zend_hash_update_ind(ht, Z_STR(key), &d); + + if (EXPECTED(!info->type)) { + info = NULL; + data = old_data; + } else { + /* little hack to disallow references */ + if (!(*var_hash)->refs) { + (*var_hash)->refs = emalloc(sizeof(HashTable)); + zend_hash_init((*var_hash)->refs, 8, NULL, ZVAL_PTR_DTOR, 0); + } + data = zend_hash_next_index_insert((*var_hash)->refs, &d); + } + } else { + var_push_dtor(var_hash, old_data); + data = zend_hash_update_ind(ht, Z_STR(key), &d); } - var_push_dtor(var_hash, old_data); - data = zend_hash_update_ind(ht, Z_STR(key), &d); } else { data = zend_hash_add_new(ht, Z_STR(key), &d); } @@ -374,6 +410,15 @@ string_key: return 0; } + if (UNEXPECTED(info)) { + data = zend_verify_property_type(info, data, &tmp, 1); + if (UNEXPECTED(!data)) { + zval_dtor(&key); + return 0; + } + ZVAL_COPY(old_data, data); + } + if (UNEXPECTED(Z_ISUNDEF_P(data))) { if (Z_TYPE(key) == IS_LONG) { zend_hash_index_del(ht, Z_LVAL(key)); @@ -466,7 +511,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJCE_P(rval))) { return 0; }