diff options
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 240 |
1 files changed, 205 insertions, 35 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 917e0c1097..ac3e633e28 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -179,6 +179,9 @@ void zend_init_compiler_context(TSRMLS_D) /* {{{ */ CG(context).literals_size = 0; CG(context).current_brk_cont = -1; CG(context).backpatch_count = 0; + CG(context).nested_calls = 0; + CG(context).used_stack = 0; + CG(context).in_finally = 0; CG(context).labels = NULL; } /* }}} */ @@ -285,7 +288,7 @@ ZEND_API zend_bool zend_is_compiling(TSRMLS_D) /* {{{ */ static zend_uint get_temporary_variable(zend_op_array *op_array) /* {{{ */ { - return (op_array->T)++ * ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)); + return (zend_uint)EX_TMP_VAR_NUM(0, (op_array->T)++); } /* }}} */ @@ -1723,7 +1726,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } { - /* Push a seperator to the switch and foreach stacks */ + /* Push a seperator to the switch stack */ zend_switch_entry switch_entry; switch_entry.cond.op_type = IS_UNUSED; @@ -1731,16 +1734,16 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n switch_entry.control_var = 0; zend_stack_push(&CG(switch_cond_stack), (void *) &switch_entry, sizeof(switch_entry)); + } - { - /* Foreach stack separator */ - zend_op dummy_opline; + { + /* Push a separator to the foreach stack */ + zend_op dummy_opline; - dummy_opline.result_type = IS_UNUSED; - dummy_opline.op1_type = IS_UNUSED; + dummy_opline.result_type = IS_UNUSED; + dummy_opline.op1_type = IS_UNUSED; - zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op)); - } + zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op)); } if (CG(doc_comment)) { @@ -1950,6 +1953,9 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace function_name->u.constant.value.str.val = lcname; zend_stack_push(&CG(function_call_stack), (void *) &function, sizeof(zend_function *)); + if (CG(context).nested_calls + 1 > CG(active_op_array)->nested_calls) { + CG(active_op_array)->nested_calls = CG(context).nested_calls + 1; + } zend_do_extended_fcall_begin(TSRMLS_C); return 0; } @@ -1988,11 +1994,13 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */ GET_POLYMORPHIC_CACHE_SLOT(last_op->op2.constant); } last_op->opcode = ZEND_INIT_METHOD_CALL; - SET_UNUSED(last_op->result); + last_op->result_type = IS_UNUSED; + last_op->result.num = CG(context).nested_calls; Z_LVAL(left_bracket->u.constant) = ZEND_INIT_FCALL_BY_NAME; } else { 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 (left_bracket->op_type == IS_CONST) { opline->op2_type = IS_CONST; @@ -2004,6 +2012,9 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */ } zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *)); + if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) { + CG(active_op_array)->nested_calls = CG(context).nested_calls; + } zend_do_extended_fcall_begin(TSRMLS_C); } /* }}} */ @@ -2031,12 +2042,14 @@ void zend_do_begin_dynamic_function_call(znode *function_name, int ns_call TSRML /* In run-time PHP will check for function with full name and internal function with short name */ 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), &function_name->u.constant TSRMLS_CC); GET_CACHE_SLOT(opline->op2.constant); } else { opline->opcode = ZEND_INIT_FCALL_BY_NAME; + opline->result.num = CG(context).nested_calls; SET_UNUSED(opline->op1); if (function_name->op_type == IS_CONST) { opline->op2_type = IS_CONST; @@ -2048,6 +2061,9 @@ void zend_do_begin_dynamic_function_call(znode *function_name, int ns_call TSRML } zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *)); + if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) { + CG(active_op_array)->nested_calls = CG(context).nested_calls; + } zend_do_extended_fcall_begin(TSRMLS_C); } /* }}} */ @@ -2395,6 +2411,7 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na opline->extended_value = class_node.EA ; } opline->opcode = ZEND_INIT_STATIC_METHOD_CALL; + opline->result.num = CG(context).nested_calls; if (class_node.op_type == IS_CONST) { opline->op1_type = IS_CONST; opline->op1.constant = @@ -2416,6 +2433,9 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na } zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *)); + if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) { + CG(active_op_array)->nested_calls = CG(context).nested_calls; + } zend_do_extended_fcall_begin(TSRMLS_C); return 1; /* Dynamic */ } @@ -2436,21 +2456,29 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) { opline->opcode = ZEND_DO_FCALL; SET_NODE(opline->op1, function_name); + SET_UNUSED(opline->op2); + opline->op2.num = CG(context).nested_calls; CALCULATE_LITERAL_HASH(opline->op1.constant); GET_CACHE_SLOT(opline->op1.constant); } else { opline->opcode = ZEND_DO_FCALL_BY_NAME; SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + opline->op2.num = --CG(context).nested_calls; } } opline->result.var = get_temporary_variable(CG(active_op_array)); opline->result_type = IS_VAR; - GET_NODE(result, opline->result) ; - SET_UNUSED(opline->op2); + GET_NODE(result, opline->result); zend_stack_del_top(&CG(function_call_stack)); opline->extended_value = Z_LVAL(argument_list->u.constant); + + 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 -= Z_LVAL(argument_list->u.constant); } /* }}} */ @@ -2558,6 +2586,10 @@ void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC) /* {{ SET_NODE(opline->op1, param); opline->op2.opline_num = offset; SET_UNUSED(opline->op2); + + if (++CG(context).used_stack > CG(active_op_array)->used_stack) { + CG(active_op_array)->used_stack = CG(context).used_stack; + } } /* }}} */ @@ -2612,9 +2644,12 @@ 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; + + /* The error for use of return inside a generator is thrown in pass_two. */ if (do_end_vparse) { - if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(expr)) { + if (returns_reference && !zend_is_function_or_method_call(expr)) { zend_do_end_variable_parse(expr, BP_VAR_W, 0 TSRMLS_CC); } else { zend_do_end_variable_parse(expr, BP_VAR_R, 0 TSRMLS_CC); @@ -2637,9 +2672,16 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ start_op_number++; } + if (CG(context).in_finally) { + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_DISCARD_EXCEPTION; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); - opline->opcode = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) ? ZEND_RETURN_BY_REF : ZEND_RETURN; + opline->opcode = returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN; if (expr) { SET_NODE(opline->op1, expr); @@ -2656,12 +2698,59 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ +void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC) /* {{{ */ +{ + zend_op *opline; + + if (!CG(active_op_array)->function_name) { + zend_error(E_COMPILE_ERROR, "The \"yield\" expression can only be used inside a function"); + } + + 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, 0 TSRMLS_CC); + } else { + zend_do_end_variable_parse(value, BP_VAR_R, 0 TSRMLS_CC); + } + } + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_YIELD; + + if (value) { + SET_NODE(opline->op1, value); + + if (is_variable && zend_is_function_or_method_call(value)) { + opline->extended_value = ZEND_RETURNS_FUNCTION; + } + } else { + SET_UNUSED(opline->op1); + } + + if (key) { + SET_NODE(opline->op2, key); + } else { + SET_UNUSED(opline->op2); + } + + opline->result_type = IS_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); +} +/* }}} */ + static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ { int try_catch_offset = CG(active_op_array)->last_try_catch++; CG(active_op_array)->try_catch_array = erealloc(CG(active_op_array)->try_catch_array, sizeof(zend_try_catch_element)*CG(active_op_array)->last_try_catch); CG(active_op_array)->try_catch_array[try_catch_offset].try_op = try_op; + CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = 0; + CG(active_op_array)->try_catch_array[try_catch_offset].finally_op = 0; + CG(active_op_array)->try_catch_array[try_catch_offset].finally_end = 0; return try_catch_offset; } /* }}} */ @@ -2678,7 +2767,7 @@ void zend_do_first_catch(znode *open_parentheses TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_initialize_try_catch_element(const znode *try_token TSRMLS_DC) /* {{{ */ +void zend_initialize_try_catch_element(znode *catch_token TSRMLS_DC) /* {{{ */ { int jmp_op_number = get_next_op_number(CG(active_op_array)); zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -2695,7 +2784,7 @@ void zend_initialize_try_catch_element(const znode *try_token TSRMLS_DC) /* {{{ zend_stack_top(&CG(bp_stack), (void **) &jmp_list_ptr); zend_llist_add_element(jmp_list_ptr, &jmp_op_number); - zend_add_catch_element(try_token->u.op.opline_num, get_next_op_number(CG(active_op_array)) TSRMLS_CC); + catch_token->EA = get_next_op_number(CG(active_op_array)); } /* }}} */ @@ -2721,7 +2810,29 @@ void zend_do_try(znode *try_token TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_do_begin_catch(znode *try_token, znode *class_name, znode *catch_var, znode *first_catch TSRMLS_DC) /* {{{ */ +void zend_do_finally(znode *finally_token TSRMLS_DC) /* {{{ */ +{ + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + finally_token->u.op.opline_num = get_next_op_number(CG(active_op_array)); + /* call the the "finally" block */ + opline->opcode = ZEND_FAST_CALL; + SET_UNUSED(opline->op1); + opline->op1.opline_num = finally_token->u.op.opline_num + 1; + SET_UNUSED(opline->op2); + /* jump to code after the "finally" block, + * the actual jump address is going to be set in zend_do_end_finally() + */ + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_JMP; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + CG(context).in_finally++; +} +/* }}} */ + +void zend_do_begin_catch(znode *catch_token, znode *class_name, znode *catch_var, znode *first_catch TSRMLS_DC) /* {{{ */ { long catch_op_number; zend_op *opline; @@ -2749,11 +2860,11 @@ void zend_do_begin_catch(znode *try_token, znode *class_name, znode *catch_var, Z_STRVAL(catch_var->u.constant) = (char*)CG(active_op_array)->vars[opline->op2.var].name; opline->result.num = 0; /* 1 means it's the last catch in the block */ - try_token->u.op.opline_num = catch_op_number; + catch_token->u.op.opline_num = catch_op_number; } /* }}} */ -void zend_do_end_catch(const znode *try_token TSRMLS_DC) /* {{{ */ +void zend_do_end_catch(znode *catch_token TSRMLS_DC) /* {{{ */ { int jmp_op_number = get_next_op_number(CG(active_op_array)); zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -2767,7 +2878,38 @@ void zend_do_end_catch(const znode *try_token TSRMLS_DC) /* {{{ */ zend_stack_top(&CG(bp_stack), (void **) &jmp_list_ptr); zend_llist_add_element(jmp_list_ptr, &jmp_op_number); - CG(active_op_array)->opcodes[try_token->u.op.opline_num].extended_value = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->opcodes[catch_token->u.op.opline_num].extended_value = get_next_op_number(CG(active_op_array)); +} +/* }}} */ + +void zend_do_bind_catch(znode *try_token, znode *catch_token TSRMLS_DC) /* {{{ */ { + if (catch_token->op_type != IS_UNUSED) { + zend_add_catch_element(try_token->u.op.opline_num, catch_token->EA TSRMLS_CC); + } +} +/* }}} */ + +void zend_do_end_finally(znode *try_token, znode* catch_token, znode *finally_token TSRMLS_DC) /* {{{ */ +{ + if (catch_token->op_type == IS_UNUSED && finally_token->op_type == IS_UNUSED) { + zend_error(E_COMPILE_ERROR, "Cannot use try without catch or finally"); + } + if (finally_token->op_type != IS_UNUSED) { + zend_op *opline; + + CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_op = finally_token->u.op.opline_num + 1; + CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_end = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->has_finally_block = 1; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_FAST_RET; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + CG(active_op_array)->opcodes[finally_token->u.op.opline_num].op1.opline_num = get_next_op_number(CG(active_op_array)); + + CG(context).in_finally--; + } } /* }}} */ @@ -5448,12 +5590,16 @@ void zend_do_begin_new_object(znode *new_token, znode *class_type TSRMLS_DC) /* new_token->u.op.opline_num = get_next_op_number(CG(active_op_array)); opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_NEW; + opline->extended_value = CG(context).nested_calls; opline->result_type = IS_VAR; opline->result.var = get_temporary_variable(CG(active_op_array)); SET_NODE(opline->op1, class_type); SET_UNUSED(opline->op2); zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(unsigned char *)); + if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) { + CG(active_op_array)->nested_calls = CG(context).nested_calls; + } } /* }}} */ @@ -5666,6 +5812,13 @@ void zend_do_shell_exec(znode *result, const znode *cmd TSRMLS_DC) /* {{{ */ opline->extended_value = 1; SET_UNUSED(opline->op2); GET_NODE(result, opline->result); + + 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 + 2 > CG(active_op_array)->used_stack) { + CG(active_op_array)->used_stack = CG(context).used_stack + 2; + } } /* }}} */ @@ -6106,7 +6259,16 @@ void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC zend_do_end_variable_parse(variable, BP_VAR_IS, 0 TSRMLS_CC); - zend_check_writable_variable(variable); + if (zend_is_function_or_method_call(variable)) { + if (type == ZEND_ISEMPTY) { + /* empty(func()) can be transformed to !func() */ + zend_do_unary_op(ZEND_BOOL_NOT, result, variable TSRMLS_CC); + } else { + zend_error(E_COMPILE_ERROR, "Cannot use isset() on the result of a function call (you can use \"null !== func()\" instead)"); + } + + return; + } if (variable->op_type == IS_CV) { last_op = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -6253,15 +6415,18 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token opline->extended_value |= ZEND_FE_FETCH_WITH_KEY; } - if ((key->op_type != IS_UNUSED) && (key->EA & ZEND_PARSED_REFERENCE_VARIABLE)) { - zend_error(E_COMPILE_ERROR, "Key element cannot be a reference"); + if ((key->op_type != IS_UNUSED)) { + if (key->EA & ZEND_PARSED_REFERENCE_VARIABLE) { + zend_error(E_COMPILE_ERROR, "Key element cannot be a reference"); + } + if (key->EA & ZEND_PARSED_LIST_EXPR) { + zend_error(E_COMPILE_ERROR, "Cannot use list as key element"); + } } if (value->EA & ZEND_PARSED_REFERENCE_VARIABLE) { assign_by_ref = 1; - if (!(opline-1)->extended_value) { - zend_error(E_COMPILE_ERROR, "Cannot create references to elements of a temporary array expression"); - } + /* Mark extended_value for assign-by-reference */ opline->extended_value |= ZEND_FE_FETCH_BYREF; CG(active_op_array)->opcodes[foreach_token->u.op.opline_num].extended_value |= ZEND_FE_RESET_REFERENCE; @@ -6290,13 +6455,21 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token GET_NODE(&value_node, opline->result); - 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); - } else { - zend_do_assign(&dummy, value, &value_node TSRMLS_CC); + if (value->EA & ZEND_PARSED_LIST_EXPR) { + if (!CG(list_llist).head) { + zend_error(E_COMPILE_ERROR, "Cannot use empty list"); + } + zend_do_list_end(&dummy, &value_node TSRMLS_CC); zend_do_free(&dummy TSRMLS_CC); + } 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); + } else { + zend_do_assign(&dummy, value, &value_node TSRMLS_CC); + zend_do_free(&dummy TSRMLS_CC); + } } if (key->op_type != IS_UNUSED) { @@ -6725,9 +6898,6 @@ again: case T_OPEN_TAG_WITH_ECHO: retval = T_ECHO; break; - case T_END_HEREDOC: - efree(Z_STRVAL(zendlval->u.constant)); - break; } INIT_PZVAL(&zendlval->u.constant); |