From: Nikita Popov Date: Fri, 18 Jul 2014 10:58:24 +0000 (+0200) Subject: Port closures X-Git-Tag: POST_AST_MERGE^2~136 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ae5ba9abfbb257af6c0e4e67b179ddcc5d0fe028;p=php Port closures --- diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 7f885ab029..3c22fda349 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -101,6 +101,7 @@ enum _zend_ast_kind { ZEND_AST_FUNC_DECL, + ZEND_AST_CLOSURE, ZEND_AST_CLOSURE_USES, }; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7daa618803..2e8e88b00a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7004,14 +7004,9 @@ void zend_compile_closure_uses(zend_ast *ast TSRMLS_DC) { } } -void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) { - zend_ast *name_ast = ast->child[0]; - zend_ast *params_ast = ast->child[1]; - zend_ast *stmt_ast = ast->child[2]; - zend_bool returns_ref = ast->attr; - zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast)); - - zend_op_array *orig_op_array = CG(active_op_array); +static zend_op *zend_begin_func_decl( + zend_string *name, zend_bool returns_ref, zend_uint start_lineno TSRMLS_DC +) { zend_op_array *op_array = emalloc(sizeof(zend_op_array)); zend_string *lcname; zend_op *opline; @@ -7027,7 +7022,7 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) { op_array->function_name = STR_COPY(name); } - op_array->line_start = ast->lineno; + op_array->line_start = start_lineno; if (returns_ref) { op_array->fn_flags |= ZEND_ACC_RETURN_REFERENCE; } @@ -7072,10 +7067,8 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) { zend_init_compiler_context(TSRMLS_C); if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) { - zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); - - opline = emit_op(NULL, ZEND_EXT_NOP, NULL, NULL TSRMLS_CC); - opline->lineno = ast->lineno; + zend_op *opline_ext = emit_op(NULL, ZEND_EXT_NOP, NULL, NULL TSRMLS_CC); + opline_ext->lineno = start_lineno; } { @@ -7098,9 +7091,10 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) { zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline); } - zend_compile_params(params_ast TSRMLS_CC); - zend_compile_stmt(stmt_ast TSRMLS_CC); + return opline; +} +static void zend_end_func_decl(TSRMLS_D) { zend_do_extended_info(TSRMLS_C); zend_do_return(NULL, 0 TSRMLS_CC); @@ -7111,13 +7105,57 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) { // TODO.AST end line // TODO.AST doc comment - CG(active_op_array) = orig_op_array; - /* Pop the switch and foreach separators */ zend_stack_del_top(&CG(switch_cond_stack)); zend_stack_del_top(&CG(foreach_copy_stack)); } +void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) { + zend_ast *name_ast = ast->child[0]; + zend_ast *params_ast = ast->child[1]; + zend_ast *stmt_ast = ast->child[2]; + zend_bool returns_ref = ast->attr; + zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast)); + + zend_op_array *orig_op_array = CG(active_op_array); + zend_begin_func_decl(name, returns_ref, ast->lineno TSRMLS_CC); + + zend_compile_params(params_ast TSRMLS_CC); + zend_compile_stmt(stmt_ast TSRMLS_CC); + + zend_end_func_decl(TSRMLS_C); + CG(active_op_array) = orig_op_array; +} + +void zend_compile_closure(znode *result, zend_ast *ast TSRMLS_DC) { + zend_ast *params_ast = ast->child[0]; + zend_ast *uses_ast = ast->child[1]; + zend_ast *stmt_ast = ast->child[2]; + zend_bool returns_ref = ast->attr; + zend_string *name = STR_INIT("{closure}", sizeof("{closure}") - 1, 0); + + zend_op_array *orig_op_array = CG(active_op_array); + zend_op *opline = zend_begin_func_decl(name, returns_ref, ast->lineno TSRMLS_CC); + + result->op_type = IS_TMP_VAR; + result->u.op.var = get_temporary_variable(orig_op_array); + + opline->opcode = ZEND_DECLARE_LAMBDA_FUNCTION; + zend_del_literal(orig_op_array, opline->op2.constant); + SET_UNUSED(opline->op2); + SET_NODE(opline->result, result); + CG(active_op_array)->fn_flags |= ZEND_ACC_CLOSURE; + + zend_compile_closure_uses(uses_ast TSRMLS_CC); + zend_compile_params(params_ast TSRMLS_CC); + zend_compile_stmt(stmt_ast TSRMLS_CC); + + zend_end_func_decl(TSRMLS_C); + CG(active_op_array) = orig_op_array; + + STR_RELEASE(name); +} + void zend_compile_binary_op(znode *result, zend_ast *ast TSRMLS_DC) { zend_ast *left_ast = ast->child[0]; zend_ast *right_ast = ast->child[1]; @@ -8083,6 +8121,9 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) { case ZEND_AST_MAGIC_CONST: zend_compile_magic_const(result, ast TSRMLS_CC); return; + case ZEND_AST_CLOSURE: + zend_compile_closure(result, ast TSRMLS_CC); + return; default: ZEND_ASSERT(0 /* not supported */); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 50263eed8a..7fdf5eb8e6 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -869,9 +869,9 @@ expr_without_variable: | T_YIELD expr { $$.u.ast = zend_ast_create_binary(ZEND_YIELD, $2.u.ast, NULL); } | T_YIELD expr T_DOUBLE_ARROW expr { $$.u.ast = zend_ast_create_binary(ZEND_YIELD, $4.u.ast, $2.u.ast); } - | function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); } - '(' parameter_list ')' { zend_compile_params($5.u.ast TSRMLS_CC); zend_ast_destroy($5.u.ast); } lexical_vars { zend_compile_closure_uses($8.u.ast TSRMLS_CC); if ($8.u.ast) zend_ast_destroy($8.u.ast); } - '{' inner_statement_list '}' { AS($11); zend_do_end_function_declaration(&$1 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$3); } + | function is_reference '(' parameter_list ')' lexical_vars '{' inner_statement_list '}' + { $$.u.ast = zend_ast_create_ex(3, ZEND_AST_CLOSURE, + $2.op_type, $4.u.ast, $6.u.ast, $8.u.ast); } | T_STATIC function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, 1 TSRMLS_CC); } '(' parameter_list ')' { zend_compile_params($6.u.ast TSRMLS_CC); zend_ast_destroy($6.u.ast); } lexical_vars { zend_compile_closure_uses($9.u.ast TSRMLS_CC); if ($9.u.ast) zend_ast_destroy($9.u.ast); } '{' inner_statement_list '}' { AS($12); zend_do_end_function_declaration(&$2 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$4); }