From: Nikita Popov Date: Mon, 14 Jul 2014 21:03:53 +0000 (+0200) Subject: Port parameter defs X-Git-Tag: POST_AST_MERGE^2~142 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1ee3277abac485f248ff9fc4b6ecd907d438b70d;p=php Port parameter defs Has one bug re \self --- diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 4b99cd7bf0..f0f8e9546a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -93,6 +93,10 @@ enum _zend_ast_kind { ZEND_AST_TRY, ZEND_AST_CATCH_LIST, ZEND_AST_CATCH, + + ZEND_AST_PARAM_LIST, + ZEND_AST_PARAM, + ZEND_AST_TYPE, }; typedef unsigned short zend_ast_kind; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 89cd5c435f..b633d7f1e1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5361,10 +5361,16 @@ static zend_bool zend_can_write_to_variable(zend_ast *ast) { static zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) { zval *name; int fetch_type; + if (name_ast->kind != ZEND_AST_ZVAL) { return 0; } + /* Fully qualified names are always default refs */ + /*if (!name_ast->attr) { + return 1; + }*/ + name = zend_ast_get_zval(name_ast); fetch_type = zend_get_class_fetch_type(Z_STRVAL_P(name), Z_STRLEN_P(name)); return fetch_type == ZEND_FETCH_CLASS_DEFAULT; @@ -6824,6 +6830,136 @@ void zend_compile_stmt_list(zend_ast *ast TSRMLS_DC) { } } +void zend_compile_params(zend_ast *ast TSRMLS_DC) { + zend_uint i; + zend_op_array *op_array = CG(active_op_array); + zend_arg_info *arg_infos; + + if (ast->children == 0) { + return; + } + + arg_infos = safe_emalloc(sizeof(zend_arg_info), ast->children, 0); + for (i = 0; i < ast->children; ++i) { + zend_ast *param_ast = ast->child[i]; + zend_ast *type_ast = param_ast->child[0]; + zend_ast *var_ast = param_ast->child[1]; + zend_ast *default_ast = param_ast->child[2]; + zend_string *name = Z_STR_P(zend_ast_get_zval(var_ast)); + zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; + zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; + + znode var_node, default_node; + zend_uchar opcode; + zend_op *opline; + zend_arg_info *arg_info; + + if (zend_is_auto_global(name TSRMLS_CC)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign auto-global variable %s", + name->val); + } + + var_node.op_type = IS_CV; + var_node.u.op.var = lookup_cv(CG(active_op_array), STR_COPY(name) TSRMLS_CC); + + if (name->len == sizeof("this") - 1 && !memcmp(name->val, "this", sizeof("this") - 1)) { + if (op_array->scope && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } + op_array->this_var = var_node.u.op.var; + } + + if (op_array->fn_flags & ZEND_ACC_VARIADIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Only the last parameter can be variadic"); + } + + if (is_variadic) { + opcode = ZEND_RECV_VARIADIC; + default_node.op_type = IS_UNUSED; + op_array->fn_flags |= ZEND_ACC_VARIADIC; + + if (default_ast) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic parameter cannot have a default value"); + } + } else if (default_ast) { + opcode = ZEND_RECV_INIT; + default_node.op_type = IS_CONST; + _tmp_compile_const_expr(&default_node.u.constant, default_ast TSRMLS_CC); + } else { + opcode = ZEND_RECV; + default_node.op_type = IS_UNUSED; + op_array->required_num_args = i + 1; + } + + opline = emit_op(NULL, opcode, NULL, &default_node TSRMLS_CC); + SET_NODE(opline->result, &var_node); + opline->op1.num = i + 1; + + arg_info = &arg_infos[i]; + arg_info->name = estrndup(name->val, name->len); + arg_info->name_len = name->len; + arg_info->pass_by_reference = is_ref; + arg_info->is_variadic = is_variadic; + arg_info->type_hint = 0; + arg_info->allow_null = 1; + arg_info->class_name = NULL; + arg_info->class_name_len = 0; + + if (type_ast) { + zend_bool has_null_default = default_ast + && (Z_TYPE(default_node.u.constant) == IS_NULL + || (Z_TYPE(default_node.u.constant) == IS_CONSTANT + && strcasecmp(Z_STRVAL(default_node.u.constant), "NULL")) + || Z_TYPE(default_node.u.constant) == IS_CONSTANT_AST); // ??? + + op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; + arg_info->allow_null = has_null_default; + + if (type_ast->kind == ZEND_AST_TYPE) { + arg_info->type_hint = type_ast->attr; + if (arg_info->type_hint == IS_ARRAY) { + if (default_ast && !has_null_default + && Z_TYPE(default_node.u.constant) != IS_ARRAY + ) { + zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " + "with array type hint can only be an array or NULL"); + } + } else if (arg_info->type_hint == IS_CALLABLE && default_ast) { + if (default_ast && !has_null_default) { + zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " + "with callable type hint can only be NULL"); + } + } + } else { + zend_string *class_name = Z_STR_P(zend_ast_get_zval(type_ast)); + zend_bool is_fully_qualified = !type_ast->attr; + + if (zend_is_const_default_class_ref(type_ast)) { + class_name = zend_resolve_class_name(class_name, is_fully_qualified TSRMLS_CC); + } else { + STR_ADDREF(class_name); + } + + arg_info->type_hint = IS_OBJECT; + arg_info->class_name = estrndup(class_name->val, class_name->len); + arg_info->class_name_len = class_name->len; + + STR_RELEASE(class_name); + + if (default_ast && !has_null_default) { + zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " + "with a class type hint can only be NULL"); + } + } + } + } + + /* These are assigned at the end to avoid unitialized memory in case of an error */ + op_array->num_args = ast->children; + op_array->arg_info = arg_infos; +} + 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]; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 3d0206a785..8a56b4c3f0 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -661,6 +661,9 @@ int zend_add_literal(zend_op_array *op_array, zval *zv TSRMLS_DC); #define ZEND_PARSED_NEW (1<<6) #define ZEND_PARSED_LIST_EXPR (1<<7) +#define ZEND_PARAM_REF (1<<0) +#define ZEND_PARAM_VARIADIC (1<<1) + /* unset types */ #define ZEND_UNSET_REG 0 diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 22d3c9fbce..0a2e1f88af 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -397,18 +397,18 @@ class_declaration_statement: is_reference: /* empty */ { $$.op_type = 0; } - | '&' { $$.op_type = 1; } + | '&' { $$.op_type = ZEND_PARAM_REF; } ; is_variadic: /* empty */ { $$.op_type = 0; } - | T_ELLIPSIS { $$.op_type = 1; } + | 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 ')' - '{' inner_statement_list '}' { AS($9); zend_do_end_function_declaration(&$1 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: @@ -550,29 +550,33 @@ alt_if_stmt: ; parameter_list: - non_empty_parameter_list - | /* empty */ + non_empty_parameter_list { $$.u.ast = $1.u.ast; } + | /* empty */ { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAM_LIST); } ; non_empty_parameter_list: parameter + { $$.u.ast = zend_ast_create_dynamic_and_add(ZEND_AST_PARAM_LIST, $1.u.ast); } | non_empty_parameter_list ',' parameter + { $$.u.ast = zend_ast_dynamic_add($1.u.ast, $3.u.ast); } ; parameter: - optional_class_type is_reference is_variadic T_VARIABLE - { zend_do_receive_param(ZEND_RECV, &$4, NULL, &$1, $2.op_type, $3.op_type TSRMLS_CC); } - | optional_class_type is_reference is_variadic T_VARIABLE '=' static_scalar - { zend_do_receive_param(ZEND_RECV_INIT, &$4, &$6, &$1, $2.op_type, $3.op_type TSRMLS_CC); } + optional_type is_reference is_variadic T_VARIABLE + { $$.u.ast = zend_ast_create_ex(3, ZEND_AST_PARAM, $2.op_type | $3.op_type, + $1.u.ast, AST_ZVAL(&$4), NULL); } + | optional_type is_reference is_variadic T_VARIABLE '=' expr + { $$.u.ast = zend_ast_create_ex(3, ZEND_AST_PARAM, $2.op_type | $3.op_type, + $1.u.ast, AST_ZVAL(&$4), $6.u.ast); } ; -optional_class_type: - /* empty */ { $$.op_type = IS_UNUSED; } - | T_ARRAY { $$.op_type = IS_CONST; Z_TYPE_INFO($$.u.constant)=IS_ARRAY; } - | T_CALLABLE { $$.op_type = IS_CONST; Z_TYPE_INFO($$.u.constant)=IS_CALLABLE; } - | fully_qualified_class_name { $$ = $1; } +optional_type: + /* empty */ { $$.u.ast = NULL; } + | T_ARRAY { $$.u.ast = zend_ast_create_ex(0, ZEND_AST_TYPE, IS_ARRAY); } + | T_CALLABLE { $$.u.ast = zend_ast_create_ex(0, ZEND_AST_TYPE, IS_CALLABLE); } + | name { $$.u.ast = $1.u.ast; } ; argument_list: @@ -629,8 +633,8 @@ class_statement: | class_constant_declaration ';' | trait_use_statement | method_modifiers function is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$4, 1, $3.op_type, &$1 TSRMLS_CC); } - '(' parameter_list ')' - method_body { zend_do_abstract_method(&$4, &$1, &$9 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } + '(' parameter_list ')' { zend_compile_params($7.u.ast TSRMLS_CC); zend_ast_destroy($7.u.ast); } + method_body { zend_do_abstract_method(&$4, &$1, &$10 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; trait_use_statement: @@ -870,11 +874,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 ')' lexical_vars - '{' inner_statement_list '}' { AS($9); 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 + '{' inner_statement_list '}' { AS($10); 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 ')' lexical_vars - '{' inner_statement_list '}' { AS($10); 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 + '{' inner_statement_list '}' { AS($11); zend_do_end_function_declaration(&$2 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$4); } ; function: