}
/* }}} */
-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;
}
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) {
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;
{ $$.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: