]> granicus.if.org Git - php/commitdiff
Port try/catch
authorNikita Popov <nikic@php.net>
Sat, 12 Jul 2014 11:50:58 +0000 (13:50 +0200)
committerNikita Popov <nikic@php.net>
Sat, 12 Jul 2014 11:50:58 +0000 (13:50 +0200)
Down to 50 failing Zend tests

Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y

index 9869e28a7c27285050f4fa3d8f7c0c716b0c471a..410fce578ab6ee727dc04bdc06b86ebafcc2d02a 100644 (file)
@@ -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;
index 1fbfec5896500bf93920f741543328e8cf67f35d..ccddfd616c06dfe6e36f891bc34b2b716668bc01 100644 (file)
@@ -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;
index 44d3f0278f36725f8915c02fa2a0deaa0fc84249..6170e9aa12b3ab51beb0a253702dc799c68bd595 100644 (file)
@@ -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: