@@ -6418,6 +6418,12 @@ static bool can_match_use_jumptable(zend_ast_list *arms) {
64186418
64196419static void zend_compile_pattern (zend_ast * * ast_ptr , void * context );
64206420
6421+ typedef struct {
6422+ bool inside_or_pattern ;
6423+ uint32_t bindings_var ;
6424+ uint32_t binding_offset ;
6425+ } zend_compile_pattern_context ;
6426+
64216427static void zend_compile_match (znode * result , zend_ast * ast )
64226428{
64236429 zend_ast * expr_ast = ast -> child [0 ];
@@ -6491,7 +6497,23 @@ static void zend_compile_match(znode *result, zend_ast *ast)
64916497 } else {
64926498 zend_ast * * pattern_ast_ptr = & cond_ast -> child [1 ];
64936499
6494- zend_compile_pattern (pattern_ast_ptr , NULL );
6500+ znode bindings_node ;
6501+ bindings_node .op_type = IS_TMP_VAR ;
6502+ bindings_node .u .op .var = get_temporary_variable ();
6503+
6504+ uint32_t is_opnum = get_next_op_number ();
6505+ zend_op * is_opline = zend_emit_op_tmp (NULL , ZEND_IS , & expr_node , & bindings_node );
6506+ SET_NODE (is_opline -> result , & case_node );
6507+ if (is_opline -> op1_type == IS_CONST ) {
6508+ Z_TRY_ADDREF_P (CT_CONSTANT (is_opline -> op1 ));
6509+ }
6510+
6511+ uint32_t op_data_opnum = get_next_op_number ();
6512+ zend_emit_op_data (NULL );
6513+
6514+ zend_compile_pattern_context pattern_context = { .bindings_var = bindings_node .u .op .var };
6515+ zend_compile_pattern (pattern_ast_ptr , & pattern_context );
6516+
64956517 zval pattern_zv ;
64966518 ZVAL_AST (& pattern_zv , zend_ast_copy (* pattern_ast_ptr ));
64976519 zend_ast_destroy (* pattern_ast_ptr );
@@ -6500,11 +6522,20 @@ static void zend_compile_match(znode *result, zend_ast *ast)
65006522 znode pattern_node ;
65016523 pattern_node .op_type = IS_CONST ;
65026524 ZVAL_COPY (& pattern_node .u .constant , & pattern_zv );
6525+ zend_op * op_data_op = & CG (active_op_array )-> opcodes [op_data_opnum ];
6526+ SET_NODE (op_data_op -> op1 , & pattern_node );
6527+ op_data_op -> extended_value = pattern_context .binding_offset ;
65036528
6504- zend_op * opline = zend_emit_op_tmp (NULL , ZEND_IS , & expr_node , & pattern_node );
6505- SET_NODE (opline -> result , & case_node );
6506- if (opline -> op1_type == IS_CONST ) {
6507- Z_TRY_ADDREF_P (CT_CONSTANT (opline -> op1 ));
6529+ is_opline = & CG (active_op_array )-> opcodes [is_opnum ];
6530+ is_opline -> extended_value = get_next_op_number ();
6531+
6532+ if (pattern_context .binding_offset ) {
6533+ zend_emit_op (NULL , ZEND_FREE , & bindings_node , NULL );
6534+ }
6535+
6536+ if (expr_node .op_type & (IS_VAR |IS_TMP_VAR )) {
6537+ // FIXME: Verify live ranges recognizes that OP1 needs to be freed if an exception occurs
6538+ zend_emit_op (NULL , ZEND_FREE , & expr_node , NULL );
65086539 }
65096540 }
65106541
@@ -6658,16 +6689,37 @@ static void zend_compile_class_const_pattern(zend_ast **ast_ptr)
66586689 // zend_compile_pattern_class_name(ast);
66596690}
66606691
6661- static void zend_compile_binding_pattern (zend_ast * * ast_ptr )
6692+ static void zend_compile_binding_pattern (zend_ast * * ast_ptr , zend_compile_pattern_context * context )
66626693{
66636694 zend_ast * binding_pattern_ast = * ast_ptr ;
66646695 zend_ast * var_name_ast = binding_pattern_ast -> child [0 ];
66656696 zend_string * var_name = zend_ast_get_str (var_name_ast );
66666697 uint32_t var = lookup_cv (var_name );
66676698
6699+ uint32_t binding_offset = context -> binding_offset ++ ;
6700+
66686701 zend_ast_destroy (var_name_ast );
66696702 binding_pattern_ast -> child [0 ] = NULL ;
6670- binding_pattern_ast -> attr = var ;
6703+ binding_pattern_ast -> attr = binding_offset ;
6704+
6705+ znode bindings_node ;
6706+ bindings_node .op_type = IS_TMP_VAR ;
6707+ bindings_node .u .op .var = context -> bindings_var ;
6708+
6709+ znode offset_node ;
6710+ offset_node .op_type = IS_CONST ;
6711+ ZVAL_LONG (& offset_node .u .constant , binding_offset );
6712+
6713+ znode copy_tmp_node ;
6714+ zend_emit_op_tmp (& copy_tmp_node , ZEND_COPY_TMP , & bindings_node , NULL );
6715+
6716+ znode dim_result ;
6717+ zend_emit_op_tmp (& dim_result , ZEND_FETCH_DIM_R , & copy_tmp_node , & offset_node );
6718+
6719+ znode var_node ;
6720+ var_node .op_type = IS_CV ;
6721+ var_node .u .op .var = var ;
6722+ zend_emit_op_tmp (NULL , ZEND_ASSIGN , & var_node , & dim_result );
66716723}
66726724
66736725static void zend_compile_array_pattern (zend_ast * * ast_ptr )
@@ -6719,19 +6771,15 @@ static void zend_compile_and_pattern(zend_ast **ast_ptr)
67196771 verify_parenthesized_compound_pattern (ast_ptr , ZEND_AST_OR_PATTERN );
67206772}
67216773
6722- struct zend_compile_pattern_context {
6723- bool inside_or_pattern ;
6724- };
6725-
67266774static void zend_compile_pattern (zend_ast * * ast_ptr , void * context )
67276775{
67286776 zend_ast * ast = * ast_ptr ;
67296777 if (ast == NULL || ast -> kind == ZEND_AST_ZVAL ) {
67306778 return ;
67316779 }
67326780
6733- struct zend_compile_pattern_context * pattern_context = context ;
6734- struct zend_compile_pattern_context tmp_context = {0 };
6781+ zend_compile_pattern_context * pattern_context = context ;
6782+ zend_compile_pattern_context tmp_context = {0 };
67356783 if (!pattern_context ) {
67366784 pattern_context = & tmp_context ;
67376785 }
@@ -6745,7 +6793,7 @@ static void zend_compile_pattern(zend_ast **ast_ptr, void *context)
67456793 if (pattern_context -> inside_or_pattern ) {
67466794 zend_throw_exception (zend_ce_compile_error , "Must not bind to variables inside | pattern" , 0 );
67476795 }
6748- zend_compile_binding_pattern (ast_ptr );
6796+ zend_compile_binding_pattern (ast_ptr , context );
67496797 break ;
67506798 case ZEND_AST_ARRAY_PATTERN :
67516799 zend_compile_array_pattern (ast_ptr );
@@ -6774,7 +6822,19 @@ static void zend_compile_is(znode *result, zend_ast *ast)
67746822 znode expr_node ;
67756823 zend_compile_expr (& expr_node , expr_ast );
67766824
6777- zend_compile_pattern (pattern_ast_ptr , NULL );
6825+ znode bindings_node ;
6826+ bindings_node .op_type = IS_TMP_VAR ;
6827+ bindings_node .u .op .var = get_temporary_variable ();
6828+
6829+ uint32_t is_opnum = get_next_op_number ();
6830+ zend_emit_op_tmp (result , ZEND_IS , & expr_node , & bindings_node );
6831+
6832+ uint32_t op_data_opnum = get_next_op_number ();
6833+ zend_emit_op_data (NULL );
6834+
6835+ zend_compile_pattern_context pattern_context = { .bindings_var = bindings_node .u .op .var };
6836+ zend_compile_pattern (pattern_ast_ptr , & pattern_context );
6837+
67786838 zval pattern_zv ;
67796839 ZVAL_AST (& pattern_zv , zend_ast_copy (* pattern_ast_ptr ));
67806840 zend_ast_destroy (* pattern_ast_ptr );
@@ -6783,8 +6843,17 @@ static void zend_compile_is(znode *result, zend_ast *ast)
67836843 znode pattern_node ;
67846844 pattern_node .op_type = IS_CONST ;
67856845 ZVAL_COPY (& pattern_node .u .constant , & pattern_zv );
6846+ zend_op * op_data_op = & CG (active_op_array )-> opcodes [op_data_opnum ];
6847+ SET_NODE (op_data_op -> op1 , & pattern_node );
6848+ op_data_op -> extended_value = pattern_context .binding_offset ;
6849+
6850+ zend_op * is_opline = & CG (active_op_array )-> opcodes [is_opnum ];
6851+ is_opline -> extended_value = get_next_op_number ();
6852+
6853+ if (pattern_context .binding_offset ) {
6854+ zend_emit_op (NULL , ZEND_FREE , & bindings_node , NULL );
6855+ }
67866856
6787- zend_emit_op_tmp (result , ZEND_IS , & expr_node , & pattern_node );
67886857 if (expr_node .op_type & (IS_VAR |IS_TMP_VAR )) {
67896858 // FIXME: Verify live ranges recognizes that OP1 needs to be freed if an exception occurs
67906859 zend_emit_op (NULL , ZEND_FREE , & expr_node , NULL );
0 commit comments