]> granicus.if.org Git - php/commitdiff
Port parameter defs
authorNikita Popov <nikic@php.net>
Mon, 14 Jul 2014 21:03:53 +0000 (23:03 +0200)
committerNikita Popov <nikic@php.net>
Mon, 14 Jul 2014 21:03:53 +0000 (23:03 +0200)
Has one bug re \self

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

index 4b99cd7bf0b8c432c24b898b9ebcfa2a1fdf1f57..f0f8e9546ab34a932f34092d6107a8a9f4129b05 100644 (file)
@@ -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;
index 89cd5c435f12ac62729f7853579abf05fd1d8b2c..b633d7f1e14470e2f76ba522d58119aa2f0c4fbe 100644 (file)
@@ -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];
index 3d0206a7851662ca56679d107c84ee9cfa903470..8a56b4c3f0711412ef48d08ab2f8c9dbd4ab5524 100644 (file)
@@ -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
index 22d3c9fbcefcb40f66fe588632a497df02d419f3..0a2e1f88af55b675dba7ff02db1aabfb2f95cb03 100644 (file)
@@ -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: