diff options
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 540 |
1 files changed, 372 insertions, 168 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 10390b04a8..60b9e3e653 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5,7 +5,7 @@ | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | - | that is bundled with this package in the file LICENSE, and is | + | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | @@ -107,7 +107,7 @@ ZEND_API zend_executor_globals executor_globals; static void zend_duplicate_property_info(zend_property_info *property_info) /* {{{ */ { - if (!IS_INTERNED(property_info->name)) { + if (!IS_INTERNED(property_info->name)) { property_info->name = estrndup(property_info->name, property_info->name_length); } if (property_info->doc_comment) { @@ -118,7 +118,7 @@ static void zend_duplicate_property_info(zend_property_info *property_info) /* { static void zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */ { - if (!IS_INTERNED(property_info->name)) { + if (!IS_INTERNED(property_info->name)) { property_info->name = zend_strndup(property_info->name, property_info->name_length); } } @@ -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)(zend_uintptr_t)EX_TMP_VAR_NUM(0, (op_array->T)++); } /* }}} */ @@ -380,7 +383,7 @@ int zend_add_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC zval c; int lc_literal; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -388,7 +391,7 @@ int zend_add_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC } else { ret = zend_add_literal(op_array, zv TSRMLS_CC); } - + lc_name = zend_str_tolower_dup(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); ZVAL_STRINGL(&c, lc_name, Z_STRLEN_P(zv), 0); lc_literal = zend_add_literal(CG(active_op_array), &c TSRMLS_CC); @@ -407,7 +410,7 @@ int zend_add_ns_func_name_literal(zend_op_array *op_array, const zval *zv TSRMLS zval c; int lc_literal; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -440,7 +443,7 @@ int zend_add_class_name_literal(zend_op_array *op_array, const zval *zv TSRMLS_D zval c; int lc_literal; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -474,7 +477,7 @@ int zend_add_const_name_literal(zend_op_array *op_array, const zval *zv, int unq int name_len, ns_len; zval c; - if (op_array->last_literal > 0 && + if (op_array->last_literal > 0 && &op_array->literals[op_array->last_literal - 1].constant == zv && op_array->literals[op_array->last_literal - 1].cache_slot == -1) { /* we already have function name as last literal (do nothing) */ @@ -483,7 +486,7 @@ int zend_add_const_name_literal(zend_op_array *op_array, const zval *zv, int unq ret = zend_add_literal(op_array, zv TSRMLS_CC); } - /* skip leading '\\' */ + /* skip leading '\\' */ if (Z_STRVAL_P(zv)[0] == '\\') { name_len = Z_STRLEN_P(zv) - 1; name = Z_STRVAL_P(zv) + 1; @@ -813,7 +816,7 @@ void fetch_array_dim(znode *result, const znode *parent, const znode *dim TSRMLS opline.result.var = opline.op1.var; zend_llist_add_element(fetch_list_ptr, &opline); } - + init_op(&opline TSRMLS_CC); opline.opcode = ZEND_FETCH_DIM_W; /* the backpatching routine assumes W */ opline.result_type = IS_VAR; @@ -827,12 +830,12 @@ void fetch_array_dim(znode *result, const znode *parent, const znode *dim TSRMLS 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); + ZVAL_LONG(&CONSTANT(opline.op2.constant), index); } else { CALCULATE_LITERAL_HASH(opline.op2.constant); } } - + GET_NODE(result, opline.result); zend_llist_add_element(fetch_list_ptr, &opline); @@ -938,7 +941,7 @@ void zend_do_assign(znode *result, znode *variable, znode *value TSRMLS_DC) /* { opline->result.var = get_temporary_variable(CG(active_op_array)); opline->op1_type = IS_CONST; LITERAL_STRINGL(opline->op1, - CG(active_op_array)->vars[value->u.op.var].name, + CG(active_op_array)->vars[value->u.op.var].name, CG(active_op_array)->vars[value->u.op.var].name_len, 1); CALCULATE_LITERAL_HASH(opline->op1.constant); SET_UNUSED(opline->op2); @@ -1567,7 +1570,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n if (is_method) { int result; - + lcname = zend_new_interned_string(zend_str_tolower_dup(name, name_len), name_len + 1, 1 TSRMLS_CC); if (IS_INTERNED(lcname)) { @@ -1619,10 +1622,14 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __toString() must have public visibility and cannot be static"); } + } else if ((name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __invoke() must have public visibility and cannot be static"); + } } } else { char *class_lcname; - + class_lcname = do_alloca(CG(active_class_entry)->name_length + 1, use_heap); zend_str_tolower_copy(class_lcname, CG(active_class_entry)->name, CG(active_class_entry)->name_length); /* Improve after RC: cache the lowercase class name */ @@ -1673,8 +1680,12 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } else if ((name_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1))) { if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __toString() must have public visibility and cannot be static"); - } + } CG(active_class_entry)->__tostring = (zend_function *) CG(active_op_array); + } else if ((name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __invoke() must have public visibility and cannot be static"); + } } else if (!(fn_flags & ZEND_ACC_STATIC)) { CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC; } @@ -1724,7 +1735,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } { - /* Push a separator to the switch and foreach stacks */ + /* Push a separator to the switch stack */ zend_switch_entry switch_entry; switch_entry.cond.op_type = IS_UNUSED; @@ -1732,16 +1743,15 @@ 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; - 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)) { @@ -1804,14 +1814,14 @@ void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC) /* if (CG(active_class_entry)) { zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC); } else { - /* we don't care if the function name is longer, in fact lowercasing only + /* we don't care if the function name is longer, in fact lowercasing only * the beginning of the name speeds up the check process */ name_len = strlen(CG(active_op_array)->function_name); zend_str_tolower_copy(lcname, CG(active_op_array)->function_name, MIN(name_len, sizeof(lcname)-1)); lcname[sizeof(lcname)-1] = '\0'; /* zend_str_tolower_copy won't necessarily set the zero byte */ if (name_len == sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)) && CG(active_op_array)->num_args != 1) { zend_error(E_COMPILE_ERROR, "%s() must take exactly 1 argument", ZEND_AUTOLOAD_FUNC_NAME); - } + } } CG(active_op_array)->line_end = zend_get_compiled_lineno(TSRMLS_C); @@ -1937,7 +1947,7 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace internal function with short name */ zend_do_begin_dynamic_function_call(function_name, 1 TSRMLS_CC); return 1; - } + } lcname = zend_str_tolower_dup(function_name->u.constant.value.str.val, function_name->u.constant.value.str.len); if ((zend_hash_find(CG(function_table), lcname, function_name->u.constant.value.str.len+1, (void **) &function)==FAILURE) || @@ -1946,11 +1956,14 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace zend_do_begin_dynamic_function_call(function_name, 0 TSRMLS_CC); efree(lcname); return 1; /* Dynamic */ - } + } efree(function_name->u.constant.value.str.val); 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; } @@ -1979,7 +1992,7 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */ name = CONSTANT(last_op->op2.constant); if (Z_TYPE(name) != IS_STRING) { zend_error(E_COMPILE_ERROR, "Method name must be a string"); - } + } if (!IS_INTERNED(Z_STRVAL(name))) { Z_STRVAL(name) = estrndup(Z_STRVAL(name), Z_STRLEN(name)); } @@ -1989,11 +2002,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; @@ -2005,6 +2020,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); } /* }}} */ @@ -2032,12 +2050,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; @@ -2049,6 +2069,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); } /* }}} */ @@ -2104,6 +2127,53 @@ void zend_resolve_non_class_name(znode *element_name, zend_bool check_namespace } /* }}} */ +void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static TSRMLS_DC) /* {{{ */ +{ + char *lcname; + int lctype; + znode constant_name; + + lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), class_name->u.constant.value.str.len); + lctype = zend_get_class_fetch_type(lcname, strlen(lcname)); + switch (lctype) { + case ZEND_FETCH_CLASS_SELF: + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, "Cannot access self::class when no class scope is active"); + } + zval_dtor(&class_name->u.constant); + class_name->op_type = IS_CONST; + ZVAL_STRINGL(&class_name->u.constant, CG(active_class_entry)->name, CG(active_class_entry)->name_length, 1); + *result = *class_name; + break; + case ZEND_FETCH_CLASS_STATIC: + case ZEND_FETCH_CLASS_PARENT: + if (is_static) { + zend_error(E_COMPILE_ERROR, + "%s::class cannot be used for compile-time class name resolution", + lctype == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); + } + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, + "Cannot access %s::class when no class scope is active", + lctype == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); + } + constant_name.op_type = IS_CONST; + ZVAL_STRINGL(&constant_name.u.constant, "class", sizeof("class")-1, 1); + zend_do_fetch_constant(result, class_name, &constant_name, ZEND_RT, 1 TSRMLS_CC); + break; + case ZEND_FETCH_CLASS_DEFAULT: + zend_resolve_class_name(class_name, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); + *result = *class_name; + break; + } + + efree(lcname); + +} +/* }}} */ + void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_name TSRMLS_DC) /* {{{ */ { char *compound; @@ -2126,7 +2196,7 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant))) { zend_error(E_COMPILE_ERROR, "'\\%s' is an invalid class name", Z_STRVAL(class_name->u.constant)); } - } else { + } else { if (CG(current_import)) { len = compound - Z_STRVAL(class_name->u.constant); lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), len); @@ -2397,6 +2467,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 = @@ -2418,6 +2489,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 */ } @@ -2438,21 +2512,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); } /* }}} */ @@ -2480,8 +2562,8 @@ void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC) /* {{ zend_error(E_COMPILE_ERROR, "Call-time pass-by-reference has been removed"); } return; - } - + } + if (function_ptr) { if (ARG_MAY_BE_SENT_BY_REF(function_ptr, (zend_uint) offset)) { if (param->op_type & (IS_VAR|IS_CV) && original_op != ZEND_SEND_VAL) { @@ -2560,6 +2642,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; + } } /* }}} */ @@ -2576,7 +2662,7 @@ static int generate_free_switch_expr(const zend_switch_entry *switch_entry TSRML opline->opcode = (switch_entry->cond.op_type == IS_TMP_VAR) ? ZEND_FREE : ZEND_SWITCH_FREE; SET_NODE(opline->op1, &switch_entry->cond); SET_UNUSED(opline->op2); - opline->extended_value = 0; + return 0; } /* }}} */ @@ -2586,7 +2672,7 @@ static int generate_free_foreach_copy(const zend_op *foreach_copy TSRMLS_DC) /* zend_op *opline; /* If we reach the separator then stop applying the stack */ - if (foreach_copy->result_type == IS_UNUSED && foreach_copy->op1_type == IS_UNUSED) { + if (foreach_copy->result_type == IS_UNUSED) { return 1; } @@ -2595,16 +2681,6 @@ static int generate_free_foreach_copy(const zend_op *foreach_copy TSRMLS_DC) /* opline->opcode = (foreach_copy->result_type == IS_TMP_VAR) ? ZEND_FREE : ZEND_SWITCH_FREE; COPY_NODE(opline->op1, foreach_copy->result); SET_UNUSED(opline->op2); - opline->extended_value = 1; - - if (foreach_copy->op1_type != IS_UNUSED) { - opline = get_next_op(CG(active_op_array) TSRMLS_CC); - - opline->opcode = (foreach_copy->op1_type == IS_TMP_VAR) ? ZEND_FREE : ZEND_SWITCH_FREE; - COPY_NODE(opline->op1, foreach_copy->op1); - SET_UNUSED(opline->op2); - opline->extended_value = 0; - } return 0; } @@ -2614,9 +2690,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); @@ -2639,9 +2718,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); @@ -2658,12 +2744,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_TMP_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; } /* }}} */ @@ -2680,7 +2813,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); @@ -2697,7 +2830,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)); } /* }}} */ @@ -2723,7 +2856,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; @@ -2751,11 +2906,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); @@ -2769,7 +2924,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--; + } } /* }}} */ @@ -2876,7 +3062,7 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ lc_class_name = zend_str_tolower_dup(ce->name, ce->name_length); if (!zend_hash_exists(&ce->function_table, lc_class_name, ce->name_length+1)) { lc_parent_class_name = zend_str_tolower_dup(ce->parent->name, ce->parent->name_length); - if (!zend_hash_exists(&ce->function_table, lc_parent_class_name, ce->parent->name_length+1) && + if (!zend_hash_exists(&ce->function_table, lc_parent_class_name, ce->parent->name_length+1) && zend_hash_find(&ce->parent->function_table, lc_parent_class_name, ce->parent->name_length+1, (void **)&function)==SUCCESS) { if (function->common.fn_flags & ZEND_ACC_CTOR) { /* inherit parent's constructor */ @@ -3015,8 +3201,8 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c return 0; } } - } - } + } + } if (fe->common.arg_info[i].type_hint != proto->common.arg_info[i].type_hint) { /* Incompatible type hint */ return 0; @@ -3045,7 +3231,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c buf = erealloc(buf, length); \ } -static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */ +static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{{ */ { char *offset, *buf; zend_uint length = 1024; @@ -3062,7 +3248,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ *(offset++) = ':'; *(offset++) = ':'; } - + { size_t name_len = strlen(fptr->common.function_name); REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len); @@ -3103,7 +3289,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ offset += type_name_len; *(offset++) = ' '; } - + if (arg_info->pass_by_reference) { *(offset++) = '&'; } @@ -3205,7 +3391,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{ *offset = '\0'; return buf; -} +} /* }}} */ static void do_inheritance_check_on_method(zend_function *child, zend_function *parent TSRMLS_DC) /* {{{ */ @@ -3217,7 +3403,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * && parent->common.fn_flags & ZEND_ACC_ABSTRACT && parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope) && child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) { - zend_error(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)", + zend_error(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)", parent->common.scope->name, child->common.function_name, child->common.prototype ? child->common.prototype->common.scope->name : child->common.scope->name); @@ -3257,7 +3443,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * } if (parent_flags & ZEND_ACC_PRIVATE) { - child->common.prototype = NULL; + child->common.prototype = NULL; } else if (parent_flags & ZEND_ACC_ABSTRACT) { child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; child->common.prototype = parent; @@ -3268,12 +3454,12 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * if (child->common.prototype && (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { if (!zend_do_perform_implementation_check(child, child->common.prototype TSRMLS_CC)) { - zend_error(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_get_function_declaration(child->common.prototype TSRMLS_CC)); + zend_error(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_get_function_declaration(child->common.prototype TSRMLS_CC)); } } else if (EG(error_reporting) & E_STRICT || EG(user_error_handler)) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */ if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) { char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC); - zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype); + zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype); efree(method_prototype); } } @@ -3292,9 +3478,9 @@ static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_f } return 1; /* method doesn't exist in child, copy from parent */ } - + do_inheritance_check_on_method(child, parent TSRMLS_CC); - + return 0; } /* }}} */ @@ -3325,7 +3511,7 @@ static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_pro zend_error(E_COMPILE_ERROR, "Cannot redeclare %s%s::$%s as %s%s::$%s", (parent_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", parent_ce->name, hash_key->arKey, (child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ce->name, hash_key->arKey); - + } if(parent_info->flags & ZEND_ACC_CHANGED) { @@ -3591,10 +3777,10 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } } ce->interfaces[ce->num_interfaces++] = iface; - + zend_hash_merge_ex(&ce->constants_table, &iface->constants_table, (copy_ctor_func_t) zval_add_ref, sizeof(zval *), (merge_checker_func_t) do_inherit_constant_check, iface); zend_hash_merge_ex(&ce->function_table, &iface->function_table, (copy_ctor_func_t) do_inherit_method, sizeof(zend_function), (merge_checker_func_t) do_inherit_method_check, ce); - + do_implement_interface(ce, iface TSRMLS_CC); zend_do_inherit_interfaces(ce, iface TSRMLS_CC); } @@ -3634,10 +3820,10 @@ static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_ { zend_uint fn_flags = fn->common.scope->ce_flags; zend_uint other_flags = other_fn->common.scope->ce_flags; - + return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC) && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC)) - && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == + && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */ } /* }}} */ @@ -3650,7 +3836,7 @@ static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint if (ce->constructor) { zend_error(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name); } - ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR; + ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR; } else if (!strncmp(mname, ZEND_DESTRUCTOR_FUNC_NAME, mname_len)) { ce->destructor = fe; fe->common.fn_flags |= ZEND_ACC_DTOR; } else if (!strncmp(mname, ZEND_GET_FUNC_NAME, mname_len)) { @@ -3787,7 +3973,7 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, ce = va_arg(args, zend_class_entry*); overriden = va_arg(args, HashTable**); exclude_table = va_arg(args, HashTable*); - + fnname_len = hash_key->nKeyLength - 1; /* apply aliases which are qualified with a class name, there should not be any ambiguity */ @@ -3801,9 +3987,9 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, && alias->trait_method->mname_len == fnname_len && (zend_binary_strcasecmp(alias->trait_method->method_name, alias->trait_method->mname_len, hash_key->arKey, fnname_len) == 0)) { fn_copy = *fn; - + /* if it is 0, no modifieres has been changed */ - if (alias->modifiers) { + if (alias->modifiers) { fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK)); } @@ -3908,7 +4094,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* cur_method_ref->ce->name, cur_method_ref->method_name); } - + /** With the other traits, we are more permissive. We do not give errors for those. This allows to be more defensive in such definitions. @@ -3922,20 +4108,20 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* if (!(cur_precedence->exclude_from_classes[j] = zend_fetch_class(class_name, name_length, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { zend_error(E_COMPILE_ERROR, "Could not find trait %s", class_name); - } + } zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j] TSRMLS_CC); /* make sure that the trait method is not from a class mentioned in exclude_from_classes, for consistency */ if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i]) { zend_error(E_COMPILE_ERROR, - "Inconsistent insteadof definition. " + "Inconsistent insteadof definition. " "The method %s is to be used from %s, but %s is also on the exclude list", cur_method_ref->method_name, cur_precedence->trait_method->ce->name, cur_precedence->trait_method->ce->name); } - + efree(class_name); j++; } @@ -3975,7 +4161,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_trait_precedence **precedences, zend_class_entry *trait) /* {{{ */ { size_t i = 0, j; - + if (!precedences) { return; } @@ -3986,7 +4172,7 @@ static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_tra if (precedences[i]->exclude_from_classes[j] == trait) { zend_uint lcname_len = precedences[i]->trait_method->mname_len; char *lcname = zend_str_tolower_dup(precedences[i]->trait_method->method_name, lcname_len); - + if (zend_hash_add(exclude_table, lcname, lcname_len, NULL, 0, NULL) == FAILURE) { efree(lcname); zend_error(E_COMPILE_ERROR, "Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times", precedences[i]->trait_method->method_name, trait->name); @@ -4023,7 +4209,7 @@ static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ zend_hash_apply_with_arguments(&ce->traits[i]->function_table TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, ce, &overriden, NULL); } } - + zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t)zend_fixup_trait_method, ce TSRMLS_CC); if (overriden) { @@ -4061,7 +4247,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { const char* class_name_unused; zend_bool not_compatible; zval* prop_value; - char* doc_comment; + char* doc_comment; zend_uint flags; /* In the following steps the properties are inserted into the property table @@ -4083,18 +4269,17 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { prop_name_length = property_info->name_length; } else { /* for private and protected we need to unmangle the names */ - zend_unmangle_property_name(property_info->name, property_info->name_length, - &class_name_unused, &prop_name); - prop_name_length = strlen(prop_name); + zend_unmangle_property_name_ex(property_info->name, property_info->name_length, + &class_name_unused, &prop_name, &prop_name_length); prop_hash = zend_get_hash_value(prop_name, prop_name_length + 1); } /* next: check for conflicts with current class */ if (zend_hash_quick_find(&ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) { - if (coliding_prop->flags & ZEND_ACC_SHADOW) { + if (coliding_prop->flags & ZEND_ACC_SHADOW) { zend_hash_quick_del(&ce->properties_info, prop_name, prop_name_length+1, prop_hash); flags |= ZEND_ACC_CHANGED; - } else { + } else { if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC)) == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) { /* flags are identical, now the value needs to be checked */ @@ -4115,14 +4300,14 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { } if (not_compatible) { - zend_error(E_COMPILE_ERROR, + zend_error(E_COMPILE_ERROR, "%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, property_info->ce->name, prop_name, ce->name); } else { - zend_error(E_STRICT, + zend_error(E_STRICT, "%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed", find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, property_info->ce->name, @@ -4142,8 +4327,8 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* { Z_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? estrndup(property_info->doc_comment, property_info->doc_comment_len) : NULL; - zend_declare_property_ex(ce, prop_name, prop_name_length, - prop_value, flags, + zend_declare_property_ex(ce, prop_name, prop_name_length, + prop_value, flags, doc_comment, property_info->doc_comment_len TSRMLS_CC); } } @@ -4155,7 +4340,7 @@ static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce int i = 0; zend_trait_alias* cur_alias; char* lc_method_name; - + if (ce->trait_aliases) { while (ce->trait_aliases[i]) { cur_alias = ce->trait_aliases[i]; @@ -4176,7 +4361,7 @@ static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce we check against it and abort. 2) it is just a plain old inconsitency/typo/bug as in the case where alias is set. */ - + lc_method_name = zend_str_tolower_dup(cur_alias->trait_method->method_name, cur_alias->trait_method->mname_len); if (zend_hash_exists(&ce->function_table, @@ -4213,7 +4398,7 @@ ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */ /* first care about all methods to be flattened into the class */ zend_do_traits_method_binding(ce TSRMLS_CC); - + /* Aliases which have not been applied indicate typos/bugs. */ zend_do_check_for_inconsistent_traits_aliasing(ce TSRMLS_CC); @@ -4222,7 +4407,7 @@ ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */ /* verify that all abstract methods from traits have been implemented */ zend_verify_abstract_class(ce TSRMLS_CC); - + /* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */ if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { ce->ce_flags -= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; @@ -4272,7 +4457,7 @@ void zend_prepare_reference(znode *result, znode *class_name, znode *method_name zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference)); method_ref->ce = NULL; - /* REM: There should not be a need for copying, + /* REM: There should not be a need for copying, zend_do_begin_class_declaration is also just using that string */ if (class_name) { zend_resolve_class_name(class_name, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); @@ -4316,8 +4501,6 @@ void zend_add_trait_alias(znode *method_reference, znode *modifiers, znode *alia } else { trait_alias->alias = NULL; } - trait_alias->function = NULL; - zend_add_to_list(&ce->trait_aliases, trait_alias TSRMLS_CC); } /* }}} */ @@ -4330,8 +4513,6 @@ void zend_add_trait_precedence(znode *method_reference, znode *trait_list TSRMLS trait_precedence->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr; trait_precedence->exclude_from_classes = (zend_class_entry**) trait_list->u.op.ptr; - trait_precedence->function = NULL; - zend_add_to_list(&ce->trait_precedences, trait_precedence TSRMLS_CC); } /* }}} */ @@ -4850,7 +5031,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name build_runtime_defined_function_key(&key, lcname, new_class_entry->name_length TSRMLS_CC); opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC); Z_HASH_P(&CONSTANT(opline->op1.constant)) = zend_hash_func(Z_STRVAL(CONSTANT(opline->op1.constant)), Z_STRLEN(CONSTANT(opline->op1.constant))); - + opline->op2_type = IS_CONST; if (doing_inheritance) { @@ -4867,7 +5048,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name LITERAL_STRINGL(opline->op2, lcname, new_class_entry->name_length, 0); CALCULATE_LITERAL_HASH(opline->op2.constant); - + zend_hash_quick_update(CG(class_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &new_class_entry, sizeof(zend_class_entry *), NULL); CG(active_class_entry) = new_class_entry; @@ -4918,7 +5099,7 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent } ce->info.user.line_end = zend_get_compiled_lineno(TSRMLS_C); - + /* Check for traits and proceed like with interfaces. * The only difference will be a combined handling of them in the end. * Thus, we need another opcode here. */ @@ -4943,7 +5124,7 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent } } /* Inherit interfaces; reset number to zero, we need it for above check and - * will restore it during actual implementation. + * will restore it during actual implementation. * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to * zend_verify_abstract_class() */ if (ce->num_interfaces > 0) { @@ -5044,7 +5225,7 @@ static int zend_strnlen(const char* s, int maxlen) /* {{{ */ } /* }}} */ -ZEND_API int zend_unmangle_property_name(const char *mangled_property, int len, const char **class_name, const char **prop_name) /* {{{ */ +ZEND_API int zend_unmangle_property_name_ex(const char *mangled_property, int len, const char **class_name, const char **prop_name, int *prop_len) /* {{{ */ { int class_name_len; @@ -5052,22 +5233,34 @@ ZEND_API int zend_unmangle_property_name(const char *mangled_property, int len, if (mangled_property[0]!=0) { *prop_name = mangled_property; + if (prop_len) { + *prop_len = len; + } return SUCCESS; } if (len < 3 || mangled_property[1]==0) { zend_error(E_NOTICE, "Illegal member variable name"); *prop_name = mangled_property; + if (prop_len) { + *prop_len = len; + } return FAILURE; } - class_name_len = zend_strnlen(mangled_property+1, --len - 1) + 1; + class_name_len = zend_strnlen(mangled_property + 1, --len - 1) + 1; if (class_name_len >= len || mangled_property[class_name_len]!=0) { zend_error(E_NOTICE, "Corrupt member variable name"); *prop_name = mangled_property; + if (prop_len) { + *prop_len = len + 1; + } return FAILURE; } - *class_name = mangled_property+1; - *prop_name = (*class_name)+class_name_len; + *class_name = mangled_property + 1; + *prop_name = (*class_name) + class_name_len; + if (prop_len) { + *prop_len = len - class_name_len; + } return SUCCESS; } /* }}} */ @@ -5133,7 +5326,7 @@ void zend_do_declare_class_constant(znode *var_name, const znode *value TSRMLS_D ALLOC_ZVAL(property); *property = value->u.constant; - + cname = zend_new_interned_string(var_name->u.constant.value.str.val, var_name->u.constant.value.str.len+1, 0 TSRMLS_CC); if (IS_INTERNED(cname)) { @@ -5146,7 +5339,7 @@ void zend_do_declare_class_constant(znode *var_name, const znode *value TSRMLS_D zend_error(E_COMPILE_ERROR, "Cannot redefine class constant %s::%s", CG(active_class_entry)->name, var_name->u.constant.value.str.val); } FREE_PNODE(var_name); - + if (CG(doc_comment)) { efree(CG(doc_comment)); CG(doc_comment) = NULL; @@ -5235,17 +5428,17 @@ void zend_do_halt_compiler_register(TSRMLS_D) /* {{{ */ char *name, *cfilename; char haltoff[] = "__COMPILER_HALT_OFFSET__"; int len, clen; - + if (CG(has_bracketed_namespaces) && CG(in_namespace)) { zend_error(E_COMPILE_ERROR, "__HALT_COMPILER() can only be used from the outermost scope"); } - + cfilename = zend_get_compiled_filename(TSRMLS_C); clen = strlen(cfilename); zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, cfilename, clen, 0); zend_register_long_constant(name, len+1, zend_get_scanned_file_offset(TSRMLS_C), CONST_CS, 0 TSRMLS_CC); pefree(name, 0); - + if (CG(in_namespace)) { zend_do_end_namespace(TSRMLS_C); } @@ -5278,12 +5471,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; + } } /* }}} */ @@ -5318,7 +5515,7 @@ static zend_constant* zend_get_ct_const(const zval *const_name, int all_internal } } else if (zend_hash_find(EG(zend_constants), Z_STRVAL_P(const_name), Z_STRLEN_P(const_name)+1, (void **) &c) == FAILURE) { char *lookup_name = zend_str_tolower_dup(Z_STRVAL_P(const_name), Z_STRLEN_P(const_name)); - + if (zend_hash_find(EG(zend_constants), lookup_name, Z_STRLEN_P(const_name)+1, (void **) &c)==SUCCESS) { if ((c->flags & CONST_CT_SUBST) && !(c->flags & CONST_CS)) { efree(lookup_name); @@ -5371,7 +5568,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con case ZEND_CT: /* this is a class constant */ type = zend_get_class_fetch_type(Z_STRVAL(constant_container->u.constant), Z_STRLEN(constant_container->u.constant)); - + if (ZEND_FETCH_CLASS_STATIC == type) { zend_error(E_ERROR, "\"static::\" is not allowed in compile-time constants"); } else if (ZEND_FETCH_CLASS_DEFAULT == type) { @@ -5435,7 +5632,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con compound = memchr(Z_STRVAL(constant_name->u.constant), '\\', Z_STRLEN(constant_name->u.constant)); zend_resolve_non_class_name(constant_name, check_namespace TSRMLS_CC); - + if(zend_constant_ct_subst(result, &constant_name->u.constant, 1 TSRMLS_CC)) { break; } @@ -5451,7 +5648,7 @@ void zend_do_fetch_constant(znode *result, znode *constant_container, znode *con /* the name is unambiguous */ opline->extended_value = 0; opline->op2.constant = zend_add_const_name_literal(CG(active_op_array), &constant_name->u.constant, 0 TSRMLS_CC); - } else { + } else { opline->extended_value = IS_CONSTANT_UNQUALIFIED; if (CG(current_namespace)) { opline->extended_value |= IS_CONSTANT_IN_NAMESPACE; @@ -5495,7 +5692,15 @@ void zend_do_shell_exec(znode *result, const znode *cmd TSRMLS_DC) /* {{{ */ GET_CACHE_SLOT(opline->op1.constant); opline->extended_value = 1; SET_UNUSED(opline->op2); + opline->op2.num = CG(context).nested_calls; 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; + } } /* }}} */ @@ -5518,7 +5723,7 @@ void zend_do_init_array(znode *result, const znode *expr, const znode *offset, z 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); + ZVAL_LONG(&CONSTANT(opline->op2.constant), index); } else { CALCULATE_LITERAL_HASH(opline->op2.constant); } @@ -5550,7 +5755,7 @@ void zend_do_add_array_element(znode *result, const znode *expr, const znode *of 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); + ZVAL_LONG(&CONSTANT(opline->op2.constant), index); } else { CALCULATE_LITERAL_HASH(opline->op2.constant); } @@ -5810,7 +6015,7 @@ void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC) Z_TYPE(value.u.constant) |= is_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR; Z_SET_REFCOUNT_P(&value.u.constant, 1); Z_UNSET_ISREF_P(&value.u.constant); - + zend_do_fetch_static_variable(varname, &value, is_ref ? ZEND_FETCH_STATIC : ZEND_FETCH_LEXICAL TSRMLS_CC); } /* }}} */ @@ -5940,7 +6145,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); @@ -6003,7 +6217,6 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno { zend_op *opline; zend_bool is_variable; - zend_bool push_container = 0; zend_op dummy_opline; if (variable) { @@ -6015,14 +6228,6 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno /* save the location of FETCH_W instruction(s) */ open_brackets_token->u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_end_variable_parse(array, BP_VAR_W, 0 TSRMLS_CC); - if (CG(active_op_array)->last > 0 && - CG(active_op_array)->opcodes[CG(active_op_array)->last-1].opcode == ZEND_FETCH_OBJ_W) { - /* Only lock the container if we are fetching from a real container and not $this */ - if (CG(active_op_array)->opcodes[CG(active_op_array)->last-1].op1_type == IS_VAR) { - CG(active_op_array)->opcodes[CG(active_op_array)->last-1].extended_value |= ZEND_FETCH_ADD_LOCK; - push_container = 1; - } - } } else { is_variable = 0; open_brackets_token->u.op.opline_num = get_next_op_number(CG(active_op_array)); @@ -6042,11 +6247,6 @@ void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, zno opline->extended_value = is_variable ? ZEND_FE_RESET_VARIABLE : 0; COPY_NODE(dummy_opline.result, opline->result); - if (push_container) { - COPY_NODE(dummy_opline.op1, CG(active_op_array)->opcodes[CG(active_op_array)->last-2].op1); - } else { - dummy_opline.op1_type = IS_UNUSED; - } zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op)); /* save the location of FE_FETCH */ @@ -6087,20 +6287,22 @@ 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; } else { - zend_op *foreach_copy; zend_op *fetch = &CG(active_op_array)->opcodes[foreach_token->u.op.opline_num]; zend_op *end = &CG(active_op_array)->opcodes[open_brackets_token->u.op.opline_num]; @@ -6117,20 +6319,25 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token fetch->opcode -= 3; /* FETCH_W -> FETCH_R */ } } - /* prevent double SWITCH_FREE */ - zend_stack_top(&CG(foreach_copy_stack), (void **) &foreach_copy); - foreach_copy->op1_type = IS_UNUSED; } 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) { @@ -6306,7 +6513,7 @@ void zend_do_jmp_set(const znode *value, znode *jmp_token, znode *colon_token TS opline->result.var = get_temporary_variable(CG(active_op_array)); SET_NODE(opline->op1, value); SET_UNUSED(opline->op2); - + GET_NODE(colon_token, opline->result); jmp_token->u.op.opline_num = op_number; @@ -6335,11 +6542,11 @@ void zend_do_jmp_set_else(znode *result, const znode *false_value, const znode * opline->extended_value = 0; SET_NODE(opline->op1, false_value); SET_UNUSED(opline->op2); - + GET_NODE(result, opline->result); CG(active_op_array)->opcodes[jmp_token->u.op.opline_num].op2.opline_num = get_next_op_number(CG(active_op_array)); - + DEC_BPC(CG(active_op_array)); } /* }}} */ @@ -6552,16 +6759,13 @@ again: CG(increment_lineno) = 1; } if (CG(has_bracketed_namespaces) && !CG(in_namespace)) { - goto again; + goto again; } retval = ';'; /* implicit ; */ break; 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); @@ -6645,13 +6849,13 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify int zend_get_class_fetch_type(const char *class_name, uint class_name_len) /* {{{ */ { if ((class_name_len == sizeof("self")-1) && - !memcmp(class_name, "self", sizeof("self")-1)) { - return ZEND_FETCH_CLASS_SELF; + !strncasecmp(class_name, "self", sizeof("self")-1)) { + return ZEND_FETCH_CLASS_SELF; } else if ((class_name_len == sizeof("parent")-1) && - !memcmp(class_name, "parent", sizeof("parent")-1)) { + !strncasecmp(class_name, "parent", sizeof("parent")-1)) { return ZEND_FETCH_CLASS_PARENT; } else if ((class_name_len == sizeof("static")-1) && - !memcmp(class_name, "static", sizeof("static")-1)) { + !strncasecmp(class_name, "static", sizeof("static")-1)) { return ZEND_FETCH_CLASS_STATIC; } else { return ZEND_FETCH_CLASS_DEFAULT; @@ -6764,7 +6968,7 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC efree(CG(current_import)); CG(current_import) = NULL; } - + if (CG(doc_comment)) { efree(CG(doc_comment)); CG(doc_comment) = NULL; @@ -6936,7 +7140,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) len_adjust += 2; if (2 == len) { /* Return "c:" on Win32 for dirname("c:"). - * It would be more consistent to return "c:." + * It would be more consistent to return "c:." * but that would require making the string *longer*. */ return len; |