]> granicus.if.org Git - php/commitdiff
Preliminary function decl support
authorNikita Popov <nikic@php.net>
Tue, 15 Jul 2014 22:06:41 +0000 (00:06 +0200)
committerNikita Popov <nikic@php.net>
Tue, 15 Jul 2014 22:07:09 +0000 (00:07 +0200)
Problem: __FUNCTION__ etc don't work anymore, due to lexer/parser
interdependency.

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

index 5ef590d2e78ee626169fd646583af4088804ce6c..1e40455022403c55312203704923c142e3e3f832 100644 (file)
@@ -55,17 +55,17 @@ static zend_ast *zend_ast_create_from_va_list(
        ast = emalloc(sizeof(zend_ast) + (children - 1) * sizeof(zend_ast *));
        ast->kind = kind;
        ast->attr = attr;
-       ast->lineno = 0;
+       ast->lineno = UINT_MAX;
        ast->children = children;
 
        for (i = 0; i < children; ++i) {
                ast->child[i] = va_arg(va, zend_ast *);
-               if (ast->lineno == 0 && ast->child[i] != NULL) {
+               if (ast->child[i] != NULL && ast->child[i]->lineno < ast->lineno) {
                        ast->lineno = ast->child[i]->lineno;
                }
        }
 
-       /*if (ast->lineno == 0) {
+       /*if (ast->lineno == UINT_MAX) {
                ast->lineno = CG(zend_lineno);
        }*/
 
index f0f8e9546ab34a932f34092d6107a8a9f4129b05..dde544804ef23c9e38b830fb388ba70432a1cab7 100644 (file)
@@ -97,6 +97,8 @@ enum _zend_ast_kind {
        ZEND_AST_PARAM_LIST,
        ZEND_AST_PARAM,
        ZEND_AST_TYPE,
+
+       ZEND_AST_FUNC_DECL,
 };
 
 typedef unsigned short zend_ast_kind;
index b633d7f1e14470e2f76ba522d58119aa2f0c4fbe..ef24086a34f43912ce4490a6c2d12c491f172cab 100644 (file)
@@ -6960,6 +6960,120 @@ void zend_compile_params(zend_ast *ast TSRMLS_DC) {
        op_array->arg_info = arg_infos;
 }
 
+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_op_array *op_array = emalloc(sizeof(zend_op_array));
+       zend_string *lcname;
+       zend_op *opline;
+
+       // TODO.AST interactive (not just here - also bpc etc!)
+       
+       init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);
+
+       if (Z_TYPE(CG(current_namespace)) != IS_UNDEF) {
+               op_array->function_name = name = zend_concat_names(
+                       Z_STRVAL(CG(current_namespace)), Z_STRLEN(CG(current_namespace)), name->val, name->len);
+       } else {
+               op_array->function_name = STR_COPY(name);
+       }
+
+       op_array->line_start = ast->lineno;
+       if (returns_ref) {
+               op_array->fn_flags |= ZEND_ACC_RETURN_REFERENCE;
+       }
+
+       lcname = STR_ALLOC(name->len, 0);
+       zend_str_tolower_copy(lcname->val, name->val, name->len);
+
+       if (CG(current_import_function)) {
+               zend_string *import_name = zend_hash_find_ptr(CG(current_import_function), lcname);
+               if (import_name) {
+                       char *import_name_lc = zend_str_tolower_dup(import_name->val, import_name->len);
+
+                       if (import_name->len != name->len
+                               || memcmp(import_name_lc, lcname->val, name->len)
+                       ) {
+                               zend_error(E_COMPILE_ERROR, "Cannot declare function %s "
+                                       "because the name is already in use", name->val);
+                       }
+
+                       efree(import_name_lc);
+               }
+       }
+
+       opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+       opline->opcode = ZEND_DECLARE_FUNCTION;
+       opline->extended_value = ZEND_DECLARE_FUNCTION;
+       opline->op2_type = IS_CONST;
+       LITERAL_STR(opline->op2, lcname);
+
+       {
+               zval key;
+               build_runtime_defined_function_key(&key, lcname->val, lcname->len TSRMLS_CC);
+
+               opline->op1_type = IS_CONST;
+               opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC);
+
+               zend_hash_update_ptr(CG(function_table), Z_STR(key), op_array);
+       }
+
+       CG(active_op_array) = op_array;
+       zend_stack_push(&CG(context_stack), (void *) &CG(context));
+       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;
+       }
+
+       {
+               /* Push a separator to the switch stack */
+               zend_switch_entry switch_entry;
+
+               switch_entry.cond.op_type = IS_UNUSED;
+               switch_entry.default_case = 0;
+               switch_entry.control_var = 0;
+
+               zend_stack_push(&CG(switch_cond_stack), (void *) &switch_entry);
+       }
+
+       {
+               /* Push a separator to the foreach stack */
+               zend_op dummy_opline;
+
+               dummy_opline.result_type = IS_UNUSED;
+
+               zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline);
+       }
+
+       zend_compile_params(params_ast TSRMLS_CC);
+       zend_compile_stmt(stmt_ast TSRMLS_CC);
+
+       zend_do_extended_info(TSRMLS_C);
+       zend_do_return(NULL, 0 TSRMLS_CC);
+
+       pass_two(CG(active_op_array) TSRMLS_CC);
+       zend_release_labels(0 TSRMLS_CC);
+
+       // TODO.AST __autoload
+       // 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_binary_op(znode *result, zend_ast *ast TSRMLS_DC) {
        zend_ast *left_ast = ast->child[0];
        zend_ast *right_ast = ast->child[1];
@@ -7766,6 +7880,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) {
                case ZEND_AST_TRY:
                        zend_compile_try(ast TSRMLS_CC);
                        break;
+               case ZEND_AST_FUNC_DECL:
+                       zend_compile_func_decl(ast TSRMLS_CC);
+                       break;
                default:
                {
                        znode result;
index 0a2e1f88af55b675dba7ff02db1aabfb2f95cb03..6e6dc32bd8d4d1ef39e023b911c499997cfa9519 100644 (file)
@@ -247,7 +247,7 @@ name:
 
 top_statement:
                statement                                               { AS($1); zend_verify_namespace(TSRMLS_C); }
-       |       function_declaration_statement  { zend_verify_namespace(TSRMLS_C); zend_do_early_binding(TSRMLS_C); }
+       |       function_declaration_statement  { AS($1); zend_verify_namespace(TSRMLS_C); zend_do_early_binding(TSRMLS_C); }
        |       class_declaration_statement             { zend_verify_namespace(TSRMLS_C); zend_do_early_binding(TSRMLS_C); }
        |       T_HALT_COMPILER '(' ')' ';'             { zend_do_halt_compiler_register(TSRMLS_C); YYACCEPT; }
        |       T_NAMESPACE namespace_name ';'  { zend_do_begin_namespace(&$2, 0 TSRMLS_CC); }
@@ -312,7 +312,7 @@ inner_statement_list:
 
 inner_statement:
                statement { $$.u.ast = $1.u.ast; }
-       |       function_declaration_statement { AN($$); }
+       |       function_declaration_statement { $$.u.ast = $1.u.ast; }
        |       class_declaration_statement { AN($$); }
        |       T_HALT_COMPILER '(' ')' ';'
                        { zend_error_noreturn(E_COMPILE_ERROR, "__HALT_COMPILER() can only be used from the outermost scope"); }
@@ -388,7 +388,9 @@ unset_variable:
 ;
 
 function_declaration_statement:
-               unticked_function_declaration_statement { DO_TICKS(); }
+       function is_reference T_STRING '(' parameter_list ')' '{' inner_statement_list '}'
+               { $$.u.ast = zend_ast_create_ex(3, ZEND_AST_FUNC_DECL,
+                     $2.op_type, AST_ZVAL(&$3), $5.u.ast, $8.u.ast); }
 ;
 
 class_declaration_statement:
@@ -405,12 +407,6 @@ is_variadic:
        |       T_ELLIPSIS  { $$.op_type = ZEND_PARAM_VARIADIC; }
 ;
 
-unticked_function_declaration_statement:
-               function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
-               '(' parameter_list ')' { zend_compile_params($6.u.ast TSRMLS_CC); zend_ast_destroy($6.u.ast); }
-               '{' inner_statement_list '}' { AS($10); zend_do_end_function_declaration(&$1 TSRMLS_CC); }
-;
-
 unticked_class_declaration_statement:
                class_entry_type T_STRING extends_from
                        { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
index e524645e4e4c841ef5cd1fec6728ab21061cf791..f457e7e0d5024d7d1521a564410e2ec5f23a8627 100644 (file)
@@ -82,6 +82,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
        op_array->required_num_args = 0;
 
        op_array->scope = NULL;
+       op_array->prototype = NULL;
 
        op_array->brk_cont_array = NULL;
        op_array->try_catch_array = NULL;