diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ae2247b29e30d..cee7ec4361d69 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -313,9 +313,11 @@ static bool zend_is_not_imported(zend_string *name) { return !FC(imports) || zend_hash_find_ptr_lc(FC(imports), name) == NULL; } -void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */ +void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_array *op_array) /* {{{ */ { *prev_context = CG(context); + CG(context).prev = CG(context).op_array ? prev_context : NULL; + CG(context).op_array = op_array; CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE; CG(context).vars_size = 0; CG(context).literals_size = 0; @@ -2920,11 +2922,21 @@ static bool is_global_var_fetch(zend_ast *ast) static bool this_guaranteed_exists(void) /* {{{ */ { - zend_op_array *op_array = CG(active_op_array); - /* Instance methods always have a $this. - * This also includes closures that have a scope and use $this. */ - return op_array->scope != NULL - && (op_array->fn_flags & ZEND_ACC_STATIC) == 0; + zend_oparray_context *ctx = &CG(context); + while (ctx) { + /* Instance methods always have a $this. + * This also includes closures that have a scope and use $this. */ + zend_op_array *op_array = ctx->op_array; + if (op_array->fn_flags & ZEND_ACC_STATIC) { + return false; + } else if (op_array->scope) { + return true; + } else if (!(op_array->fn_flags & ZEND_ACC_CLOSURE)) { + return false; + } + ctx = ctx->prev; + } + return false; } /* }}} */ @@ -7869,7 +7881,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) op_array->fn_flags |= ZEND_ACC_TOP_LEVEL; } - zend_oparray_context_begin(&orig_oparray_context); + zend_oparray_context_begin(&orig_oparray_context, op_array); { /* Push a separator to the loop variable stack */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8efc57163da86..6a5626492ec73 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -190,6 +190,8 @@ typedef struct _zend_live_range { /* Compilation context that is different for each op array. */ typedef struct _zend_oparray_context { + struct _zend_oparray_context *prev; + zend_op_array *op_array; uint32_t opcodes_size; int vars_size; int literals_size; @@ -802,7 +804,7 @@ void init_compiler(void); void shutdown_compiler(void); void zend_init_compiler_data_structures(void); -void zend_oparray_context_begin(zend_oparray_context *prev_context); +void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_array *op_array); void zend_oparray_context_end(zend_oparray_context *prev_context); void zend_file_context_begin(zend_file_context *prev_context); void zend_file_context_end(zend_file_context *prev_context); diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 054ed7bdc1ef6..ae7e91629db72 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -614,7 +614,7 @@ static zend_op_array *zend_compile(int type) } zend_file_context_begin(&original_file_context); - zend_oparray_context_begin(&original_oparray_context); + zend_oparray_context_begin(&original_oparray_context, op_array); zend_compile_top_stmt(CG(ast)); CG(zend_lineno) = last_lineno; zend_emit_final_return(type == ZEND_USER_FUNCTION); diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index de9f594a2756b..10459da7a9c65 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1742,7 +1742,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -1790,7 +1793,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -1831,7 +1837,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -2305,7 +2314,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; op1_addr = 0; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } on_this = 1; } else { op1_info = OP1_INFO(); @@ -2456,7 +2468,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; op1_addr = 0; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } on_this = 1; } else { op1_info = OP1_INFO(); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 3f7992b4269d1..cba20edb6a560 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4692,7 +4692,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -4783,7 +4786,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -4863,7 +4869,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -5930,7 +5939,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { @@ -6209,7 +6221,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + /* scope is NULL for closures. */ + if (ce) { + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + } op1_addr = 0; on_this = 1; } else { diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 7df2cd5b9029b..fd4d83aeca71a 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -692,6 +692,12 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } } op1_type |= flags; + } else if (opline->op1_type == IS_UNUSED && (op_array->fn_flags & ZEND_ACC_CLOSURE)) { + uint32_t op1_flags = ZEND_VM_OP1_FLAGS(zend_get_opcode_flags(opline->opcode)); + if ((op1_flags & ZEND_VM_OP_MASK) == ZEND_VM_OP_THIS) { + op1_type = IS_OBJECT; + ce1 = Z_OBJCE(EX(This)); + } } if (opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV) && opline->opcode != ZEND_INSTANCEOF