@@ -1087,6 +1087,48 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
10871087#endif
10881088 }
10891089
1090+ /* Elide the run-time type check on typed property writes (ASSIGN_OBJ)
1091+ * when the assigned value is statically proven to already satisfy the
1092+ * property type, so no verification or coercion is required. The flag is
1093+ * read by the ASSIGN_OBJ handler from the following OP_DATA opline. */
1094+ for (int i = 0 ; i < (int ) op_array -> last ; i ++ ) {
1095+ zend_op * op = op_array -> opcodes + i ;
1096+ if (op -> opcode != ZEND_ASSIGN_OBJ ) {
1097+ continue ;
1098+ }
1099+
1100+ const zend_property_info * prop_info = zend_fetch_prop_info (op_array , ssa , op , & ssa -> ops [i ]);
1101+ if (!prop_info
1102+ || !ZEND_TYPE_IS_SET (prop_info -> type )
1103+ || !ZEND_TYPE_IS_ONLY_MASK (prop_info -> type )
1104+ || prop_info -> hooks
1105+ || (prop_info -> flags & (ZEND_ACC_READONLY | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_VIRTUAL ))) {
1106+ continue ;
1107+ }
1108+
1109+ /* The assigned value lives in the following OP_DATA opline. */
1110+ const zend_op * data = op + 1 ;
1111+ uint32_t val_type ;
1112+ if (data -> op1_type == IS_CONST ) {
1113+ val_type = _const_op_type (CRT_CONSTANT (data -> op1 ));
1114+ } else if (ssa -> ops [i + 1 ].op1_use >= 0 ) {
1115+ val_type = ssa -> var_info [ssa -> ops [i + 1 ].op1_use ].type ;
1116+ } else {
1117+ continue ;
1118+ }
1119+
1120+ if (val_type & (MAY_BE_REF | MAY_BE_UNDEF )) {
1121+ continue ;
1122+ }
1123+
1124+ uint32_t pure = val_type & MAY_BE_ANY ;
1125+ if (!pure || (pure & ~ZEND_TYPE_PURE_MASK (prop_info -> type ))) {
1126+ continue ;
1127+ }
1128+
1129+ op_array -> opcodes [i + 1 ].extended_value |= ZEND_ASSIGN_OBJ_SKIP_TYPE_CHECK ;
1130+ }
1131+
10901132 for (v = op_array -> last_var ; v < ssa -> vars_count ; v ++ ) {
10911133
10921134 op_1 = ssa -> vars [v ].definition ;
0 commit comments