]> granicus.if.org Git - php/commitdiff
Port closure use
authorNikita Popov <nikic@php.net>
Fri, 18 Jul 2014 10:30:39 +0000 (12:30 +0200)
committerNikita Popov <nikic@php.net>
Fri, 18 Jul 2014 10:30:39 +0000 (12:30 +0200)
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y

index 864503db7245e94dc5976b15074bb26be912ccaf..7f885ab029c1f9a43d7b4a2bfe93a89cce4b83eb 100644 (file)
@@ -100,6 +100,8 @@ enum _zend_ast_kind {
        ZEND_AST_TYPE,
 
        ZEND_AST_FUNC_DECL,
+
+       ZEND_AST_CLOSURE_USES,
 };
 
 typedef unsigned short zend_ast_kind;
index 145f98981a6d73ff498749a2268749eb5370cdcb..7daa61880318ffdd177bb8215a68d5d2000e9154 100644 (file)
@@ -6200,26 +6200,13 @@ void zend_compile_global_var(zend_ast *ast TSRMLS_DC) {
        efree(fetch_ast);
 }
 
-void zend_compile_static_var(zend_ast *ast TSRMLS_DC) {
-       zend_ast *var_ast = ast->child[0];
-       zend_ast *value_ast = ast->child[1];
-
+static void zend_compile_static_var_common(
+       zend_ast *var_ast, zval *value, zend_bool by_ref TSRMLS_DC
+) {
        znode var_node, result;
-       zval value_zv;
        zend_op *opline;
 
        zend_compile_expr(&var_node, var_ast TSRMLS_CC);
-       if (var_node.op_type == IS_CONST) {
-               if (Z_TYPE(var_node.u.constant) != IS_STRING) {
-                       convert_to_string(&var_node.u.constant);
-               }
-       }
-
-       if (value_ast) {
-               _tmp_compile_const_expr(&value_zv, value_ast TSRMLS_CC);
-       } else {
-               ZVAL_NULL(&value_zv);
-       }
 
        if (!CG(active_op_array)->static_variables) {
                if (CG(active_op_array)->scope) {
@@ -6229,15 +6216,40 @@ void zend_compile_static_var(zend_ast *ast TSRMLS_DC) {
                zend_hash_init(CG(active_op_array)->static_variables, 8, NULL, ZVAL_PTR_DTOR, 0);
        }
 
-       zend_hash_update(CG(active_op_array)->static_variables,
-               Z_STR(var_node.u.constant), &value_zv);
+       zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value);
 
-       opline = emit_op(&result, ZEND_FETCH_W, &var_node, NULL TSRMLS_CC);
+       opline = emit_op(&result, by_ref ? ZEND_FETCH_W : ZEND_FETCH_R, &var_node, NULL TSRMLS_CC);
        opline->extended_value = ZEND_FETCH_STATIC;
 
-       zend_ast *fetch_ast = zend_ast_create_unary(ZEND_AST_VAR, var_ast);
-       zend_compile_assign_ref_common(NULL, fetch_ast, &result TSRMLS_CC);
-       efree(fetch_ast);
+       if (by_ref) {
+               zend_ast *fetch_ast = zend_ast_create_unary(ZEND_AST_VAR, var_ast);
+               zend_compile_assign_ref_common(NULL, fetch_ast, &result TSRMLS_CC);
+               efree(fetch_ast);
+       } else {
+               zend_ast *fetch_ast = zend_ast_create_unary(ZEND_AST_VAR, var_ast);
+               zend_ast *znode_ast = zend_ast_create_znode(&result);
+               zend_ast *assign_ast = zend_ast_create_binary(ZEND_AST_ASSIGN, fetch_ast, znode_ast);
+               znode dummy_node;
+               zend_compile_expr(&dummy_node, assign_ast TSRMLS_CC);
+               zend_do_free(&dummy_node TSRMLS_CC);
+               efree(znode_ast);
+               efree(assign_ast);
+               efree(fetch_ast);
+       }
+}
+
+void zend_compile_static_var(zend_ast *ast TSRMLS_DC) {
+       zend_ast *var_ast = ast->child[0];
+       zend_ast *value_ast = ast->child[1];
+       zval value_zv;
+
+       if (value_ast) {
+               _tmp_compile_const_expr(&value_zv, value_ast TSRMLS_CC);
+       } else {
+               ZVAL_NULL(&value_zv);
+       }
+
+       zend_compile_static_var_common(var_ast, &value_zv, 1 TSRMLS_CC);
 }
 
 void zend_compile_unset(zend_ast *ast TSRMLS_DC) {
@@ -6968,6 +6980,30 @@ void zend_compile_params(zend_ast *ast TSRMLS_DC) {
        op_array->arg_info = arg_infos;
 }
 
+void zend_compile_closure_uses(zend_ast *ast TSRMLS_DC) {
+       zend_uint i;
+
+       if (!ast) {
+               return;
+       }
+
+       for (i = 0; i < ast->children; ++i) {
+               zend_ast *var_ast = ast->child[i];
+               zend_string *name = Z_STR_P(zend_ast_get_zval(var_ast));
+               zend_bool by_ref = var_ast->attr;
+               zval zv;
+
+               if (name->len == sizeof("this") - 1 && !memcmp(name->val, "this", sizeof("this") - 1)) {
+                       zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
+               }
+
+               ZVAL_NULL(&zv);
+               Z_CONST_FLAGS(zv) = by_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR;
+
+               zend_compile_static_var_common(var_ast, &zv, by_ref TSRMLS_CC);
+       }
+}
+
 void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
        zend_ast *name_ast = ast->child[0];
        zend_ast *params_ast = ast->child[1];
index f48cd3285a83cc44f8b5a88623f5106a927802b8..50263eed8acdcfab20442d61f1f1193ff5b9a0db 100644 (file)
@@ -870,11 +870,11 @@ expr_without_variable:
        |       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
-               '{' inner_statement_list '}' { AS($10); zend_do_end_function_declaration(&$1 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$3); }
+               '(' 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); }
        |       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
-               '{' inner_statement_list '}' { AS($11); zend_do_end_function_declaration(&$2 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$4); }
+               '(' 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); }
 ;
 
 function:
@@ -882,15 +882,18 @@ function:
 ;
 
 lexical_vars:
-               /* empty */
-       |       T_USE '(' lexical_var_list ')'
+               /* empty */ { $$.u.ast = NULL; }
+       |       T_USE '(' lexical_var_list ')' { $$.u.ast = $3.u.ast; }
 ;
 
 lexical_var_list:
-               lexical_var_list ',' T_VARIABLE                 { zend_do_fetch_lexical_variable(&$3, 0 TSRMLS_CC); }
-       |       lexical_var_list ',' '&' T_VARIABLE             { zend_do_fetch_lexical_variable(&$4, 1 TSRMLS_CC); }
-       |       T_VARIABLE                                                              { zend_do_fetch_lexical_variable(&$1, 0 TSRMLS_CC); }
-       |       '&' T_VARIABLE                                                  { zend_do_fetch_lexical_variable(&$2, 1 TSRMLS_CC); }
+               lexical_var_list ',' lexical_var { $$.u.ast = zend_ast_dynamic_add($1.u.ast, $3.u.ast); }
+       |       lexical_var { $$.u.ast = zend_ast_create_dynamic_and_add(ZEND_AST_CLOSURE_USES, $1.u.ast); }
+;
+
+lexical_var:
+               T_VARIABLE              { $$.u.ast = AST_ZVAL(&$1); }
+       |       '&' T_VARIABLE  { $$.u.ast = zend_ast_create_zval_ex(&$2.u.constant, 1); }
 ;
 
 function_call: