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;
}
}
+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];
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:
;
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:
| 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:
| 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: