Skip to content

Commit

Permalink
Port parameter defs
Browse files Browse the repository at this point in the history
Has one bug re \self
  • Loading branch information
nikic committed Jul 14, 2014
1 parent 47f0717 commit 1ee3277
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 21 deletions.
4 changes: 4 additions & 0 deletions Zend/zend_ast.h
Expand Up @@ -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;
Expand Down
136 changes: 136 additions & 0 deletions Zend/zend_compile.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_compile.h
Expand Up @@ -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
Expand Down
46 changes: 25 additions & 21 deletions Zend/zend_language_parser.y
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 1ee3277

Please sign in to comment.