diff --git a/Zend/tests/throw/leaks.phpt b/Zend/tests/throw/leaks.phpt new file mode 100644 index 0000000000000..072f65f2fc80c --- /dev/null +++ b/Zend/tests/throw/leaks.phpt @@ -0,0 +1,31 @@ +--TEST-- +throw expression should not leak temporaries +--FILE-- + +--EXPECT-- +Caught +Caught +Caught +int(32767) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 381a180f2953c..46a943121d8f2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4548,10 +4548,13 @@ void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */ znode expr_node; zend_compile_expr(&expr_node, expr_ast); - zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL); - - result->op_type = IS_CONST; - ZVAL_BOOL(&result->u.constant, 1); + zend_op *opline = zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL); + if (result) { + /* Mark this as an "expression throw" for opcache. */ + opline->extended_value = ZEND_THROW_IS_EXPR; + result->op_type = IS_CONST; + ZVAL_BOOL(&result->u.constant, 1); + } } /* }}} */ @@ -8789,6 +8792,9 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ case ZEND_AST_HALT_COMPILER: zend_compile_halt_compiler(ast); break; + case ZEND_AST_THROW: + zend_compile_throw(NULL, ast); + break; default: { znode result; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8631747c06300..a90219b1b4453 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -927,6 +927,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_SEND_BY_REF 1u #define ZEND_SEND_PREFER_REF 2u +#define ZEND_THROW_IS_EXPR 1u + /* The send mode and is_variadic flag are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT #define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2)) diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index c5b1b53a0ec63..6ac5781e09dca 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -296,11 +296,17 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: - case ZEND_THROW: if (i + 1 < op_array->last) { BB_START(i + 1); } break; + case ZEND_THROW: + /* Don't treat THROW as terminator if it's used in expression context, + * as we may lose live ranges when eliminating unreachable code. */ + if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) { + BB_START(i + 1); + } + break; case ZEND_INCLUDE_OR_EVAL: flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; case ZEND_GENERATOR_CREATE: