From: Nikita Popov Date: Sat, 12 Jul 2014 11:50:58 +0000 (+0200) Subject: Port try/catch X-Git-Tag: POST_AST_MERGE^2~152 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e367e68111bb557c00fc008759b8fc7310587843;p=php Port try/catch Down to 50 failing Zend tests --- diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 9869e28a7c..410fce578a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -84,9 +84,14 @@ enum _zend_ast_kind { ZEND_AST_REF, ZEND_AST_IF, ZEND_AST_IF_ELEM, + ZEND_AST_SWITCH, ZEND_AST_SWITCH_LIST, ZEND_AST_SWITCH_CASE, + + ZEND_AST_TRY, + ZEND_AST_CATCH_LIST, + ZEND_AST_CATCH, }; typedef unsigned short zend_ast_kind; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 1fbfec5896..ccddfd616c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2153,20 +2153,26 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ -static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ +static zend_uint zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ { - int try_catch_offset = CG(active_op_array)->last_try_catch++; + zend_op_array *op_array = CG(active_op_array); + zend_uint try_catch_offset = op_array->last_try_catch++; + zend_try_catch_element *elem; + + op_array->try_catch_array = safe_erealloc( + op_array->try_catch_array, sizeof(zend_try_catch_element), op_array->last_try_catch, 0); + + elem = &op_array->try_catch_array[try_catch_offset]; + elem->try_op = try_op; + elem->catch_op = 0; + elem->finally_op = 0; + elem->finally_end = 0; - CG(active_op_array)->try_catch_array = erealloc(CG(active_op_array)->try_catch_array, sizeof(zend_try_catch_element)*CG(active_op_array)->last_try_catch); - CG(active_op_array)->try_catch_array[try_catch_offset].try_op = try_op; - CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = 0; - CG(active_op_array)->try_catch_array[try_catch_offset].finally_op = 0; - CG(active_op_array)->try_catch_array[try_catch_offset].finally_end = 0; return try_catch_offset; } /* }}} */ -static void zend_add_catch_element(int offset, zend_uint catch_op TSRMLS_DC) /* {{{ */ +static void zend_add_catch_element(zend_uint offset, zend_uint catch_op TSRMLS_DC) /* {{{ */ { CG(active_op_array)->try_catch_array[offset].catch_op = catch_op; } @@ -7400,6 +7406,95 @@ void zend_compile_switch(zend_ast *ast TSRMLS_DC) { efree(jmpnz_opnums); } +void zend_compile_try(zend_ast *ast TSRMLS_DC) { + zend_ast *try_ast = ast->child[0]; + zend_ast *catches_ast = ast->child[1]; + zend_ast *finally_ast = ast->child[2]; + + zend_uint i; + zend_op *opline; + zend_uint try_catch_offset = zend_add_try_element( + get_next_op_number(CG(active_op_array)) TSRMLS_CC); + zend_uint *jmp_opnums = safe_emalloc(sizeof(zend_uint), catches_ast->children + 1, 0); + + if (catches_ast->children == 0 && !finally_ast) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally"); + } + + zend_compile_stmt(try_ast TSRMLS_CC); + + jmp_opnums[0] = get_next_op_number(CG(active_op_array)); + opline = emit_op(NULL, ZEND_JMP, NULL, NULL TSRMLS_CC); // TODO + + for (i = 0; i < catches_ast->children; ++i) { + zend_ast *catch_ast = catches_ast->child[i]; + zend_ast *class_ast = catch_ast->child[0]; + zend_ast *var_ast = catch_ast->child[1]; + zend_ast *stmt_ast = catch_ast->child[2]; + zval *var_name = zend_ast_get_zval(var_ast); + + znode class_node; + zend_uint opnum_catch; + + if (zend_is_const_default_class_ref(class_ast)) { + zend_compile_expr(&class_node, class_ast TSRMLS_CC); + zend_resolve_class_name_old(&class_node TSRMLS_CC); + } else { + zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement"); + } + + opnum_catch = get_next_op_number(CG(active_op_array)); + zend_add_catch_element(try_catch_offset, opnum_catch TSRMLS_CC); + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_CATCH; + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_class_name_literal( + CG(active_op_array), &class_node.u.constant TSRMLS_CC); + opline->op2_type = IS_CV; + opline->op2.var = lookup_cv(CG(active_op_array), Z_STR_P(var_name) TSRMLS_CC); + opline->result.num = (i == ast->children - 1); /* Whether this is the last catch */ + + zend_compile_stmt(stmt_ast TSRMLS_CC); + + jmp_opnums[i + 1] = get_next_op_number(CG(active_op_array)); + emit_op(NULL, ZEND_JMP, NULL, NULL TSRMLS_CC); + + opline = &CG(active_op_array)->opcodes[opnum_catch]; + opline->extended_value = get_next_op_number(CG(active_op_array)); + } + + for (i = 0; i < catches_ast->children + 1; ++i) { + opline = &CG(active_op_array)->opcodes[jmp_opnums[i]]; + opline->op1.opline_num = get_next_op_number(CG(active_op_array)); + } + + if (finally_ast) { + zend_uint opnum_jmp = get_next_op_number(CG(active_op_array)) + 1; + + opline = emit_op(NULL, ZEND_FAST_CALL, NULL, NULL TSRMLS_CC); + opline->op1.opline_num = opnum_jmp + 1; + + emit_op(NULL, ZEND_JMP, NULL, NULL TSRMLS_CC); + + CG(context).in_finally++; + zend_compile_stmt(finally_ast TSRMLS_CC); + CG(context).in_finally--; + + CG(active_op_array)->try_catch_array[try_catch_offset].finally_op = opnum_jmp + 1; + CG(active_op_array)->try_catch_array[try_catch_offset].finally_end + = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->has_finally_block = 1; + + emit_op(NULL, ZEND_FAST_RET, NULL, NULL TSRMLS_CC); + + opline = &CG(active_op_array)->opcodes[opnum_jmp]; + opline->op1.opline_num = get_next_op_number(CG(active_op_array)); + } + + efree(jmp_opnums); +} + void zend_compile_stmt_list(zend_ast *ast TSRMLS_DC) { zend_uint i; for (i = 0; i < ast->children; ++i) { @@ -8203,6 +8298,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) { case ZEND_AST_SWITCH: zend_compile_switch(ast TSRMLS_CC); break; + case ZEND_AST_TRY: + zend_compile_try(ast TSRMLS_CC); + break; default: { znode result; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 44d3f0278f..6170e9aa12 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -358,39 +358,33 @@ unticked_statement: { $$.u.ast = zend_ast_create(4, ZEND_AST_FOREACH, $3.u.ast, $7.u.ast, $5.u.ast, $9.u.ast); } | T_DECLARE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); AN($$); } - | ';' /* empty statement */ { AN($$); } - | T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}' { AS($4); } + | ';' /* empty statement */ { $$.u.ast = NULL; } + | T_TRY '{' inner_statement_list '}' catch_list finally_statement + { $$.u.ast = zend_ast_create_ternary(ZEND_AST_TRY, $3.u.ast, $5.u.ast, $6.u.ast); } + /*| T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}' { AS($4); } catch_statement { zend_do_bind_catch(&$1, &$7 TSRMLS_CC); } - finally_statement { zend_do_end_finally(&$1, &$6, &$8 TSRMLS_CC); AN($$); } + finally_statement { zend_do_end_finally(&$1, &$6, &$8 TSRMLS_CC); AN($$); }*/ | T_THROW expr ';' { $$.u.ast = zend_ast_create_unary(ZEND_THROW, $2.u.ast); } | T_GOTO T_STRING ';' { $$.u.ast = zend_ast_create_unary(ZEND_GOTO, AST_ZVAL(&$2)); } ; -catch_statement: - /* empty */ { $$.op_type = IS_UNUSED; } - | T_CATCH '(' { zend_initialize_try_catch_element(&$1 TSRMLS_CC); } +catch_list: + /* empty */ + { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_CATCH_LIST); } + | catch_list T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}' + { $$.u.ast = zend_ast_dynamic_add($1.u.ast, + zend_ast_create_ternary(ZEND_AST_CATCH, $4.u.ast, AST_ZVAL(&$5), $8.u.ast)); } + /*| T_CATCH '(' { zend_initialize_try_catch_element(&$1 TSRMLS_CC); } fully_qualified_class_name { zend_do_first_catch(&$2 TSRMLS_CC); } T_VARIABLE ')' { zend_do_begin_catch(&$1, &$4, &$6, &$2 TSRMLS_CC); } '{' inner_statement_list '}' { AS($10); zend_do_end_catch(&$1 TSRMLS_CC); } - additional_catches { zend_do_mark_last_catch(&$2, &$13 TSRMLS_CC); $$ = $1;} - -finally_statement: - /* empty */ { $$.op_type = IS_UNUSED; } - | T_FINALLY { zend_do_finally(&$1 TSRMLS_CC); } '{' inner_statement_list '}' { AS($4); $$ = $1; } + additional_catches { zend_do_mark_last_catch(&$2, &$13 TSRMLS_CC); $$ = $1;}*/ ; -additional_catches: - non_empty_additional_catches { $$ = $1; } - | /* empty */ { $$.u.op.opline_num = -1; } -; - -non_empty_additional_catches: - additional_catch { $$ = $1; } - | non_empty_additional_catches additional_catch { $$ = $2; } -; - -additional_catch: - T_CATCH '(' fully_qualified_class_name { $$.u.op.opline_num = get_next_op_number(CG(active_op_array)); } T_VARIABLE ')' { zend_do_begin_catch(&$1, &$3, &$5, NULL TSRMLS_CC); } '{' inner_statement_list '}' { AS($9); zend_do_end_catch(&$1 TSRMLS_CC); } +finally_statement: + /* empty */ { $$.u.ast = NULL; } + | T_FINALLY '{' inner_statement_list '}' { $$.u.ast = $3.u.ast; } + /*| T_FINALLY { zend_do_finally(&$1 TSRMLS_CC); } '{' inner_statement_list '}' { AS($4); $$ = $1; }*/ ; unset_variables: