From: Nikita Popov Date: Sat, 7 Jun 2014 11:06:53 +0000 (+0200) Subject: AST-based compiler: Stage 1 X-Git-Tag: POST_AST_MERGE^2~202 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=258a733ed2f368409d16c9168eaa8daa24fac798;p=php AST-based compiler: Stage 1 --- diff --git a/Zend/tests/bug39304.phpt b/Zend/tests/bug39304.phpt index 8303b82573..5540135fa4 100644 --- a/Zend/tests/bug39304.phpt +++ b/Zend/tests/bug39304.phpt @@ -9,8 +9,8 @@ echo "I am alive"; --EXPECTF-- Notice: Uninitialized string offset: 0 in %sbug39304.php on line %d -Notice: Uninitialized string offset: 1 in %sbug39304.php on line %d - Notice: Uninitialized string offset: 0 in %sbug39304.php on line %d + +Notice: Uninitialized string offset: 1 in %sbug39304.php on line %d I am alive diff --git a/Zend/tests/bug39304_2_4.phpt b/Zend/tests/bug39304_2_4.phpt index b0e6ddc72c..cc0709b424 100644 --- a/Zend/tests/bug39304_2_4.phpt +++ b/Zend/tests/bug39304_2_4.phpt @@ -11,8 +11,8 @@ Bug #39304 (Segmentation fault with list unpacking of string offset) --EXPECTF-- Notice: Uninitialized string offset: 0 in %sbug39304_2_4.php on line %d -Notice: Uninitialized string offset: 1 in %sbug39304_2_4.php on line %d - Notice: Uninitialized string offset: 0 in %sbug39304_2_4.php on line %d + +Notice: Uninitialized string offset: 1 in %sbug39304_2_4.php on line %d string(0) "" string(0) "" diff --git a/Zend/tests/foreach_list_002.phpt b/Zend/tests/foreach_list_002.phpt index 251870ba09..bc17d94268 100644 --- a/Zend/tests/foreach_list_002.phpt +++ b/Zend/tests/foreach_list_002.phpt @@ -9,7 +9,7 @@ foreach (array(array(1,2), array(3,4)) as list($a, )) { $array = [['a', 'b'], 'c', 'd']; -foreach($array as list(list(), $a)) { +foreach($array as list(, $a)) { var_dump($a); } diff --git a/Zend/tests/list_005.phpt b/Zend/tests/list_005.phpt index 4afc353b2f..ec5640a60a 100644 --- a/Zend/tests/list_005.phpt +++ b/Zend/tests/list_005.phpt @@ -3,33 +3,33 @@ Testing list() with several variables --FILE-- --EXPECTF-- -Notice: Undefined offset: 1 in %s on line %d +Notice: Undefined offset: 0 in %s on line %d Notice: Undefined offset: 1 in %s on line %d - -Notice: Undefined offset: 0 in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 82bf42b0d2..ba0b57fccc 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -32,6 +32,15 @@ ZEND_API zend_ast *zend_ast_create_constant(zval *zv) return (zend_ast *) ast; } +ZEND_API zend_ast *zend_ast_create_znode(znode *node) +{ + zend_ast_znode *ast = emalloc(sizeof(zend_ast_znode)); + ast->kind = ZEND_AST_ZNODE; + ast->children = 0; + ast->node = *node; + return (zend_ast *) ast; +} + ZEND_API zend_ast* zend_ast_create_unary(uint kind, zend_ast *op0) { zend_ast *ast = emalloc(sizeof(zend_ast)); @@ -62,7 +71,7 @@ ZEND_API zend_ast* zend_ast_create_ternary(uint kind, zend_ast *op0, zend_ast *o return ast; } -ZEND_API zend_ast* zend_ast_create_dynamic(uint kind) +ZEND_API zend_ast *zend_ast_create_dynamic(uint kind) { /* use 4 children as default */ zend_ast *ast = emalloc(sizeof(zend_ast) + sizeof(zend_ast *) * 3); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index d411e4e802..08fecd132a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -26,12 +26,35 @@ typedef enum _zend_ast_kind { /* first 256 kinds are reserved for opcodes */ - ZEND_CONST = 256, + ZEND_CONST = 256, /* TODO.AST: Split in constant lookup and literal zval */ ZEND_BOOL_AND, ZEND_BOOL_OR, ZEND_SELECT, ZEND_UNARY_PLUS, ZEND_UNARY_MINUS, + + ZEND_AST_ZNODE, + + ZEND_AST_VAR, + ZEND_AST_DIM, + ZEND_AST_PROP, + ZEND_AST_STATIC_PROP, + + ZEND_AST_CALL, + ZEND_AST_METHOD_CALL, + ZEND_AST_STATIC_CALL, + + ZEND_AST_ASSIGN, + ZEND_AST_ASSIGN_REF, + ZEND_AST_LIST, + ZEND_AST_GLOBAL, + ZEND_AST_UNSET, + + ZEND_AST_PARAMS, + ZEND_AST_UNPACK, + + ZEND_AST_NAME, + ZEND_AST_NAME_FQ, } zend_ast_kind; struct _zend_ast { @@ -66,4 +89,28 @@ ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *s ZEND_API zend_ast *zend_ast_copy(zend_ast *ast); ZEND_API void zend_ast_destroy(zend_ast *ast); +static inline zend_ast *zend_ast_create_var(zval *name) { + return zend_ast_create_unary(ZEND_AST_VAR, zend_ast_create_constant(name)); +} + +/* Temporary, for porting */ +#define AST_COMPILE(res, ast) do { \ + zend_ast *_ast = (ast); \ + zend_compile_expr((res), _ast TSRMLS_CC); \ + zend_ast_destroy(_ast); \ +} while (0) +#define AST_COMPILE_VAR(res, ast, type) do { \ + zend_ast *_ast = (ast); \ + zend_compile_var((res), _ast, type TSRMLS_CC); \ + zend_ast_destroy(_ast); \ +} while (0) +#define AST_COMPILE_STMT(ast) do { \ + zend_ast *_ast = (ast); \ + zend_compile_stmt(_ast TSRMLS_CC); \ + zend_ast_destroy(_ast); \ +} while (0) + +#define AST_ZNODE(znode) zend_ast_create_znode((znode)) +#define AST_ZVAL(znode) zend_ast_create_constant(&(znode)->u.constant) + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f7adf95ed3..536b904214 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2554,6 +2554,8 @@ void zend_do_end_function_call(znode *function_name, znode *result, int is_metho } /* }}} */ +static zend_bool zend_is_call(zend_ast *ast); + void zend_do_pass_param(znode *param, zend_uchar op TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2587,10 +2589,10 @@ void zend_do_pass_param(znode *param, zend_uchar op TSRMLS_DC) /* {{{ */ if (function_ptr) { if (ARG_MAY_BE_SENT_BY_REF(function_ptr, fcall->arg_num)) { - if (op == ZEND_SEND_VAR && param->op_type & (IS_VAR|IS_CV)) { + // TODO.AST + if (op == ZEND_SEND_VAR /*&& param->op_type & (IS_VAR|IS_CV)*/) { send_by_reference = ZEND_ARG_SEND_BY_REF; - if (zend_is_function_or_method_call(param)) { - /* Method call */ + if (zend_is_call(param->u.ast)) { op = ZEND_SEND_VAR_NO_REF; send_function = ZEND_ARG_SEND_FUNCTION | ZEND_ARG_SEND_SILENT; } @@ -2602,8 +2604,7 @@ void zend_do_pass_param(znode *param, zend_uchar op TSRMLS_DC) /* {{{ */ } } - if (op == ZEND_SEND_VAR && zend_is_function_or_method_call(param)) { - /* Method call */ + if (op == ZEND_SEND_VAR && zend_is_call(param->u.ast)) { op = ZEND_SEND_VAR_NO_REF; send_function = ZEND_ARG_SEND_FUNCTION; } else if (op == ZEND_SEND_VAL && (param->op_type & (IS_VAR|IS_CV))) { @@ -2612,7 +2613,7 @@ void zend_do_pass_param(znode *param, zend_uchar op TSRMLS_DC) /* {{{ */ if (op!=ZEND_SEND_VAR_NO_REF && send_by_reference==ZEND_ARG_SEND_BY_REF) { /* change to passing by reference */ - switch (param->op_type) { + /*switch (param->op_type) { case IS_VAR: case IS_CV: op = ZEND_SEND_REF; @@ -2620,23 +2621,27 @@ void zend_do_pass_param(znode *param, zend_uchar op TSRMLS_DC) /* {{{ */ default: zend_error_noreturn(E_COMPILE_ERROR, "Only variables can be passed by reference"); break; - } + }*/ + // TODO.AST + op = ZEND_SEND_REF; } if (original_op == ZEND_SEND_VAR) { switch (op) { case ZEND_SEND_VAR_NO_REF: - zend_do_end_variable_parse(param, BP_VAR_R, 0 TSRMLS_CC); + AST_COMPILE_VAR(param, param->u.ast, BP_VAR_R); break; case ZEND_SEND_VAR: if (function_ptr) { - zend_do_end_variable_parse(param, BP_VAR_R, 0 TSRMLS_CC); + AST_COMPILE_VAR(param, param->u.ast, BP_VAR_R); } else { - zend_do_end_variable_parse(param, BP_VAR_FUNC_ARG, fcall->arg_num TSRMLS_CC); + // TODO.AST ! + //zend_do_end_variable_parse(param, BP_VAR_FUNC_ARG, fcall->arg_num TSRMLS_CC); + AST_COMPILE_VAR(param, param->u.ast, BP_VAR_R); } break; case ZEND_SEND_REF: - zend_do_end_variable_parse(param, BP_VAR_W, 0 TSRMLS_CC); + AST_COMPILE_VAR(param, param->u.ast, BP_VAR_W); break; } } @@ -2743,14 +2748,16 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ zend_op *opline; int start_op_number, end_op_number; zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + zend_bool is_call = 0; /* The error for use of return inside a generator is thrown in pass_two. */ if (do_end_vparse) { - if (returns_reference && !zend_is_function_or_method_call(expr)) { - zend_do_end_variable_parse(expr, BP_VAR_W, 1 TSRMLS_CC); + is_call = zend_is_call(expr->u.ast); + if (returns_reference && !is_call) { + AST_COMPILE_VAR(expr, expr->u.ast, BP_VAR_REF); } else { - zend_do_end_variable_parse(expr, BP_VAR_R, 0 TSRMLS_CC); + AST_COMPILE_VAR(expr, expr->u.ast, BP_VAR_R); } } @@ -2786,7 +2793,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ if (!do_end_vparse) { opline->extended_value = ZEND_RETURNS_VALUE; - } else if (zend_is_function_or_method_call(expr)) { + } else if (is_call) { opline->extended_value = ZEND_RETURNS_FUNCTION; } } else { @@ -2801,6 +2808,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ void zend_do_yield(znode *result, znode *value, znode *key, zend_bool is_variable TSRMLS_DC) /* {{{ */ { zend_op *opline; + zend_bool is_call = 0; if (!CG(active_op_array)->function_name) { zend_error_noreturn(E_COMPILE_ERROR, "The \"yield\" expression can only be used inside a function"); @@ -2809,10 +2817,11 @@ void zend_do_yield(znode *result, znode *value, znode *key, zend_bool is_variabl CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; if (is_variable) { - if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(value)) { - zend_do_end_variable_parse(value, BP_VAR_W, 1 TSRMLS_CC); + is_call = zend_is_call(value->u.ast); + if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !is_call) { + AST_COMPILE_VAR(value, value->u.ast, BP_VAR_REF); } else { - zend_do_end_variable_parse(value, BP_VAR_R, 0 TSRMLS_CC); + AST_COMPILE_VAR(value, value->u.ast, BP_VAR_R); } } @@ -2823,7 +2832,7 @@ void zend_do_yield(znode *result, znode *value, znode *key, zend_bool is_variabl if (value) { SET_NODE(opline->op1, value); - if (is_variable && zend_is_function_or_method_call(value)) { + if (is_call) { opline->extended_value = ZEND_RETURNS_FUNCTION; } } else { @@ -5882,7 +5891,6 @@ void zend_do_end_array(znode *result, const znode *array_node TSRMLS_DC) /* {{{ int i; int constant_array = 0; zval array; - zend_constant *c; /* check if constructed array consists only from constants */ if ((init_opline->op1_type & (IS_UNUSED | IS_CONST)) && @@ -6224,7 +6232,7 @@ void zend_do_fetch_global_variable(znode *varname, const znode *static_assignmen opline->result.var = get_temporary_variable(CG(active_op_array)); SET_NODE(opline->op1, varname); SET_UNUSED(opline->op2); - opline->extended_value = fetch_type; + opline->extended_value = fetch_type; // ZEND_FETCH_GLOBAL_LOCK GET_NODE(&result, opline->result); if (varname->op_type == IS_CONST) { @@ -6320,11 +6328,10 @@ void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC { zend_op *last_op; - zend_do_end_variable_parse(variable, BP_VAR_IS, 0 TSRMLS_CC); - - if (zend_is_function_or_method_call(variable)) { + if (zend_is_call(variable->u.ast)) { if (type == ZEND_ISEMPTY) { /* empty(func()) can be transformed to !func() */ + AST_COMPILE(variable, variable->u.ast); zend_do_unary_op(ZEND_BOOL_NOT, result, variable TSRMLS_CC); } else { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use isset() on the result of a function call (you can use \"null !== func()\" instead)"); @@ -6333,6 +6340,8 @@ void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC return; } + AST_COMPILE_VAR(variable, variable->u.ast, BP_VAR_IS); + if (variable->op_type == IS_CV) { last_op = get_next_op(CG(active_op_array) TSRMLS_CC); last_op->opcode = ZEND_ISSET_ISEMPTY_VAR; @@ -6398,14 +6407,13 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno open_brackets_token->u.op.opline_num = get_next_op_number(CG(active_op_array)); if (variable) { - if (zend_is_function_or_method_call(array) - || !zend_can_parse_variable_for_write(array TSRMLS_CC) - ) { + // TODO.AST or !zend_can_parse_variable_for_write(array TSRMLS_CC) + if (zend_is_call(array->u.ast)) { is_variable = 0; - zend_do_end_variable_parse(array, BP_VAR_R, 0 TSRMLS_CC); + AST_COMPILE_VAR(array, array->u.ast, BP_VAR_R); } else { is_variable = 1; - zend_do_end_variable_parse(array, BP_VAR_W, 0 TSRMLS_CC); + AST_COMPILE_VAR(array, array->u.ast, BP_VAR_W); } } else { is_variable = 0; @@ -6452,6 +6460,9 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno } /* }}} */ +void zend_compile_assign(znode *result, zend_ast *ast TSRMLS_DC); +static void zend_compile_list_assign(znode *result, zend_ast *list_ast, znode *expr_node TSRMLS_DC); + void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token, const znode *as_token, znode *value, znode *key TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -6480,6 +6491,7 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token } } + // TODO.AST check writable if (value->EA & ZEND_PARSED_REFERENCE_VARIABLE) { assign_by_ref = 1; @@ -6510,18 +6522,18 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token GET_NODE(&value_node, opline->result); if (value->EA & ZEND_PARSED_LIST_EXPR) { - if (!CG(list_llist).head) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use empty list"); - } - zend_do_list_end(&dummy, &value_node TSRMLS_CC); + zend_compile_list_assign(&dummy, value->u.ast, &value_node TSRMLS_CC); zend_do_free(&dummy TSRMLS_CC); + zend_ast_destroy(value->u.ast); } else { if (assign_by_ref) { - zend_do_end_variable_parse(value, BP_VAR_W, 0 TSRMLS_CC); - /* Mark FE_FETCH as IS_VAR as it holds the data directly as a value */ - zend_do_assign_ref(NULL, value, &value_node TSRMLS_CC); + zend_ast *assign_ast = zend_ast_create_binary( + ZEND_AST_ASSIGN_REF, value->u.ast, AST_ZNODE(&value_node)); + AST_COMPILE(NULL, assign_ast); } else { - zend_do_assign(&dummy, value, &value_node TSRMLS_CC); + zend_ast *assign_ast = zend_ast_create_binary( + ZEND_AST_ASSIGN, value->u.ast, AST_ZNODE(&value_node)); + AST_COMPILE(&dummy, assign_ast); zend_do_free(&dummy TSRMLS_CC); } } @@ -6534,7 +6546,9 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token opline->result.opline_num = get_temporary_variable(CG(active_op_array)); GET_NODE(&key_node, opline->result); - zend_do_assign(&dummy, key, &key_node TSRMLS_CC); + zend_ast *assign_ast = zend_ast_create_binary( + ZEND_AST_ASSIGN, key->u.ast, AST_ZNODE(&key_node)); + AST_COMPILE(&dummy, assign_ast); zend_do_free(&dummy TSRMLS_CC); } @@ -7586,6 +7600,949 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* }}} */ +static void zend_adjust_for_fetch_type(zend_op *opline, zend_uint type) { + switch (type & BP_VAR_MASK) { + case BP_VAR_R: + return; + case BP_VAR_W: + opline->opcode += 3; + return; + case BP_VAR_REF: + opline->opcode += 3; + opline->extended_value |= ZEND_FETCH_MAKE_REF; + return; + case BP_VAR_RW: + opline->opcode += 6; + return; + case BP_VAR_IS: + opline->opcode += 9; + return; + case BP_VAR_FUNC_ARG: + opline->opcode += 12; + opline->extended_value |= type >> BP_VAR_SHIFT; + return; + case BP_VAR_UNSET: + opline->opcode += 15; + return; + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +static zend_op *emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2 TSRMLS_DC) { + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = opcode; + + if (op1 == NULL) { + SET_UNUSED(opline->op1); + } else { + SET_NODE(opline->op1, op1); + } + + if (op2 == NULL) { + SET_UNUSED(opline->op2); + } else { + SET_NODE(opline->op2, op2); + } + + if (result == NULL) { + SET_UNUSED(opline->result); + } else { + opline->result_type = IS_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); + } + return opline; +} + +static zend_bool zend_is_variable(zend_ast *ast) { + return ast->kind == ZEND_AST_VAR || ast->kind == ZEND_AST_DIM || ast->kind == ZEND_AST_PROP + || ast->kind == ZEND_AST_CALL || ast->kind == ZEND_AST_METHOD_CALL + || ast->kind == ZEND_AST_STATIC_CALL; +} + +static zend_bool zend_is_call(zend_ast *ast) { + return ast->kind == ZEND_AST_CALL + || ast->kind == ZEND_AST_METHOD_CALL + || ast->kind == ZEND_AST_STATIC_CALL; +} + +static int zend_try_compile_cv(znode *result, zend_ast *ast TSRMLS_DC) { + zend_ast *name_ast = ast->child[0]; + if (name_ast->kind == ZEND_CONST) { + zend_string *name = zval_get_string(zend_ast_get_zval(name_ast)); + + if (zend_is_auto_global(name TSRMLS_CC) || + (CG(active_op_array)->last != 0 && + CG(active_op_array)->opcodes[CG(active_op_array)->last-1].opcode == ZEND_BEGIN_SILENCE) + ) { + // TODO.AST: handle ZEND_BEGIN_SILENCE properly + STR_RELEASE(name); + return FAILURE; + } + + result->op_type = IS_CV; + result->u.op.var = lookup_cv(CG(active_op_array), name TSRMLS_CC); + result->EA = 0; + + if (name->len == sizeof("this") - 1 && !memcmp(name->val, "this", sizeof("this") - 1)) { + CG(active_op_array)->this_var = result->u.op.var; + } + return SUCCESS; + } + + return FAILURE; +} + +static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_ast *name_ast = ast->child[0]; + znode name_node; + zend_op *opline; + + /* there is a chance someone is accessing $this */ + if (ast->kind != ZEND_CONST + && CG(active_op_array)->scope && CG(active_op_array)->this_var == -1 + ) { + zend_string *key = STR_INIT("this", sizeof("this") - 1, 0); + CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key TSRMLS_CC); + } + + zend_compile_expr(&name_node, name_ast TSRMLS_CC); + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline = emit_op(result, ZEND_FETCH_R, &name_node, NULL TSRMLS_CC); + + opline->extended_value = ZEND_FETCH_LOCAL; + if (name_node.op_type == IS_CONST) { + if (zend_is_auto_global(Z_STR(name_node.u.constant) TSRMLS_CC)) { + opline->extended_value = ZEND_FETCH_GLOBAL; + } + } + + return opline; +} + +static void zend_compile_simple_var(znode *result, zend_ast *ast, int type TSRMLS_DC) { + if (zend_try_compile_cv(result, ast TSRMLS_CC) == FAILURE) { + zend_op *opline = zend_compile_simple_var_no_cv(result, ast, type TSRMLS_CC); + zend_adjust_for_fetch_type(opline, type); + } +} + +static void handle_numeric_op2(zend_op *opline TSRMLS_DC) { + if (opline->op2_type == IS_CONST && Z_TYPE(CONSTANT(opline->op2.constant)) == IS_STRING) { + ulong index; + int numeric = 0; + + ZEND_HANDLE_NUMERIC_EX(Z_STRVAL(CONSTANT(opline->op2.constant)), Z_STRLEN(CONSTANT(opline->op2.constant))+1, index, numeric = 1); + if (numeric) { + zval_dtor(&CONSTANT(opline->op2.constant)); + ZVAL_LONG(&CONSTANT(opline->op2.constant), index); + } + } +} + +static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, int type TSRMLS_DC) { + if (type != BP_VAR_R && type != BP_VAR_IS && zend_is_call(ast)) { + zend_op *opline = emit_op(NULL, ZEND_SEPARATE, node, NULL TSRMLS_CC); + opline->result_type = IS_VAR; + opline->result.var = opline->op1.var; + } +} + +zend_op *zend_compile_dim_common(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_ast *var_ast = ast->child[0]; + zend_ast *dim_ast = ast->child[1]; + + znode var_node, dim_node; + zend_op *opline; + + if (dim_ast == NULL) { + if (type == BP_VAR_R || type == BP_VAR_IS) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading"); + } + if (type == BP_VAR_UNSET) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for unsetting"); + } + dim_node.op_type = IS_UNUSED; + } else { + zend_compile_expr(&dim_node, dim_ast TSRMLS_CC); + } + + zend_compile_var(&var_node, var_ast, type TSRMLS_CC); + zend_separate_if_call_and_write(&var_node, var_ast, type TSRMLS_CC); + + opline = emit_op(result, ZEND_FETCH_DIM_R, &var_node, &dim_node TSRMLS_CC); + handle_numeric_op2(opline TSRMLS_CC); + + return opline; +} + +void zend_compile_dim(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_op *opline = zend_compile_dim_common(result, ast, type TSRMLS_CC); + zend_adjust_for_fetch_type(opline, type); +} + +static zend_bool is_this_fetch(zend_ast *ast) { + if (ast->kind != ZEND_AST_VAR || ast->child[0]->kind != ZEND_CONST) { + return 0; + } + + zval *name = zend_ast_get_zval(ast->child[0]); + return Z_TYPE_P(name) == IS_STRING && Z_STRLEN_P(name) == sizeof("this") - 1 + && !memcmp(Z_STRVAL_P(name), "this", sizeof("this") - 1); +} + +zend_op *zend_compile_prop_common(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_ast *obj_ast = ast->child[0]; + zend_ast *prop_ast = ast->child[1]; + + znode obj_node, prop_node; + zend_op *opline; + + zend_compile_expr(&prop_node, prop_ast TSRMLS_CC); + if (is_this_fetch(obj_ast)) { + obj_node.op_type = IS_UNUSED; + } else { + zend_compile_var(&obj_node, obj_ast, type TSRMLS_CC); + zend_separate_if_call_and_write(&obj_node, obj_ast, type TSRMLS_CC); + } + + opline = emit_op(result, ZEND_FETCH_OBJ_R, &obj_node, &prop_node TSRMLS_CC); + if (opline->op2_type == IS_CONST && Z_TYPE(CONSTANT(opline->op2.constant)) == IS_STRING) { + GET_POLYMORPHIC_CACHE_SLOT(opline->op2.constant); + } + + return opline; +} + +void zend_compile_prop(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_op *opline = zend_compile_prop_common(result, ast, type TSRMLS_CC); + zend_adjust_for_fetch_type(opline, type); +} + +void zend_compile_static_prop(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_ast *class_ast = ast->child[0]; + zend_ast *prop_ast = ast->child[1]; + + znode class_name_node, class_node, prop_node; + zend_op *opline; + + zend_compile_expr(&class_name_node, class_ast TSRMLS_CC); + if (class_name_node.op_type == IS_CONST && + ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type( + Z_STRVAL(class_name_node.u.constant), Z_STRLEN(class_name_node.u.constant)) + ) { + zend_resolve_class_name(&class_name_node TSRMLS_CC); + class_node = class_name_node; + } else { + zend_do_fetch_class(&class_node, &class_name_node TSRMLS_CC); + } + + zend_compile_expr(&prop_node, prop_ast TSRMLS_CC); + + opline = emit_op(result, ZEND_FETCH_R, &prop_node, NULL TSRMLS_CC); + zend_adjust_for_fetch_type(opline, type); + if (opline->op1_type == IS_CONST) { + GET_POLYMORPHIC_CACHE_SLOT(opline->op1.constant); + } + if (class_node.op_type == IS_CONST) { + opline->op2_type = IS_CONST; + opline->op2.constant = + zend_add_class_name_literal(CG(active_op_array), &class_node.u.constant TSRMLS_CC); + } else { + SET_NODE(opline->op2, &class_node); + } + opline->extended_value |= ZEND_FETCH_STATIC_MEMBER; +} + +static zend_uchar get_list_fetch_opcode(zend_uchar op_type) { + switch (op_type) { + case IS_VAR: + case IS_CV: + return ZEND_FETCH_DIM_R; + case IS_TMP_VAR: + case IS_CONST: + return ZEND_FETCH_DIM_TMP_VAR; + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +static void zend_compile_list_assign(znode *result, zend_ast *list_ast, znode *expr_node TSRMLS_DC) { + zend_uint i; + + if (list_ast->children == 1 && !list_ast->child[0]) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use empty list"); + } + + for (i = 0; i < list_ast->children; ++i) { + zend_ast *var_ast = list_ast->child[i]; + znode fetch_result, dim_node, var_node, assign_result; + zend_op *opline; + + if (var_ast == NULL) { + continue; + } + + dim_node.op_type = IS_CONST; + ZVAL_LONG(&dim_node.u.constant, i); + + opline = emit_op(&fetch_result, + get_list_fetch_opcode(expr_node->op_type), expr_node, &dim_node TSRMLS_CC); + opline->extended_value |= ZEND_FETCH_ADD_LOCK; + + if (var_ast->kind != ZEND_AST_LIST) { + if (is_this_fetch(var_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } + zend_compile_var(&var_node, var_ast, BP_VAR_W TSRMLS_CC); + emit_op(&assign_result, ZEND_ASSIGN, &var_node, &fetch_result TSRMLS_CC); + zend_do_free(&assign_result TSRMLS_CC); + } else { + zend_compile_list_assign(&assign_result, var_ast, &fetch_result TSRMLS_CC); + zend_do_free(&assign_result TSRMLS_CC); + } + } + *result = *expr_node; +} + +void zend_ensure_writable_variable(const zend_ast *ast) { + if (ast->kind == ZEND_AST_CALL) { + zend_error_noreturn(E_COMPILE_ERROR, "Can't use function return value in write context"); + } + if (ast->kind == ZEND_AST_METHOD_CALL || ast->kind == ZEND_AST_STATIC_CALL) { + zend_error_noreturn(E_COMPILE_ERROR, "Can't use method return value in write context"); + } +} + +/* Detected $a[b]... = $a pattern */ +zend_bool zend_is_dim_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast TSRMLS_DC) { + if (expr_ast->kind != ZEND_AST_VAR || expr_ast->child[0]->kind != ZEND_CONST) { + return 0; + } + + while (zend_is_variable(var_ast) && !var_ast->kind == ZEND_AST_VAR) { + var_ast = var_ast->child[0]; + } + + if (var_ast->kind != ZEND_AST_VAR || var_ast->child[0]->kind != ZEND_CONST) { + return 0; + } + + { + zend_string *name1 = zval_get_string(zend_ast_get_zval(var_ast->child[0])); + zend_string *name2 = zval_get_string(zend_ast_get_zval(expr_ast->child[0])); + zend_bool result = name1->len == name2->len && !memcmp(name1->val, name2->val, name1->len); + STR_RELEASE(name1); + STR_RELEASE(name2); + return result; + } +} + +void zend_compile_assign(znode *result, zend_ast *ast TSRMLS_DC) { + zend_ast *var_ast = ast->child[0]; + zend_ast *expr_ast = ast->child[1]; + + znode var_node, expr_node; + zend_op *opline; + + if (is_this_fetch(var_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } + + zend_ensure_writable_variable(var_ast); + + // TODO.AST handle $a[b] = $a + //if (zend_is_dim_assign_to_self(var_ast, expr_ast TSRMLS_CC)) { + //zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R TSRMLS_CC); + //} else { + zend_compile_expr(&expr_node, expr_ast TSRMLS_CC); + //} + + switch (var_ast->kind) { + case ZEND_AST_VAR: + case ZEND_AST_STATIC_PROP: + zend_compile_var(&var_node, var_ast, BP_VAR_W TSRMLS_CC); + emit_op(result, ZEND_ASSIGN, &var_node, &expr_node TSRMLS_CC); + return; + case ZEND_AST_DIM: + opline = zend_compile_dim_common(result, var_ast, BP_VAR_W TSRMLS_CC); + opline->opcode = ZEND_ASSIGN_DIM; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_do_op_data(opline, &expr_node TSRMLS_CC); + opline->op2.var = get_temporary_variable(CG(active_op_array)); + opline->op2_type = IS_VAR; + SET_UNUSED(opline->result); + return; + case ZEND_AST_PROP: + opline = zend_compile_prop_common(result, var_ast, BP_VAR_W TSRMLS_CC); + opline->opcode = ZEND_ASSIGN_OBJ; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_do_op_data(opline, &expr_node TSRMLS_CC); + SET_UNUSED(opline->result); + return; + case ZEND_AST_LIST: + zend_compile_list_assign(result, var_ast, &expr_node TSRMLS_CC); + return; + EMPTY_SWITCH_DEFAULT_CASE(); + } +} + +zend_op *zend_compile_assign_ref_common( + znode *result, zend_ast *target_ast, znode *source_node TSRMLS_DC +) { + znode target_node; + zend_op *opline; + + if (is_this_fetch(target_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } + + zend_ensure_writable_variable(target_ast); + zend_compile_var(&target_node, target_ast, BP_VAR_W TSRMLS_CC); + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_ASSIGN_REF; + SET_NODE(opline->op1, &target_node); + SET_NODE(opline->op2, source_node); + + if (result) { + opline->result_type = IS_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); + } else { + opline->result_type = IS_UNUSED | EXT_TYPE_UNUSED; + } + + return opline; +} + +void zend_compile_assign_ref(znode *result, zend_ast *ast TSRMLS_DC) { + zend_ast *target_ast = ast->child[0]; + zend_ast *source_ast = ast->child[1]; + + znode source_node; + zend_op *opline; + + zend_compile_var(&source_node, source_ast, BP_VAR_REF TSRMLS_CC); + opline = zend_compile_assign_ref_common(result, target_ast, &source_node TSRMLS_CC); + + if (zend_is_call(source_ast)) { + opline->extended_value = ZEND_RETURNS_FUNCTION; + } + // TODO.AST + /*if (rvar->EA & ZEND_PARSED_NEW) { + opline->extended_value = ZEND_RETURNS_NEW; + }*/ +} + +void zend_compile_compound_assign(znode *result, zend_ast *ast TSRMLS_DC) { + zend_ast *var_ast = ast->child[0]; + zend_ast *expr_ast = ast->child[1]; + + znode var_node, expr_node; + zend_op *opline; + + zend_ensure_writable_variable(var_ast); + zend_compile_expr(&expr_node, expr_ast TSRMLS_CC); + + switch (var_ast->kind) { + case ZEND_AST_VAR: + zend_compile_var(&var_node, var_ast, BP_VAR_RW TSRMLS_CC); + emit_op(result, ast->kind, &var_node, &expr_node TSRMLS_CC); + return; + case ZEND_AST_DIM: + opline = zend_compile_dim_common(result, var_ast, BP_VAR_RW TSRMLS_CC); + opline->opcode = ast->kind; + opline->extended_value = ZEND_ASSIGN_DIM; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_do_op_data(opline, &expr_node TSRMLS_CC); + opline->op2.var = get_temporary_variable(CG(active_op_array)); + opline->op2_type = IS_VAR; + SET_UNUSED(opline->result); + return; + case ZEND_AST_PROP: + opline = zend_compile_prop_common(result, var_ast, BP_VAR_RW TSRMLS_CC); + opline->opcode = ast->kind; + opline->extended_value = ZEND_ASSIGN_OBJ; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_do_op_data(opline, &expr_node TSRMLS_CC); + SET_UNUSED(opline->result); + return; + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +zend_uint zend_compile_params(zend_ast *params, zend_function *fbc TSRMLS_DC) { + // TODO.AST &var error + zend_uint i; + zend_bool uses_arg_unpack = 0; + zend_uint arg_count = 0; /* number of arguments not including unpacks */ + + for (i = 0; i < params->children; ++i) { + zend_ast *param = params->child[i]; + zend_uint arg_num = i + 1; + + znode param_node; + zend_op *opline; + zend_uchar opcode; + zend_ulong flags = 0; + + if (param->kind == ZEND_AST_UNPACK) { + uses_arg_unpack = 1; + + zend_compile_expr(¶m_node, param->child[0] TSRMLS_CC); + opline = emit_op(NULL, ZEND_SEND_UNPACK, ¶m_node, NULL TSRMLS_CC); + opline->op2.num = arg_count; + continue; + } + + if (uses_arg_unpack) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use positional argument after argument unpacking"); + } + + arg_count++; + if (zend_is_variable(param)) { + if (zend_is_call(param)) { + zend_compile_var(¶m_node, param, BP_VAR_R TSRMLS_CC); + opcode = ZEND_SEND_VAR_NO_REF; + flags |= ZEND_ARG_SEND_FUNCTION; + if (fbc && ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) { + flags |= ZEND_ARG_SEND_BY_REF; + if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) { + flags |= ZEND_ARG_SEND_SILENT; + } + } + } else if (fbc) { + if (ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) { + zend_compile_var(¶m_node, param, BP_VAR_W TSRMLS_CC); + opcode = ZEND_SEND_REF; + } else { + zend_compile_var(¶m_node, param, BP_VAR_R TSRMLS_CC); + opcode = ZEND_SEND_VAR; + } + } else { + zend_compile_var(¶m_node, param, + BP_VAR_FUNC_ARG | (arg_num << BP_VAR_SHIFT) TSRMLS_CC); + opcode = ZEND_SEND_VAR; + } + } else { + zend_compile_expr(¶m_node, param TSRMLS_CC); + if (param_node.op_type & (IS_VAR|IS_CV)) { + opcode = ZEND_SEND_VAR_NO_REF; + if (fbc && ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) { + flags |= ZEND_ARG_SEND_BY_REF; + } + } else { + opcode = ZEND_SEND_VAL; + if (fbc && ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) { + zend_error_noreturn(E_COMPILE_ERROR, "Only variables can be passed by reference"); + } + } + } + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = opcode; + SET_NODE(opline->op1, ¶m_node); + SET_UNUSED(opline->op2); + opline->op2.opline_num = arg_num; + + if (opcode == ZEND_SEND_VAR_NO_REF) { + if (fbc) { + flags |= ZEND_ARG_COMPILE_TIME_BOUND; + } + opline->extended_value = flags; + } else { + opline->extended_value = fbc ? ZEND_DO_FCALL : ZEND_DO_FCALL_BY_NAME; + } + + if (++CG(context).used_stack > CG(active_op_array)->used_stack) { + CG(active_op_array)->used_stack = CG(context).used_stack; + } + } + + return arg_count; +} + +zend_op *zend_compile_call_common(znode *result, zend_ast *params_ast, zend_function *fbc TSRMLS_DC) { + zend_op *opline; + zend_uint arg_count; + + zend_do_extended_fcall_begin(TSRMLS_C); + + if (!fbc) { + CG(context).nested_calls++; + } + + arg_count = zend_compile_params(params_ast, fbc TSRMLS_CC); + + opline = emit_op(result, fbc ? ZEND_DO_FCALL : ZEND_DO_FCALL_BY_NAME, NULL, NULL TSRMLS_CC); + opline->op2.num = CG(context).nested_calls; + opline->extended_value = arg_count; + + if (!fbc) { + CG(context).nested_calls--; + } + + zend_do_extended_fcall_end(TSRMLS_C); + + if (CG(context).nested_calls + 1 > CG(active_op_array)->nested_calls) { + CG(active_op_array)->nested_calls = CG(context).nested_calls + 1; + } + if (CG(context).used_stack + 1 > CG(active_op_array)->used_stack) { + CG(active_op_array)->used_stack = CG(context).used_stack + 1; + } + CG(context).used_stack -= arg_count; + + return opline; +} + +zend_bool zend_compile_function_name(znode *name_node, zend_ast *name_ast TSRMLS_DC) { + zend_bool check_namespace; + char *is_compound; + + zval *name = &name_node->u.constant; + ZVAL_COPY(name, zend_ast_get_zval(name_ast->child[0])); + name_node->op_type = IS_CONST; + + check_namespace = name_ast->kind == ZEND_AST_NAME; + is_compound = memchr(Z_STRVAL_P(name), '\\', Z_STRLEN_P(name)); + + zend_resolve_function_name(name_node, &check_namespace TSRMLS_CC); + + return check_namespace && Z_TYPE(CG(current_namespace)) != IS_UNDEF && !is_compound; +} + +void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *params_ast TSRMLS_DC) { + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_INIT_NS_FCALL_BY_NAME; + opline->result.num = CG(context).nested_calls; + SET_UNUSED(opline->op1); + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_ns_func_name_literal( + CG(active_op_array), &name_node->u.constant TSRMLS_CC); + GET_CACHE_SLOT(opline->op2.constant); + + zend_compile_call_common(result, params_ast, NULL TSRMLS_CC); +} + +void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *params_ast TSRMLS_DC) { + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_INIT_FCALL_BY_NAME; + opline->result.num = CG(context).nested_calls; + SET_UNUSED(opline->op1); + if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) { + opline->op2_type = IS_CONST; + opline->op2.constant + = zend_add_func_name_literal(CG(active_op_array), &name_node->u.constant TSRMLS_CC); + GET_CACHE_SLOT(opline->op2.constant); + } else { + SET_NODE(opline->op2, name_node); + } + + zend_compile_call_common(result, params_ast, NULL TSRMLS_CC); +} + +zend_bool zend_params_contain_unpack(zend_ast *params) { + zend_uint i; + for (i = 0; i < params->children; ++i) { + if (params->child[i]->kind == ZEND_AST_UNPACK) { + return 1; + } + } + return 0; +} + +void zend_compile_call(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_ast *name_ast = ast->child[0]; + zend_ast *params_ast = ast->child[1]; + + znode name_node; + + if (name_ast->kind != ZEND_AST_NAME && name_ast->kind != ZEND_AST_NAME_FQ) { + zend_compile_expr(&name_node, name_ast TSRMLS_CC); + zend_compile_dynamic_call(result, &name_node, params_ast TSRMLS_CC); + return; + } + + { + zend_bool needs_runtime_resolution = zend_compile_function_name(&name_node, name_ast TSRMLS_CC); + if (needs_runtime_resolution) { + zend_compile_ns_call(result, &name_node, params_ast TSRMLS_CC); + return; + } + } + + /* Arg unpacking needs EX(call), so don't try to optimize it away */ + if (zend_params_contain_unpack(params_ast)) { + zend_compile_dynamic_call(result, &name_node, params_ast TSRMLS_CC); + return; + } + + { + zval *name = &name_node.u.constant; + zend_string *lcname = STR_ALLOC(Z_STRLEN_P(name), 0); + zend_function *fbc; + zend_op *opline; + + zend_str_tolower_copy(lcname->val, Z_STRVAL_P(name), Z_STRLEN_P(name)); + if (!(fbc = zend_hash_find_ptr(CG(function_table), lcname)) || + ((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) && + fbc->type == ZEND_INTERNAL_FUNCTION) + ) { + STR_FREE(lcname); + zend_compile_dynamic_call(result, &name_node, params_ast TSRMLS_CC); + return; + } + + zval_ptr_dtor(&name_node.u.constant); + ZVAL_NEW_STR(&name_node.u.constant, lcname); + + opline = zend_compile_call_common(result, params_ast, fbc TSRMLS_CC); + SET_NODE(opline->op1, &name_node); + GET_CACHE_SLOT(opline->op1.constant); + } +} + +void zend_compile_method_call(znode *result, zend_ast *ast, int type TSRMLS_DC) { + //zend_ast *obj_ast = ast->child[0]; + //zend_ast *method_ast = ast->child[1]; + zend_ast *params_ast = ast->child[2]; + + // TODO.AST __clone check - WTF is that done in here?! + + // TODO.AST using an evil overload from AST_METHOD to AST_PROP here ... + zend_op *opline = zend_compile_prop_common(NULL, ast, BP_VAR_R TSRMLS_CC); + opline->opcode = ZEND_INIT_METHOD_CALL; + opline->result.num = CG(context).nested_calls; + + if (opline->op2_type == IS_CONST) { + zval name; + name = CONSTANT(opline->op2.constant); + if (Z_TYPE(name) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Method name must be a string"); + } + Z_STR(name) = STR_COPY(Z_STR(name)); + FREE_POLYMORPHIC_CACHE_SLOT(opline->op2.constant); + opline->op2.constant = + zend_add_func_name_literal(CG(active_op_array), &name TSRMLS_CC); + GET_POLYMORPHIC_CACHE_SLOT(opline->op2.constant); + } + + zend_compile_call_common(result, params_ast, NULL TSRMLS_CC); +} + +zend_bool zend_is_constructor(zend_string *name) { + char *lcname = zend_str_tolower_dup(name->val, name->len); + zend_bool is_ctor = name->len == sizeof(ZEND_CONSTRUCTOR_FUNC_NAME) - 1 && + !memcmp(lcname, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME) - 1); + efree(lcname); + return is_ctor; +} + +void zend_compile_static_call(znode *result, zend_ast *ast, int type TSRMLS_DC) { + zend_ast *class_ast = ast->child[0]; + zend_ast *method_ast = ast->child[1]; + zend_ast *params_ast = ast->child[2]; + + znode class_name_node, class_node, method_node; + zend_op *opline; + zend_ulong extended_value = 0; + + zend_compile_expr(&class_name_node, class_ast TSRMLS_CC); + if (class_name_node.op_type == IS_CONST && + ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type( + Z_STRVAL(class_name_node.u.constant), Z_STRLEN(class_name_node.u.constant)) + ) { + zend_resolve_class_name(&class_name_node TSRMLS_CC); + class_node = class_name_node; + } else { + zend_do_fetch_class(&class_node, &class_name_node TSRMLS_CC); + extended_value = class_node.EA; + } + + zend_compile_expr(&method_node, method_ast TSRMLS_CC); + if (method_node.op_type == IS_CONST) { + zval *name = &method_node.u.constant; + if (Z_TYPE_P(name) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Method name must be a string"); + } + if (zend_is_constructor(Z_STR_P(name))) { + zval_ptr_dtor(name); + method_node.op_type = IS_UNUSED; + } + } + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_INIT_STATIC_METHOD_CALL; + opline->result.num = CG(context).nested_calls; + opline->extended_value = extended_value; + + if (class_node.op_type == IS_CONST) { + opline->op1_type = IS_CONST; + opline->op1.constant = + zend_add_class_name_literal(CG(active_op_array), &class_node.u.constant TSRMLS_CC); + } else { + SET_NODE(opline->op1, &class_node); + } + + if (method_node.op_type == IS_CONST) { + opline->op2_type = IS_CONST; + opline->op2.constant = + zend_add_func_name_literal(CG(active_op_array), &method_node.u.constant TSRMLS_CC); + if (opline->op1_type == IS_CONST) { + GET_CACHE_SLOT(opline->op2.constant); + } else { + GET_POLYMORPHIC_CACHE_SLOT(opline->op2.constant); + } + } else { + SET_NODE(opline->op2, &method_node); + } + + zend_compile_call_common(result, params_ast, NULL TSRMLS_CC); +} + +void zend_compile_global_var(zend_ast *ast TSRMLS_DC) { + zend_ast *var_ast = ast->child[0]; + + znode var_node, result; + zend_op *opline; + + zend_compile_expr(&var_node, var_ast TSRMLS_CC); + if (var_node.op_type == IS_CONST) { + if (Z_TYPE(var_node.u.constant) != IS_STRING) { + convert_to_string(&var_node.u.constant); + } + } + + opline = emit_op(&result, ZEND_FETCH_W, &var_node, NULL TSRMLS_CC); + opline->extended_value = ZEND_FETCH_GLOBAL_LOCK; + + if (var_node.op_type == IS_CONST) { + //zval_copy_ctor(&var_node.u.constant); + } + + zend_ast *fetch_ast = zend_ast_create_unary(ZEND_AST_VAR, var_ast); + zend_compile_assign_ref_common(NULL, fetch_ast, &result TSRMLS_CC); + efree(fetch_ast); +} + +void zend_compile_unset(zend_ast *ast TSRMLS_DC) { + zend_ast *var_ast = ast->child[0]; + + znode var_node; + zend_op *opline; + switch (var_ast->kind) { + case ZEND_AST_VAR: + if (zend_try_compile_cv(&var_node, ast TSRMLS_CC) == SUCCESS) { + opline = emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL TSRMLS_CC); + opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET; + } else { + opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET TSRMLS_CC); + opline->opcode = ZEND_UNSET_VAR; + } + return; + case ZEND_AST_DIM: + opline = zend_compile_dim_common(NULL, var_ast, BP_VAR_UNSET TSRMLS_CC); + opline->opcode = ZEND_UNSET_DIM; + return; + case ZEND_AST_PROP: + opline = zend_compile_prop_common(NULL, var_ast, BP_VAR_UNSET TSRMLS_CC); + opline->opcode = ZEND_UNSET_OBJ; + return; + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +void zend_compile_stmt(zend_ast *ast TSRMLS_DC) { + switch (ast->kind) { + case ZEND_AST_GLOBAL: + zend_compile_global_var(ast TSRMLS_CC); + return; + case ZEND_AST_UNSET: + zend_compile_unset(ast TSRMLS_CC); + return; + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) { + switch (ast->kind) { + case ZEND_CONST: + ZVAL_COPY(&result->u.constant, zend_ast_get_zval(ast)); + result->op_type = IS_CONST; + return; + case ZEND_AST_ZNODE: + *result = *zend_ast_get_znode(ast); + return; + case ZEND_AST_VAR: + case ZEND_AST_DIM: + case ZEND_AST_PROP: + case ZEND_AST_STATIC_PROP: + case ZEND_AST_CALL: + case ZEND_AST_METHOD_CALL: + case ZEND_AST_STATIC_CALL: + zend_compile_var(result, ast, BP_VAR_R TSRMLS_CC); + return; + case ZEND_AST_ASSIGN: + zend_compile_assign(result, ast TSRMLS_CC); + return; + case ZEND_AST_ASSIGN_REF: + zend_compile_assign_ref(result, ast TSRMLS_CC); + return; + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_POW: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_XOR: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + zend_compile_compound_assign(result, ast TSRMLS_CC); + return; + default: + ZEND_ASSERT(0 /* not supported */); + } +} + +void zend_compile_var(znode *result, zend_ast *ast, int type TSRMLS_DC) { + switch (ast->kind) { + case ZEND_AST_VAR: + zend_compile_simple_var(result, ast, type TSRMLS_CC); + return; + case ZEND_AST_DIM: + zend_compile_dim(result, ast, type TSRMLS_CC); + return; + case ZEND_AST_PROP: + zend_compile_prop(result, ast, type TSRMLS_CC); + return; + case ZEND_AST_STATIC_PROP: + zend_compile_static_prop(result, ast, type TSRMLS_CC); + return; + case ZEND_AST_CALL: + zend_compile_call(result, ast, type TSRMLS_CC); + return; + case ZEND_AST_METHOD_CALL: + zend_compile_method_call(result, ast, type TSRMLS_CC); + return; + case ZEND_AST_STATIC_CALL: + zend_compile_static_call(result, ast, type TSRMLS_CC); + return; + default: + // TODO.AST check writable + zend_compile_expr(result, ast TSRMLS_CC); + return; + } +} + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index bb4142884d..7fb428f15a 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -87,6 +87,22 @@ typedef struct _znode { /* used only during compilation */ zend_uint EA; /* extended attributes */ } znode; +/* Temporarily defined here, to avoid header ordering issues */ +typedef struct _zend_ast_znode { + unsigned short kind; + unsigned short children; + znode node; +} zend_ast_znode; +ZEND_API zend_ast *zend_ast_create_znode(znode *node); + +static inline znode *zend_ast_get_znode(zend_ast *ast) { + return &((zend_ast_znode *) ast)->node; +} + +void zend_compile_stmt(zend_ast *ast TSRMLS_DC); +void zend_compile_expr(znode *node, zend_ast *ast TSRMLS_DC); +void zend_compile_var(znode *node, zend_ast *ast, int type TSRMLS_DC); + typedef struct _zend_execute_data zend_execute_data; #define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data TSRMLS_DC @@ -759,9 +775,13 @@ int zend_add_literal(zend_op_array *op_array, zval *zv TSRMLS_DC); #define BP_VAR_W 1 #define BP_VAR_RW 2 #define BP_VAR_IS 3 -#define BP_VAR_NA 4 /* if not applicable */ -#define BP_VAR_FUNC_ARG 5 -#define BP_VAR_UNSET 6 +#define BP_VAR_FUNC_ARG 4 +#define BP_VAR_UNSET 5 +#define BP_VAR_REF 6 /* right-hand side of by-ref assignment */ + +/* Bottom 3 bits are the type, top bits are arg num for BP_VAR_FUNC_ARG */ +#define BP_VAR_SHIFT 3 +#define BP_VAR_MASK 7 #define ZEND_INTERNAL_FUNCTION 1 diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 013e3ddbf0..77508e048e 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -389,7 +389,8 @@ unset_variables: ; unset_variable: - variable { zend_do_end_variable_parse(&$1, BP_VAR_UNSET, 0 TSRMLS_CC); zend_do_unset(&$1 TSRMLS_CC); } + variable + { $$.u.ast = zend_ast_create_unary(ZEND_AST_UNSET, $1.u.ast); AST_COMPILE_STMT($$.u.ast); } ; function_declaration_statement: @@ -469,9 +470,9 @@ foreach_optional_arg: ; foreach_variable: - variable { zend_check_writable_variable(&$1); $$ = $1; } - | '&' variable { zend_check_writable_variable(&$2); $$ = $2; $$.EA |= ZEND_PARSED_REFERENCE_VARIABLE; } - | T_LIST '(' { zend_do_list_init(TSRMLS_C); } assignment_list ')' { $$ = $1; $$.EA = ZEND_PARSED_LIST_EXPR; } + variable { $$.u.ast = $1.u.ast; $$.EA = 0; } + | '&' variable { $$.u.ast = $2.u.ast; $$.EA = ZEND_PARSED_REFERENCE_VARIABLE; } + | T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; $$.EA = ZEND_PARSED_LIST_EXPR; } ; for_statement: @@ -596,13 +597,37 @@ function_call_parameter: | T_ELLIPSIS expr { zend_do_unpack_params(&$2 TSRMLS_CC); } ; +function_call_parameter_list_ast: + '(' ')' { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAMS); } + | '(' non_empty_function_call_parameter_list_ast ')' { $$.u.ast = $2.u.ast; } + | '(' yield_expr ')' + { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAMS); + zend_ast_dynamic_add(&$$.u.ast, AST_ZNODE(&$2)); } +; + +non_empty_function_call_parameter_list_ast: + function_call_parameter_ast + { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAMS); + zend_ast_dynamic_add(&$$.u.ast, $1.u.ast); } + | non_empty_function_call_parameter_list_ast ',' function_call_parameter_ast + { zend_ast_dynamic_add(&$1.u.ast, $3.u.ast); $$.u.ast = $1.u.ast; } +; + +function_call_parameter_ast: + expr_without_variable { $$.u.ast = AST_ZNODE(&$1); } + | variable { $$.u.ast = $1.u.ast; } + | '&' w_variable { /* ERROR */ ZEND_ASSERT(0); } + | T_ELLIPSIS expr { $$.u.ast = zend_ast_create_unary(ZEND_AST_UNPACK, AST_ZNODE(&$2)); } +; + global_var_list: global_var_list ',' global_var | global_var ; global_var: - simple_variable { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); } + simple_variable + { $$.u.ast = zend_ast_create_unary(ZEND_AST_GLOBAL, $1.u.ast); AST_COMPILE_STMT($$.u.ast); } ; @@ -749,23 +774,53 @@ new_expr: ; expr_without_variable: - T_LIST '(' { zend_do_list_init(TSRMLS_C); } assignment_list ')' '=' expr { zend_do_list_end(&$$, &$7 TSRMLS_CC); } - | variable '=' expr { zend_check_writable_variable(&$1); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); } - | variable '=' '&' variable { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$4, BP_VAR_W, 1 TSRMLS_CC); zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); zend_do_assign_ref(&$$, &$1, &$4 TSRMLS_CC); } + T_LIST '(' assignment_list ')' '=' expr + { $$.u.ast = zend_ast_create_binary(ZEND_AST_ASSIGN, $3.u.ast, AST_ZNODE(&$6)); + AST_COMPILE(&$$, $$.u.ast); } + | variable '=' expr + { $$.u.ast = zend_ast_create_binary(ZEND_AST_ASSIGN, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $$.u.ast); } + | variable '=' '&' variable + { $$.u.ast = zend_ast_create_binary(ZEND_AST_ASSIGN_REF, $1.u.ast, $4.u.ast); + AST_COMPILE(&$$, $$.u.ast); } | variable '=' '&' T_NEW class_name_reference { zend_error(E_DEPRECATED, "Assigning the return value of new by reference is deprecated"); zend_check_writable_variable(&$1); zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$4, &$5 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$3, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); $3.EA = ZEND_PARSED_NEW; zend_do_assign_ref(&$$, &$1, &$3 TSRMLS_CC); } | T_CLONE expr { zend_do_clone(&$$, &$2 TSRMLS_CC); } - | variable T_PLUS_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_ADD, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_MINUS_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SUB, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_MUL_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MUL, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_POW_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_POW, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_DIV_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_DIV, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_CONCAT_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_CONCAT, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_MOD_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MOD, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_AND_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_AND, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_OR_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_OR, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_XOR_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_XOR, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_SL_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SL, &$$, &$1, &$3 TSRMLS_CC); } - | variable T_SR_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SR, &$$, &$1, &$3 TSRMLS_CC); } + | variable T_PLUS_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_ADD, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_MINUS_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_SUB, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_MUL_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_MUL, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_POW_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_POW, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_DIV_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_DIV, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_CONCAT_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_CONCAT, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_MOD_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_MOD, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_AND_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_BW_AND, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_OR_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_BW_OR, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_XOR_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_BW_XOR, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_SL_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_SL, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } + | variable T_SR_EQUAL expr + { $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_SR, $1.u.ast, AST_ZNODE(&$3)); + AST_COMPILE(&$$, $2.u.ast); } | rw_variable T_INC { zend_do_post_incdec(&$$, &$1, ZEND_POST_INC TSRMLS_CC); } | T_INC rw_variable { zend_do_pre_incdec(&$$, &$2, ZEND_PRE_INC TSRMLS_CC); } | rw_variable T_DEC { zend_do_post_incdec(&$$, &$1, ZEND_POST_DEC TSRMLS_CC); } @@ -853,18 +908,25 @@ lexical_var_list: ; function_call: - namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); } - function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_NAMESPACE T_NS_SEPARATOR namespace_name { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $$.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); } - function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_NS_SEPARATOR namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); } - function_call_parameter_list { zend_do_end_function_call(&$2, &$$, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | class_name T_PAAMAYIM_NEKUDOTAYIM member_name { $$.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list { zend_do_end_function_call(NULL, &$$, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | callable_expr { zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); } - function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + namespace_name function_call_parameter_list_ast + { $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL, + zend_ast_create_unary(ZEND_AST_NAME, AST_ZVAL(&$1)), $2.u.ast); } + | T_NAMESPACE T_NS_SEPARATOR namespace_name function_call_parameter_list_ast + { ZVAL_EMPTY_STRING(&$1.u.constant); + zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); + $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL, + zend_ast_create_unary(ZEND_AST_NAME_FQ, AST_ZVAL(&$1)), $4.u.ast); } + | T_NS_SEPARATOR namespace_name function_call_parameter_list_ast + { $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL, + zend_ast_create_unary(ZEND_AST_NAME_FQ, AST_ZVAL(&$2)), $3.u.ast); } + | class_name T_PAAMAYIM_NEKUDOTAYIM member_name function_call_parameter_list_ast + { $$.u.ast = zend_ast_create_ternary(ZEND_AST_STATIC_CALL, + AST_ZVAL(&$1), $3.u.ast, $4.u.ast); } + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name function_call_parameter_list_ast + { $$.u.ast = zend_ast_create_ternary(ZEND_AST_STATIC_CALL, + AST_ZNODE(&$1), $3.u.ast, $4.u.ast); } + | callable_expr function_call_parameter_list_ast + { $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL, $1.u.ast, $2.u.ast); } ; class_name: @@ -884,7 +946,7 @@ fully_qualified_class_name: class_name_reference: class_name { zend_do_fetch_class(&$$, &$1 TSRMLS_CC); } - | new_variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); } + | new_variable { AST_COMPILE_VAR(&$1, $1.u.ast, BP_VAR_R); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); } ; exit_expr: @@ -1032,112 +1094,110 @@ parenthesis_expr: r_variable: - variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; } + variable { zend_compile_var(&$$, $1.u.ast, BP_VAR_R TSRMLS_CC); zend_ast_destroy($1.u.ast); } ; - w_variable: - variable { zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); $$ = $1; + variable { zend_compile_var(&$$, $1.u.ast, BP_VAR_W TSRMLS_CC); zend_ast_destroy($1.u.ast); zend_check_writable_variable(&$1); } ; rw_variable: - variable { zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); $$ = $1; - zend_check_writable_variable(&$1); } + variable { zend_compile_var(&$$, $1.u.ast, BP_VAR_RW TSRMLS_CC); zend_ast_destroy($1.u.ast); + zend_check_writable_variable(&$$); } ; variable_class_name: - dereferencable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; } + dereferencable { zend_compile_expr(&$$, $1.u.ast TSRMLS_CC); zend_ast_destroy($1.u.ast); } ; dereferencable: - variable { $$ = $1; } - | '(' expr ')' { $$ = $2; zend_do_begin_variable_parse(TSRMLS_C); $$.EA = 0; } - | dereferencable_scalar { $$ = $1; zend_do_begin_variable_parse(TSRMLS_C); $$.EA = 0; } + variable { $$.u.ast = $1.u.ast; } + | '(' expr ')' { $$.u.ast = AST_ZNODE(&$2); } + | dereferencable_scalar { $$.u.ast = AST_ZNODE(&$1); } ; callable_expr: - callable_variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; } - | '(' expr ')' { $$ = $2; } - | dereferencable_scalar { $$ = $1; } + callable_variable { $$.u.ast = $1.u.ast; } + | '(' expr ')' { $$.u.ast = AST_ZNODE(&$2); } + | dereferencable_scalar { $$.u.ast = AST_ZNODE(&$1); } ; callable_variable: simple_variable - { zend_do_begin_variable_parse(TSRMLS_C); - fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); - $$.EA = ZEND_PARSED_VARIABLE; } + { $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $1.u.ast); } | dereferencable '[' dim_offset ']' - { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); $$.EA = ZEND_PARSED_VARIABLE; } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, $3.u.ast); } | dereferencable '{' expr '}' - { fetch_string_offset(&$$, &$1, &$3 TSRMLS_CC); $$.EA = ZEND_PARSED_VARIABLE; } - | dereferencable T_OBJECT_OPERATOR member_name - { zend_do_fetch_property(&$$, &$1, &$3 TSRMLS_CC); - zend_do_begin_method_call(&$$ TSRMLS_CC); } - function_call_parameter_list - { zend_do_end_function_call(&$4, &$$, 1, 1 TSRMLS_CC); - zend_do_extended_fcall_end(TSRMLS_C); - $$.EA = ZEND_PARSED_METHOD_CALL; } - | function_call - { zend_do_begin_variable_parse(TSRMLS_C); - $$ = $1; $$.EA = ZEND_PARSED_FUNCTION_CALL; } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, AST_ZNODE(&$3)); } + | dereferencable T_OBJECT_OPERATOR member_name function_call_parameter_list_ast + { $$.u.ast = zend_ast_create_ternary(ZEND_AST_METHOD_CALL, $1.u.ast, $3.u.ast, $4.u.ast); } + | function_call { $$.u.ast = $1.u.ast; } ; variable: - callable_variable { $$ = $1; } - | static_member { $$ = $1; $$.EA = ZEND_PARSED_STATIC_MEMBER; } + callable_variable + { $$.u.ast = $1.u.ast; } + | static_member + { $$.u.ast = $1.u.ast; } | dereferencable T_OBJECT_OPERATOR member_name - { zend_do_fetch_property(&$$, &$1, &$3 TSRMLS_CC); $$.EA = ZEND_PARSED_MEMBER; } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_PROP, $1.u.ast, $3.u.ast); } ; simple_variable: - T_VARIABLE { $$ = $1; } - | '$' '{' expr '}' { $$ = $3; } - | '$' simple_variable { zend_do_indirect_reference(&$$, &$2 TSRMLS_CC); } + T_VARIABLE + { $$.u.ast = AST_ZVAL(&$1); } + | '$' '{' expr '}' + { $$.u.ast = zend_ast_create_znode(&$3); } + | '$' simple_variable + { $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $2.u.ast); } ; static_member: - class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); } - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); } - + class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable + { $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, AST_ZVAL(&$1), $3.u.ast); } + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable + { $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, AST_ZNODE(&$1), $3.u.ast); } ; new_variable: simple_variable - { zend_do_begin_variable_parse(TSRMLS_C); - fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); } - | new_variable '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); } - | new_variable '{' expr '}' { fetch_string_offset(&$$, &$1, &$3 TSRMLS_CC); } + { $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $1.u.ast); } + | new_variable '[' dim_offset ']' + { $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, $3.u.ast); } + | new_variable '{' expr '}' + { $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, AST_ZNODE(&$3)); } | new_variable T_OBJECT_OPERATOR member_name - { zend_do_fetch_property(&$$, &$1, &$3 TSRMLS_CC); } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_PROP, $1.u.ast, $3.u.ast); } | class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable - { zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, AST_ZVAL(&$1), $3.u.ast); } | new_variable T_PAAMAYIM_NEKUDOTAYIM simple_variable - { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); - zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); } + { $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, $1.u.ast, $3.u.ast); } ; dim_offset: - /* empty */ { $$.op_type = IS_UNUSED; } - | expr { $$ = $1; } + /* empty */ { $$.u.ast = NULL; } + | expr { $$.u.ast = AST_ZNODE(&$1); } ; member_name: - T_STRING { $$ = $1; } - | '{' expr '}' { $$ = $2; } - | simple_variable { fetch_simple_variable_ex(&$$, &$1, 0, ZEND_FETCH_R TSRMLS_CC); } + T_STRING { $$.u.ast = AST_ZVAL(&$1); } + | '{' expr '}' { $$.u.ast = AST_ZNODE(&$2); } + | simple_variable { $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $1.u.ast); } ; assignment_list: assignment_list ',' assignment_list_element + { zend_ast_dynamic_add(&$1.u.ast, $3.u.ast); $$.u.ast = $1.u.ast; } | assignment_list_element + { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_LIST); + zend_ast_dynamic_add(&$$.u.ast, $1.u.ast); } ; - assignment_list_element: - variable { zend_do_add_list_element(&$1 TSRMLS_CC); } - | T_LIST '(' { zend_do_new_list_begin(TSRMLS_C); } assignment_list ')' { zend_do_new_list_end(TSRMLS_C); } - | /* empty */ { zend_do_add_list_element(NULL TSRMLS_CC); } + variable { $$.u.ast = $1.u.ast; } + | T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; } + | /* empty */ { $$.u.ast = NULL; } ; @@ -1158,34 +1218,42 @@ non_empty_array_pair_list: ; encaps_list: - encaps_list encaps_var { zend_do_end_variable_parse(&$2, BP_VAR_R, 0 TSRMLS_CC); zend_do_add_variable(&$$, &$1, &$2 TSRMLS_CC); } + encaps_list encaps_var + { AST_COMPILE(&$2, $2.u.ast); zend_do_add_variable(&$$, &$1, &$2 TSRMLS_CC); } | encaps_list T_ENCAPSED_AND_WHITESPACE { zend_do_add_string(&$$, &$1, &$2 TSRMLS_CC); } - | encaps_var { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_add_variable(&$$, NULL, &$1 TSRMLS_CC); } - | T_ENCAPSED_AND_WHITESPACE encaps_var { zend_do_add_string(&$$, NULL, &$1 TSRMLS_CC); zend_do_end_variable_parse(&$2, BP_VAR_R, 0 TSRMLS_CC); zend_do_add_variable(&$$, &$$, &$2 TSRMLS_CC); } + | encaps_var { AST_COMPILE(&$1, $1.u.ast); zend_do_add_variable(&$$, NULL, &$1 TSRMLS_CC); } + | T_ENCAPSED_AND_WHITESPACE encaps_var + { zend_do_add_string(&$$, NULL, &$1 TSRMLS_CC); + AST_COMPILE(&$2, $2.u.ast); zend_do_add_variable(&$$, &$$, &$2 TSRMLS_CC); } ; - - encaps_var: - T_VARIABLE { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); } - | T_VARIABLE '[' { zend_do_begin_variable_parse(TSRMLS_C); } encaps_var_offset ']' { fetch_array_begin(&$$, &$1, &$4 TSRMLS_CC); } - | T_VARIABLE T_OBJECT_OPERATOR T_STRING { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$2, &$1, 1 TSRMLS_CC); zend_do_fetch_property(&$$, &$2, &$3 TSRMLS_CC); } - | T_DOLLAR_OPEN_CURLY_BRACES expr '}' { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$$, &$2, 1 TSRMLS_CC); } - | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}' { zend_do_begin_variable_parse(TSRMLS_C); fetch_array_begin(&$$, &$2, &$4 TSRMLS_CC); } - | T_CURLY_OPEN variable '}' { $$ = $2; } + T_VARIABLE + { $$.u.ast = zend_ast_create_var(&$1.u.constant); } + | T_VARIABLE '[' encaps_var_offset ']' + { $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, + zend_ast_create_var(&$1.u.constant), $3.u.ast); } + | T_VARIABLE T_OBJECT_OPERATOR T_STRING + { $$.u.ast = zend_ast_create_binary(ZEND_AST_PROP, + zend_ast_create_var(&$1.u.constant), AST_ZNODE(&$3)); } + | T_DOLLAR_OPEN_CURLY_BRACES expr '}' + { $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, AST_ZNODE(&$2)); } + | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}' + { $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, + zend_ast_create_var(&$2.u.constant), AST_ZNODE(&$4)); } + | T_CURLY_OPEN variable '}' { $$.u.ast = $2.u.ast; } ; - encaps_var_offset: - T_STRING { $$ = $1; } - | T_NUM_STRING { $$ = $1; } - | T_VARIABLE { fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); } + T_STRING { $$.u.ast = AST_ZVAL(&$1); } + | T_NUM_STRING { $$.u.ast = AST_ZVAL(&$1); } + | T_VARIABLE { $$.u.ast = zend_ast_create_var(&$1.u.constant); } ; internal_functions_in_yacc: T_ISSET '(' isset_variables ')' { $$ = $3; } - | T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); } + | T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); } | T_EMPTY '(' expr_without_variable ')' { zend_do_unary_op(ZEND_BOOL_NOT, &$$, &$3 TSRMLS_CC); } | T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); } | T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); } @@ -1200,7 +1268,7 @@ isset_variables: ; isset_variable: - variable { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); } + variable { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); } | expr_without_variable { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use isset() on the result of an expression (you can use \"null !== expression\" instead)"); } ;