diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 5ef590d2e78..1e404550224 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -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); }*/ diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index f0f8e9546ab..dde544804ef 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -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; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b633d7f1e14..ef24086a34f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 0a2e1f88af5..6e6dc32bd8d 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -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); } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index e524645e4e4..f457e7e0d50 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -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;