Don't treat expression exit as terminator

Same as with throw expressions, this may remove later temporary
consuming instructions and thus eliminate live ranges, resulting
in a memory leak. We make use of the same hack and don't consider
exit a terminator if used in an expression context.
This commit is contained in:
Nikita Popov 2021-09-23 10:24:24 +02:00
parent 757c1277eb
commit c9762be566
3 changed files with 14 additions and 6 deletions

View file

@ -299,13 +299,13 @@ ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, u
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_MATCH_ERROR:
case ZEND_VERIFY_NEVER_TYPE:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_EXIT:
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. */

View file

@ -23,6 +23,9 @@ try {
}
var_dump(error_reporting());
// Exit also unwinds and thus has the same basic problem.
new stdClass(exit);
?>
--EXPECT--
Caught

View file

@ -9045,17 +9045,21 @@ static void zend_compile_print(znode *result, zend_ast *ast) /* {{{ */
static void zend_compile_exit(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
if (expr_ast) {
znode expr_node;
zend_compile_expr(&expr_node, expr_ast);
zend_emit_op(NULL, ZEND_EXIT, &expr_node, NULL);
} else {
zend_emit_op(NULL, ZEND_EXIT, NULL, NULL);
expr_node.op_type = IS_UNUSED;
}
zend_op *opline = zend_emit_op(NULL, ZEND_EXIT, &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_TRUE(&result->u.constant);
}
}
/* }}} */
@ -9999,6 +10003,7 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
zend_compile_halt_compiler(ast);
break;
case ZEND_AST_THROW:
case ZEND_AST_EXIT:
zend_compile_expr(NULL, ast);
break;
default: