diff options
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 2020 |
1 files changed, 1404 insertions, 616 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5a15392fe9..2b935a8f4d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2015 Zend Technologies Ltd. (http://www.zend.com) | + | Copyright (c) 1998-2016 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 | @@ -32,6 +32,7 @@ #include "zend_multibyte.h" #include "zend_language_scanner.h" #include "zend_inheritance.h" +#include "zend_vm.h" #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -53,6 +54,16 @@ #define FC(member) (CG(file_context).member) +typedef struct _zend_loop_var { + zend_uchar opcode; + zend_uchar var_type; + uint32_t var_num; + union { + uint32_t try_catch_offset; + uint32_t live_range_offset; + } u; +} zend_loop_var; + static inline void zend_alloc_cache_slot(uint32_t literal) { zend_op_array *op_array = CG(active_op_array); Z_CACHE_SLOT(op_array->literals[literal]) = op_array->cache_size; @@ -75,6 +86,8 @@ ZEND_API zend_compiler_globals compiler_globals; ZEND_API zend_executor_globals executor_globals; #endif +static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2); + static void zend_destroy_property_info_internal(zval *zv) /* {{{ */ { zend_property_info *property_info = Z_PTR_P(zv); @@ -84,6 +97,12 @@ static void zend_destroy_property_info_internal(zval *zv) /* {{{ */ } /* }}} */ +static void zend_destroy_class_constant_internal(zval *zv) /* {{{ */ +{ + free(Z_PTR_P(zv)); +} +/* }}} */ + static zend_string *zend_new_interned_string_safe(zend_string *str) /* {{{ */ { zend_string *interned_str; @@ -140,6 +159,7 @@ static const struct reserved_class_name reserved_class_names[] = { {ZEND_STRL("static")}, {ZEND_STRL("string")}, {ZEND_STRL("true")}, + {ZEND_STRL("void")}, {NULL, 0} }; @@ -179,10 +199,11 @@ typedef struct _builtin_type_info { } builtin_type_info; static const builtin_type_info builtin_types[] = { - {"int", sizeof("int") - 1, IS_LONG}, - {"float", sizeof("float") - 1, IS_DOUBLE}, - {"string", sizeof("string") - 1, IS_STRING}, - {"bool", sizeof("bool") - 1, _IS_BOOL}, + {ZEND_STRL("int"), IS_LONG}, + {ZEND_STRL("float"), IS_DOUBLE}, + {ZEND_STRL("string"), IS_STRING}, + {ZEND_STRL("bool"), _IS_BOOL}, + {ZEND_STRL("void"), IS_VOID}, {NULL, 0, IS_UNDEF} }; @@ -210,16 +231,23 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */ CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE; CG(context).vars_size = 0; CG(context).literals_size = 0; - CG(context).current_brk_cont = -1; CG(context).backpatch_count = 0; CG(context).in_finally = 0; CG(context).fast_call_var = -1; + CG(context).try_catch_offset = -1; + CG(context).current_brk_cont = -1; + CG(context).last_brk_cont = 0; + CG(context).brk_cont_array = NULL; CG(context).labels = NULL; } /* }}} */ void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */ { + if (CG(context).brk_cont_array) { + efree(CG(context).brk_cont_array); + CG(context).brk_cont_array = NULL; + } if (CG(context).labels) { zend_hash_destroy(CG(context).labels); FREE_HASHTABLE(CG(context).labels); @@ -283,7 +311,7 @@ void zend_file_context_end(zend_file_context *prev_context) /* {{{ */ void zend_init_compiler_data_structures(void) /* {{{ */ { - zend_stack_init(&CG(loop_var_stack), sizeof(znode)); + zend_stack_init(&CG(loop_var_stack), sizeof(zend_loop_var)); zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op)); CG(active_class_entry) = NULL; CG(in_compilation) = 0; @@ -308,7 +336,7 @@ void init_compiler(void) /* {{{ */ memset(&CG(context), 0, sizeof(CG(context))); zend_init_compiler_data_structures(); zend_init_rsrc_list(); - zend_hash_init(&CG(filenames_table), 8, NULL, free_string_zval, 0); + zend_hash_init(&CG(filenames_table), 8, NULL, ZVAL_PTR_DTOR, 0); zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) file_handle_dtor, 0); CG(unclean_shutdown) = 0; } @@ -326,17 +354,19 @@ void shutdown_compiler(void) /* {{{ */ ZEND_API zend_string *zend_set_compiled_filename(zend_string *new_compiled_filename) /* {{{ */ { - zend_string *p; + zval *p, rv; - p = zend_hash_find_ptr(&CG(filenames_table), new_compiled_filename); - if (p != NULL) { - CG(compiled_filename) = p; - return p; + if ((p = zend_hash_find(&CG(filenames_table), new_compiled_filename))) { + ZEND_ASSERT(Z_TYPE_P(p) == IS_STRING); + CG(compiled_filename) = Z_STR_P(p); + return Z_STR_P(p); } - p = zend_string_copy(new_compiled_filename); - zend_hash_update_ptr(&CG(filenames_table), new_compiled_filename, p); - CG(compiled_filename) = p; - return p; + + ZVAL_STR_COPY(&rv, new_compiled_filename); + zend_hash_update(&CG(filenames_table), new_compiled_filename, &rv); + + CG(compiled_filename) = new_compiled_filename; + return new_compiled_filename; } /* }}} */ @@ -557,90 +587,190 @@ void zend_stop_lexing(void) LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit); } -static inline void zend_begin_loop(const znode *loop_var) /* {{{ */ +static uint32_t zend_start_live_range(zend_op_array *op_array, uint32_t start) /* {{{ */ +{ + zend_live_range *range; + + op_array->last_live_range++; + op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range); + range = op_array->live_range + op_array->last_live_range - 1; + range->start = start; + return op_array->last_live_range - 1; +} +/* }}} */ + +static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start) /* {{{ */ +{ + if (op_array->last_live_range == 0 || + op_array->live_range[op_array->last_live_range - 1].start <= start) { + return zend_start_live_range(op_array, start); + } else { + /* Live ranges have to be sorted by "start" field */ + uint32_t n = op_array->last_live_range; + + /* move early ranges to make a room */ + op_array->last_live_range = n + 1; + op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range); + do { + op_array->live_range[n] = op_array->live_range[n-1]; + n--; + } while (n != 0 && op_array->live_range[n-1].start > start); + + /* initialize new range */ + op_array->live_range[n].start = start; + + /* update referens to live-ranges from stack */ + if (!zend_stack_is_empty(&CG(loop_var_stack))) { + zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack)); + zend_loop_var *base = zend_stack_base(&CG(loop_var_stack)); + int check_opcodes = 0; + + for (; loop_var >= base; loop_var--) { + if (loop_var->opcode == ZEND_RETURN) { + /* Stack separator */ + break; + } else if (loop_var->opcode == ZEND_FREE || + loop_var->opcode == ZEND_FE_FREE) { + if (loop_var->u.live_range_offset >= n) { + loop_var->u.live_range_offset++; + check_opcodes = 1; + } else { + break; + } + } + } + + /* update previously generated FREE/FE_FREE opcodes */ + if (check_opcodes) { + zend_op *opline = op_array->opcodes + op_array->live_range[n+1].start; + zend_op *end = op_array->opcodes + op_array->last; + + while (opline < end) { + if ((opline->opcode == ZEND_FREE || + opline->opcode == ZEND_FE_FREE) && + (opline->extended_value & ZEND_FREE_ON_RETURN) && + opline->op2.num >= n) { + opline->op2.num++; + } + opline++; + } + } + } + return n; + } +} +/* }}} */ + +static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32_t end, uint32_t kind, uint32_t var) /* {{{ */ +{ + zend_live_range *range = op_array->live_range + offset; + + if (range->start == end && offset == op_array->last_live_range - 1) { + op_array->last_live_range--; + } else { + range->end = end; + range->var = (var * sizeof(zval)) | kind; + } +} +/* }}} */ + +static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */ { zend_brk_cont_element *brk_cont_element; int parent = CG(context).current_brk_cont; + zend_loop_var info = {0}; - CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont; - brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); + CG(context).current_brk_cont = CG(context).last_brk_cont; + brk_cont_element = get_next_brk_cont_element(); brk_cont_element->parent = parent; - if (loop_var) { - zend_stack_push(&CG(loop_var_stack), loop_var); - brk_cont_element->start = get_next_op_number(CG(active_op_array)); + if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) { + uint32_t start = get_next_op_number(CG(active_op_array)); + + info.opcode = free_opcode; + info.var_type = loop_var->op_type; + info.var_num = loop_var->u.op.var; + info.u.live_range_offset = zend_start_live_range(CG(active_op_array), start); + brk_cont_element->start = start; } else { + info.opcode = ZEND_NOP; /* The start field is used to free temporary variables in case of exceptions. * We won't try to free something of we don't have loop variable. */ brk_cont_element->start = -1; } + + zend_stack_push(&CG(loop_var_stack), &info); } /* }}} */ -static inline void zend_end_loop(int cont_addr) /* {{{ */ +static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */ { + uint32_t end = get_next_op_number(CG(active_op_array)); zend_brk_cont_element *brk_cont_element - = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont]; + = &CG(context).brk_cont_array[CG(context).current_brk_cont]; brk_cont_element->cont = cont_addr; - brk_cont_element->brk = get_next_op_number(CG(active_op_array)); + brk_cont_element->brk = end; CG(context).current_brk_cont = brk_cont_element->parent; - if (brk_cont_element->start >= 0) { - zend_stack_del_top(&CG(loop_var_stack)); + if (brk_cont_element->start != -1) { + zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack)); + zend_end_live_range(CG(active_op_array), loop_var->u.live_range_offset, end, + loop_var->opcode == ZEND_FE_FREE ? ZEND_LIVE_LOOP : ZEND_LIVE_TMPVAR, + var_node->u.op.var); } + + zend_stack_del_top(&CG(loop_var_stack)); } /* }}} */ void zend_do_free(znode *op1) /* {{{ */ { - if (op1->op_type==IS_TMP_VAR) { - zend_op *opline = get_next_op(CG(active_op_array)); - - opline->opcode = ZEND_FREE; - SET_NODE(opline->op1, op1); - SET_UNUSED(opline->op2); - } else if (op1->op_type==IS_VAR) { + if (op1->op_type == IS_TMP_VAR) { zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; - while (opline->opcode == ZEND_END_SILENCE || opline->opcode == ZEND_EXT_FCALL_END || opline->opcode == ZEND_OP_DATA) { + while (opline->opcode == ZEND_END_SILENCE) { + opline--; + } + + if (opline->result_type == IS_TMP_VAR && opline->result.var == op1->u.op.var) { + if (opline->opcode == ZEND_BOOL || opline->opcode == ZEND_BOOL_NOT) { + return; + } + } + + zend_emit_op(NULL, ZEND_FREE, op1, NULL); + } else if (op1->op_type == IS_VAR) { + zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; + while (opline->opcode == ZEND_END_SILENCE || + opline->opcode == ZEND_EXT_FCALL_END || + opline->opcode == ZEND_OP_DATA) { opline--; } if (opline->result_type == IS_VAR && opline->result.var == op1->u.op.var) { if (opline->opcode == ZEND_FETCH_R || opline->opcode == ZEND_FETCH_DIM_R || - opline->opcode == ZEND_FETCH_OBJ_R) { + opline->opcode == ZEND_FETCH_OBJ_R || + opline->opcode == ZEND_FETCH_STATIC_PROP_R) { /* It's very rare and useless case. It's better to use additional FREE opcode and simplify the FETCH handlers their selves */ - opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_FREE; - SET_NODE(opline->op1, op1); - SET_UNUSED(opline->op2); + zend_emit_op(NULL, ZEND_FREE, op1, NULL); } else { - opline->result_type |= EXT_TYPE_UNUSED; + opline->result_type = IS_UNUSED; } } else { while (opline >= CG(active_op_array)->opcodes) { if (opline->opcode == ZEND_FETCH_LIST && opline->op1_type == IS_VAR && opline->op1.var == op1->u.op.var) { - opline = get_next_op(CG(active_op_array)); - - opline->opcode = ZEND_FREE; - SET_NODE(opline->op1, op1); - SET_UNUSED(opline->op2); + zend_emit_op(NULL, ZEND_FREE, op1, NULL); return; } - if (opline->result_type==IS_VAR + if (opline->result_type == IS_VAR && opline->result.var == op1->u.op.var) { if (opline->opcode == ZEND_NEW) { - opline->result_type |= EXT_TYPE_UNUSED; - opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; - while (opline->opcode != ZEND_DO_FCALL || opline->op1.num != ZEND_CALL_CTOR) { - opline--; - } - opline->op1.num |= ZEND_CALL_CTOR_RESULT_UNUSED; + zend_emit_op(NULL, ZEND_FREE, op1, NULL); } break; } @@ -742,6 +872,7 @@ zend_string *zend_resolve_non_class_name( if (ZSTR_VAL(name)[0] == '\\') { /* Remove \ prefix (only relevant if this is a string rather than a label) */ + *is_fully_qualified = 1; return zend_string_init(ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1, 0); } @@ -863,9 +994,9 @@ zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ } /* }}} */ -static void ptr_dtor(zval *zv) /* {{{ */ +static void label_ptr_dtor(zval *zv) /* {{{ */ { - efree(Z_PTR_P(zv)); + efree_size(Z_PTR_P(zv), sizeof(zend_label)); } /* }}} */ @@ -874,84 +1005,8 @@ static void str_dtor(zval *zv) /* {{{ */ { } /* }}} */ -void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */ -{ - zend_label *dest; - int current, distance; - zval *label; - - if (pass2) { - label = RT_CONSTANT(op_array, opline->op2); - } else { - label = CT_CONSTANT_EX(op_array, opline->op2.constant); - } - if (CG(context).labels == NULL || - (dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) { - - if (pass2) { - CG(in_compilation) = 1; - CG(active_op_array) = op_array; - CG(zend_lineno) = opline->lineno; - zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label)); - } else { - /* Label is not defined. Delay to pass 2. */ - return; - } - } - - opline->op1.opline_num = dest->opline_num; - zval_dtor(label); - ZVAL_NULL(label); - - /* Check that we are not moving into loop or switch */ - current = opline->extended_value; - for (distance = 0; current != dest->brk_cont; distance++) { - if (current == -1) { - if (pass2) { - CG(in_compilation) = 1; - CG(active_op_array) = op_array; - CG(zend_lineno) = opline->lineno; - } - zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed"); - } - current = op_array->brk_cont_array[current].parent; - } - - if (distance == 0) { - /* Nothing to break out of, optimize to ZEND_JMP */ - opline->opcode = ZEND_JMP; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - } else { - /* Set real break distance */ - ZVAL_LONG(label, distance); - } -} -/* }}} */ - static zend_bool zend_is_call(zend_ast *ast); -static int generate_free_loop_var(znode *var) /* {{{ */ -{ - switch (var->op_type) { - case IS_UNUSED: - /* Stack separator on function boundary, stop applying */ - return 1; - case IS_VAR: - case IS_TMP_VAR: - { - zend_op *opline = get_next_op(CG(active_op_array)); - - opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE; - SET_NODE(opline->op1, var); - SET_UNUSED(opline->op2); - } - } - - return 0; -} -/* }}} */ - static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */ { zend_op_array *op_array = CG(active_op_array); @@ -996,24 +1051,24 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */ { zend_function *function, *new_function; - zval *op1, *op2; + zval *lcname, *rtd_key; if (compile_time) { - op1 = CT_CONSTANT_EX(op_array, opline->op1.constant); - op2 = CT_CONSTANT_EX(op_array, opline->op2.constant); + lcname = CT_CONSTANT_EX(op_array, opline->op1.constant); + rtd_key = lcname + 1; } else { - op1 = RT_CONSTANT(op_array, opline->op1); - op2 = RT_CONSTANT(op_array, opline->op2); + lcname = RT_CONSTANT(op_array, opline->op1); + rtd_key = lcname + 1; } - function = zend_hash_find_ptr(function_table, Z_STR_P(op1)); + function = zend_hash_find_ptr(function_table, Z_STR_P(rtd_key)); new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_function, function, sizeof(zend_op_array)); - if (zend_hash_add_ptr(function_table, Z_STR_P(op2), new_function) == NULL) { + if (zend_hash_add_ptr(function_table, Z_STR_P(lcname), new_function) == NULL) { int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR; zend_function *old_function; - if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(op2))) != NULL + if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(lcname))) != NULL && old_function->type == ZEND_USER_FUNCTION && old_function->op_array.last > 0) { zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)", @@ -1037,21 +1092,21 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opli ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time) /* {{{ */ { zend_class_entry *ce; - zval *op1, *op2; + zval *lcname, *rtd_key; if (compile_time) { - op1 = CT_CONSTANT_EX(op_array, opline->op1.constant); - op2 = CT_CONSTANT_EX(op_array, opline->op2.constant); + lcname = CT_CONSTANT_EX(op_array, opline->op1.constant); + rtd_key = lcname + 1; } else { - op1 = RT_CONSTANT(op_array, opline->op1); - op2 = RT_CONSTANT(op_array, opline->op2); + lcname = RT_CONSTANT(op_array, opline->op1); + rtd_key = lcname + 1; } - if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(op1))) == NULL) { - zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(op1)); + if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key))) == NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(rtd_key)); return NULL; } ce->refcount++; - if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) { + if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) { ce->refcount--; if (!compile_time) { /* If we're in compile time, in practice, it's quite possible @@ -1074,17 +1129,17 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time) /* {{{ */ { zend_class_entry *ce; - zval *op1, *op2; + zval *lcname, *rtd_key; if (compile_time) { - op1 = CT_CONSTANT_EX(op_array, opline->op1.constant); - op2 = CT_CONSTANT_EX(op_array, opline->op2.constant); + lcname = CT_CONSTANT_EX(op_array, opline->op1.constant); + rtd_key = lcname + 1; } else { - op1 = RT_CONSTANT(op_array, opline->op1); - op2 = RT_CONSTANT(op_array, opline->op2); + lcname = RT_CONSTANT(op_array, opline->op1); + rtd_key = lcname + 1; } - ce = zend_hash_find_ptr(class_table, Z_STR_P(op1)); + ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key)); if (!ce) { if (!compile_time) { @@ -1093,12 +1148,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array * so we shut up about it. This allows the if (!defined('FOO')) { return; } * approach to work. */ - zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(op2)), Z_STRVAL_P(op2)); + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname)), Z_STRVAL_P(lcname)); } return NULL; } - if (zend_hash_exists(class_table, Z_STR_P(op2))) { + if (zend_hash_exists(class_table, Z_STR_P(lcname))) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); } @@ -1107,7 +1162,7 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array ce->refcount++; /* Register the derived class */ - if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) { + if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); } return ce; @@ -1181,13 +1236,39 @@ void zend_do_early_binding(void) /* {{{ */ return; } - zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1))); + zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)+1)); + zend_del_literal(CG(active_op_array), opline->op1.constant+1); zend_del_literal(CG(active_op_array), opline->op1.constant); - zend_del_literal(CG(active_op_array), opline->op2.constant); MAKE_NOP(opline); } /* }}} */ +static void zend_mark_function_as_generator() /* {{{ */ +{ + if (!CG(active_op_array)->function_name) { + zend_error_noreturn(E_COMPILE_ERROR, + "The \"yield\" expression can only be used inside a function"); + } + + if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + const char *msg = "Generators may only declare a return type of Generator, Iterator or Traversable, %s is not permitted"; + zend_arg_info return_info = CG(active_op_array)->arg_info[-1]; + + if (!return_info.class_name) { + zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(return_info.type_hint)); + } + + if (!zend_string_equals_literal_ci(return_info.class_name, "Traversable") + && !zend_string_equals_literal_ci(return_info.class_name, "Iterator") + && !zend_string_equals_literal_ci(return_info.class_name, "Generator")) { + zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(return_info.class_name)); + } + } + + CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; +} +/* }}} */ + ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array) /* {{{ */ { if (op_array->early_binding != (uint32_t)-1) { @@ -1231,10 +1312,11 @@ static zend_always_inline size_t zend_strnlen(const char* s, size_t maxlen) /* { ZEND_API int zend_unmangle_property_name_ex(const zend_string *name, const char **class_name, const char **prop_name, size_t *prop_len) /* {{{ */ { size_t class_name_len; + size_t anonclass_src_len; *class_name = NULL; - if (ZSTR_VAL(name)[0] != '\0') { + if (!ZSTR_LEN(name) || ZSTR_VAL(name)[0] != '\0') { *prop_name = ZSTR_VAL(name); if (prop_len) { *prop_len = ZSTR_LEN(name); @@ -1261,6 +1343,10 @@ ZEND_API int zend_unmangle_property_name_ex(const zend_string *name, const char } *class_name = ZSTR_VAL(name) + 1; + anonclass_src_len = zend_strnlen(*class_name + class_name_len + 1, ZSTR_LEN(name) - class_name_len - 2); + if (class_name_len + anonclass_src_len + 2 != ZSTR_LEN(name)) { + class_name_len += anonclass_src_len + 1; + } *prop_name = ZSTR_VAL(name) + class_name_len + 2; if (prop_len) { *prop_len = ZSTR_LEN(name) - class_name_len - 2; @@ -1429,14 +1515,15 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ { uint32_t fetch_type = zend_get_class_fetch_type(class_name); + zend_class_constant *cc; zval *c; if (class_name_refers_to_active_ce(class_name, fetch_type)) { - c = zend_hash_find(&CG(active_class_entry)->constants_table, name); + cc = zend_hash_find_ptr(&CG(active_class_entry)->constants_table, name); } else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); if (ce) { - c = zend_hash_find(&ce->constants_table, name); + cc = zend_hash_find_ptr(&ce->constants_table, name); } else { return 0; } @@ -1448,8 +1535,14 @@ static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, return 0; } + if (!cc || !zend_verify_const_access(cc, CG(active_class_entry))) { + return 0; + } + + c = &cc->value; + /* Substitute case-sensitive (or lowercase) persistent class constants */ - if (c && Z_TYPE_P(c) < IS_OBJECT) { + if (Z_TYPE_P(c) < IS_OBJECT) { ZVAL_DUP(zv, c); return 1; } @@ -1598,6 +1691,10 @@ int zendlex(zend_parser_stack_elem *elem) /* {{{ */ again: ZVAL_UNDEF(&zv); retval = lex_scan(&zv); + if (EG(exception)) { + return T_ERROR; + } + switch (retval) { case T_COMMENT: case T_DOC_COMMENT: @@ -1626,15 +1723,18 @@ again: ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers) /* {{{ */ { zend_bool persistent_hashes = (ce->type == ZEND_INTERNAL_CLASS) ? 1 : 0; - dtor_func_t zval_ptr_dtor_func = ((persistent_hashes) ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR); ce->refcount = 1; ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED; + if (CG(compiler_options) & ZEND_COMPILE_GUARDS) { + ce->ce_flags |= ZEND_ACC_USE_GUARDS; + } + ce->default_properties_table = NULL; ce->default_static_members_table = NULL; zend_hash_init_ex(&ce->properties_info, 8, NULL, (persistent_hashes ? zend_destroy_property_info_internal : NULL), persistent_hashes, 0); - zend_hash_init_ex(&ce->constants_table, 8, NULL, zval_ptr_dtor_func, persistent_hashes, 0); + zend_hash_init_ex(&ce->constants_table, 8, NULL, (persistent_hashes ? zend_destroy_class_constant_internal : NULL), persistent_hashes, 0); zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0); if (ce->type == ZEND_INTERNAL_CLASS) { @@ -1722,18 +1822,6 @@ zend_ast *zend_ast_append_str(zend_ast *left_ast, zend_ast *right_ast) /* {{{ */ } /* }}} */ -/* A hacky way that is used to store the doc comment for properties */ -zend_ast *zend_ast_append_doc_comment(zend_ast *list) /* {{{ */ -{ - if (CG(doc_comment)) { - list = zend_ast_list_add(list, zend_ast_create_zval_from_str(CG(doc_comment))); - CG(doc_comment) = NULL; - } - - return list; -} -/* }}} */ - void zend_verify_namespace(void) /* {{{ */ { if (FC(has_bracketed_namespaces) && !FC(in_namespace)) { @@ -1749,7 +1837,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) register char *end = path + len - 1; unsigned int len_adjust = 0; -#ifdef PHP_WIN32 +#ifdef ZEND_WIN32 /* Note that on Win32 CWD is per drive (heritage from CP/M). * This means dirname("c:foo") maps to "c:." or "c:" - which means CWD on C: drive. */ @@ -1838,25 +1926,26 @@ ZEND_API size_t zend_dirname(char *path, size_t len) static void zend_adjust_for_fetch_type(zend_op *opline, uint32_t type) /* {{{ */ { + zend_uchar factor = (opline->opcode == ZEND_FETCH_STATIC_PROP_R) ? 1 : 3; + switch (type & BP_VAR_MASK) { case BP_VAR_R: return; case BP_VAR_W: - case BP_VAR_REF: - opline->opcode += 3; + opline->opcode += 1 * factor; return; case BP_VAR_RW: - opline->opcode += 6; + opline->opcode += 2 * factor; return; case BP_VAR_IS: - opline->opcode += 9; + opline->opcode += 3 * factor; return; case BP_VAR_FUNC_ARG: - opline->opcode += 12; + opline->opcode += 4 * factor; opline->extended_value |= type >> BP_VAR_SHIFT; return; case BP_VAR_UNSET: - opline->opcode += 15; + opline->opcode += 5 * factor; return; EMPTY_SWITCH_DEFAULT_CASE() } @@ -1879,6 +1968,136 @@ static inline void zend_make_tmp_result(znode *result, zend_op *opline) /* {{{ * } /* }}} */ +static void zend_find_live_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */ +{ + zend_op *def = opline; + + while (def != CG(active_op_array)->opcodes) { + def--; + if (def->result_type == type && def->result.var == var) { + if (def->opcode == ZEND_ADD_ARRAY_ELEMENT || + def->opcode == ZEND_ROPE_ADD) { + /* not a real definition */ + continue; + } else if (def->opcode == ZEND_JMPZ_EX || + def->opcode == ZEND_JMPNZ_EX || + def->opcode == ZEND_BOOL || + def->opcode == ZEND_BOOL_NOT) { + /* result IS_BOOL, it does't have to be destroyed */ + break; + } else if (def->opcode == ZEND_DECLARE_CLASS || + def->opcode == ZEND_DECLARE_INHERITED_CLASS || + def->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED || + def->opcode == ZEND_DECLARE_ANON_CLASS || + def->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS) { + /* classes don't have to be destroyed */ + break; + } else if (def->opcode == ZEND_FAST_CALL) { + /* fast_calls don't have to be destroyed */ + break; + } else if (def->opcode == ZEND_NEW) { + /* Objects created via ZEND_NEW are only fully initialized + * after the DO_FCALL (constructor call) */ + def = CG(active_op_array)->opcodes + def->op2.opline_num - 1; + if (def + 1 == opline) { + break; + } + } + + zend_end_live_range(CG(active_op_array), + zend_start_live_range_ex(CG(active_op_array), + def + 1 - CG(active_op_array)->opcodes), + opline - CG(active_op_array)->opcodes, + ZEND_LIVE_TMPVAR, var); + break; + } + } +} +/* }}} */ + +static zend_always_inline int zend_is_def_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */ +{ + while (1) { + if (opline->result_type == type && opline->result.var == var) { + return opline->opcode != ZEND_ADD_ARRAY_ELEMENT && + opline->opcode != ZEND_ROPE_ADD; + } else if (opline->opcode == ZEND_OP_DATA) { + return (opline-1)->result_type == type && + (opline-1)->result.var == var; + } else if (opline->opcode == ZEND_END_SILENCE || + opline->opcode == ZEND_NOP || + opline->opcode == ZEND_EXT_NOP || + opline->opcode == ZEND_EXT_STMT || + opline->opcode == ZEND_EXT_FCALL_BEGIN || + opline->opcode == ZEND_EXT_FCALL_END || + opline->opcode == ZEND_TICKS) { + opline--; + } else { + return 0; + } + } +} +/* }}} */ + +static void zend_check_live_ranges(zend_op *opline) /* {{{ */ +{ + if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && + !zend_is_def_range(opline - 1, opline->op1_type, opline->op1.var)) { + + if (opline->opcode == ZEND_OP_DATA) { + if (!zend_is_def_range(opline - 2, opline->op1_type, opline->op1.var)) { + zend_find_live_range(opline - 1, opline->op1_type, opline->op1.var); + } + } else if (opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || + opline->opcode == ZEND_NEW || + opline->opcode == ZEND_FETCH_CLASS_CONSTANT || + opline->opcode == ZEND_ADD_INTERFACE || + opline->opcode == ZEND_ADD_TRAIT || + opline->opcode == ZEND_BIND_TRAITS || + opline->opcode == ZEND_VERIFY_ABSTRACT_CLASS) { + /* classes don't have to be destroyed */ + } else if (opline->opcode == ZEND_FAST_RET) { + /* fast_calls don't have to be destroyed */ + } else if (opline->opcode == ZEND_CASE || + opline->opcode == ZEND_FE_FETCH_R || + opline->opcode == ZEND_FE_FETCH_RW || + opline->opcode == ZEND_FE_FREE || + opline->opcode == ZEND_ROPE_ADD || + opline->opcode == ZEND_ROPE_END || + opline->opcode == ZEND_END_SILENCE || + opline->opcode == ZEND_FETCH_LIST || + opline->opcode == ZEND_VERIFY_RETURN_TYPE || + opline->opcode == ZEND_BIND_LEXICAL) { + /* these opcodes are handled separately */ + } else { + zend_find_live_range(opline, opline->op1_type, opline->op1.var); + } + } + + if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && + !zend_is_def_range(opline - 1, opline->op2_type, opline->op2.var)) { + + if (opline->opcode == ZEND_OP_DATA) { + if (!zend_is_def_range(opline - 2, opline->op2_type, opline->op2.var)) { + zend_find_live_range(opline-1, opline->op2_type, opline->op2.var); + } + } else if (opline->opcode == ZEND_FETCH_STATIC_PROP_R || + opline->opcode == ZEND_FETCH_STATIC_PROP_W || + opline->opcode == ZEND_FETCH_STATIC_PROP_RW || + opline->opcode == ZEND_FETCH_STATIC_PROP_IS || + opline->opcode == ZEND_FETCH_STATIC_PROP_FUNC_ARG || + opline->opcode == ZEND_FETCH_STATIC_PROP_UNSET || + opline->opcode == ZEND_UNSET_STATIC_PROP || + opline->opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP || + opline->opcode == ZEND_INSTANCEOF) { + /* classes don't have to be destroyed */ + } else { + zend_find_live_range(opline, opline->op2_type, opline->op2.var); + } + } +} +/* }}} */ + static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array)); @@ -1896,6 +2115,8 @@ static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode SET_NODE(opline->op2, op2); } + zend_check_live_ranges(opline); + if (result) { zend_make_var_result(result, opline); } @@ -1920,6 +2141,8 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z SET_NODE(opline->op2, op2); } + zend_check_live_ranges(opline); + if (result) { zend_make_tmp_result(result, opline); } @@ -1930,7 +2153,14 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z static void zend_emit_tick(void) /* {{{ */ { - zend_op *opline = get_next_op(CG(active_op_array)); + zend_op *opline; + + /* This prevents a double TICK generated by the parser statement of "declare()" */ + if (CG(active_op_array)->last && CG(active_op_array)->opcodes[CG(active_op_array)->last - 1].opcode == ZEND_TICKS) { + return; + } + + opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_TICKS; SET_UNUSED(opline->op1); @@ -1974,6 +2204,7 @@ static inline void zend_update_jump_target(uint32_t opnum_jump, uint32_t opnum_t case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: + case ZEND_JMP_SET: opline->op2.opline_num = opnum_target; break; EMPTY_SWITCH_DEFAULT_CASE() @@ -2022,20 +2253,62 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */ zend_op *opline = NULL, *oplines = zend_stack_base(&CG(delayed_oplines_stack)); uint32_t i, count = zend_stack_count(&CG(delayed_oplines_stack)); - ZEND_ASSERT(count > offset); + ZEND_ASSERT(count >= offset); for (i = offset; i < count; ++i) { opline = get_next_op(CG(active_op_array)); memcpy(opline, &oplines[i], sizeof(zend_op)); + zend_check_live_ranges(opline); } CG(delayed_oplines_stack).top = offset; return opline; } /* }}} */ -static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */ +static void zend_emit_return_type_check( + znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */ { + /* `return ...;` is illegal in a void function (but `return;` isn't) */ + if (return_info->type_hint == IS_VOID) { + if (expr) { + if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) { + zend_error_noreturn(E_COMPILE_ERROR, + "A void function must not return a value " + "(did you mean \"return;\" instead of \"return null;\"?)"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value"); + } + } + /* we don't need run-time check */ + return; + } + if (return_info->type_hint != IS_UNDEF) { - zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); + zend_op *opline; + + if (!expr && !implicit) { + if (return_info->allow_null) { + zend_error_noreturn(E_COMPILE_ERROR, + "A function with return type must return a value " + "(did you mean \"return null;\" instead of \"return;\"?)"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "A function with return type must return a value"); + } + } + + if (expr && expr->op_type == IS_CONST) { + if ((return_info->type_hint == Z_TYPE(expr->u.constant)) + ||((return_info->type_hint == _IS_BOOL) + && (Z_TYPE(expr->u.constant) == IS_FALSE + || Z_TYPE(expr->u.constant) == IS_TRUE)) + || (return_info->allow_null + && Z_TYPE(expr->u.constant) == IS_NULL)) { + /* we don't need run-time check */ + return; + } + } + + opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); if (expr && expr->op_type == IS_CONST) { opline->result_type = expr->op_type = IS_TMP_VAR; opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array)); @@ -2050,23 +2323,26 @@ static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) } /* }}} */ -void zend_emit_final_return(zval *zv) /* {{{ */ +void zend_emit_final_return(int return_one) /* {{{ */ { znode zn; + zend_op *ret; zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; - if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1); + if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE + && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) { + zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1, 1); } zn.op_type = IS_CONST; - if (zv) { - ZVAL_COPY_VALUE(&zn.u.constant, zv); + if (return_one) { + ZVAL_LONG(&zn.u.constant, 1); } else { ZVAL_NULL(&zn.u.constant); } - zend_emit_op(NULL, returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN, &zn, NULL); + ret = zend_emit_op(NULL, returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN, &zn, NULL); + ret->extended_value = -1; } /* }}} */ @@ -2089,7 +2365,9 @@ static inline zend_bool zend_is_call(zend_ast *ast) /* {{{ */ static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */ { - return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL; + return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL + || ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_DECL + || ast->kind == ZEND_AST_USE_TRAIT || ast->kind == ZEND_AST_METHOD; } /* }}} */ @@ -2145,8 +2423,15 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th zend_compile_expr(&name_node, name_ast); if (name_node.op_type == IS_CONST) { - zend_string *name = Z_STR(name_node.u.constant); - uint32_t fetch_type = zend_get_class_fetch_type(name); + zend_string *name; + uint32_t fetch_type; + + if (Z_TYPE(name_node.u.constant) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name"); + } + + name = Z_STR(name_node.u.constant); + fetch_type = zend_get_class_fetch_type(name); opline = zend_emit_op(result, ZEND_FETCH_CLASS, NULL, NULL); opline->extended_value = fetch_type | (throw_exception ? ZEND_FETCH_CLASS_EXCEPTION : 0); @@ -2170,6 +2455,61 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th } /* }}} */ +static void zend_compile_class_ref_ex(znode *result, zend_ast *name_ast, uint32_t fetch_flags) /* {{{ */ +{ + uint32_t fetch_type; + + if (name_ast->kind != ZEND_AST_ZVAL) { + znode name_node; + + zend_compile_expr(&name_node, name_ast); + + if (name_node.op_type == IS_CONST) { + zend_string *name; + + if (Z_TYPE(name_node.u.constant) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name"); + } + + name = Z_STR(name_node.u.constant); + fetch_type = zend_get_class_fetch_type(name); + + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { + result->op_type = IS_CONST; + ZVAL_STR(&result->u.constant, zend_resolve_class_name(name, ZEND_NAME_FQ)); + } else { + zend_ensure_valid_class_fetch_type(fetch_type); + result->op_type = IS_UNUSED; + result->u.op.num = fetch_type | fetch_flags; + } + + zend_string_release(name); + } else { + zend_op *opline = zend_emit_op(result, ZEND_FETCH_CLASS, NULL, &name_node); + opline->extended_value = ZEND_FETCH_CLASS_DEFAULT | fetch_flags; + } + return; + } + + /* Fully qualified names are always default refs */ + if (name_ast->attr == ZEND_NAME_FQ) { + result->op_type = IS_CONST; + ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast)); + return; + } + + fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); + if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) { + result->op_type = IS_CONST; + ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast)); + } else { + zend_ensure_valid_class_fetch_type(fetch_type); + result->op_type = IS_UNUSED; + result->u.op.num = fetch_type | fetch_flags; + } +} +/* }}} */ + static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *name_ast = ast->child[0]; @@ -2224,7 +2564,7 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint if (ast->kind != ZEND_AST_ZVAL && CG(active_op_array)->scope && CG(active_op_array)->this_var == (uint32_t)-1 ) { - zend_string *key = zend_string_init("this", sizeof("this") - 1, 0); + zend_string *key = CG(known_strings)[ZEND_STR_THIS]; CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key); } } @@ -2258,13 +2598,13 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type); void zend_compile_assign(znode *result, zend_ast *ast); -static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node); +static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style); static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */ { znode dummy_node; - if (var_ast->kind == ZEND_AST_LIST) { - zend_compile_list_assign(&dummy_node, var_ast, value_node); + if (var_ast->kind == ZEND_AST_ARRAY) { + zend_compile_list_assign(&dummy_node, var_ast, value_node, var_ast->attr); } else { zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast, zend_ast_create_znode(value_node)); @@ -2376,21 +2716,17 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t znode class_node, prop_node; zend_op *opline; - if (zend_is_const_default_class_ref(class_ast)) { - class_node.op_type = IS_CONST; - ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast)); - } else { - zend_compile_class_ref(&class_node, class_ast, 1); - } + zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); zend_compile_expr(&prop_node, prop_ast); if (delayed) { - opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &prop_node, NULL); + opline = zend_delayed_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL); } else { - opline = zend_emit_op(result, ZEND_FETCH_R, &prop_node, NULL); + opline = zend_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL); } if (opline->op1_type == IS_CONST) { + convert_to_string(CT_CONSTANT(opline->op1)); zend_alloc_polymorphic_cache_slot(opline->op1.constant); } if (class_node.op_type == IS_CONST) { @@ -2400,7 +2736,6 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t } else { SET_NODE(opline->op2, &class_node); } - opline->extended_value |= ZEND_FETCH_STATIC_MEMBER; return opline; } @@ -2413,19 +2748,35 @@ void zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type, int d } /* }}} */ -static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node) /* {{{ */ +static void zend_verify_list_assign_target(zend_ast *var_ast, zend_bool old_style) /* {{{ */ { + if (var_ast->kind == ZEND_AST_ARRAY) { + if (old_style != var_ast->attr) { + zend_error(E_COMPILE_ERROR, "Cannot mix [] and list()"); + } + } else if (!zend_can_write_to_variable(var_ast)) { + zend_error(E_COMPILE_ERROR, "Assignments can only happen to writable values"); + } +} +/* }}} */ + +static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */ { - zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; zend_bool has_elems = 0; for (i = 0; i < list->children; ++i) { - zend_ast *var_ast = list->child[i]; + zend_ast *elem_ast = list->child[i]; + zend_ast *var_ast; znode fetch_result, dim_node; - if (var_ast == NULL) { + if (elem_ast == NULL) { continue; } + if (elem_ast->attr) { + zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference"); + } + + var_ast = elem_ast->child[0]; has_elems = 1; dim_node.op_type = IS_CONST; @@ -2435,6 +2786,12 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n Z_TRY_ADDREF(expr_node->u.constant); } + if (elem_ast->child[1] != NULL) { + zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments"); + } + + zend_verify_list_assign_target(var_ast, old_style); + zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node); zend_emit_assign_znode(var_ast, &fetch_result); } @@ -2442,6 +2799,54 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n if (!has_elems) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use empty list"); } +} +/* }}} */ + +static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */ +{ + uint32_t i; + + for (i = 0; i < list->children; ++i) { + zend_ast *pair_ast = list->child[i]; + zend_ast *var_ast = pair_ast->child[0]; + zend_ast *key_ast = pair_ast->child[1]; + znode fetch_result, dim_node; + + if (pair_ast->attr) { + zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference"); + } + + zend_compile_expr(&dim_node, key_ast); + + if (expr_node->op_type == IS_CONST) { + Z_TRY_ADDREF(expr_node->u.constant); + } + + if (var_ast == NULL) { + zend_error(E_COMPILE_ERROR, "Cannot use empty array entries in keyed array"); + } + + if (key_ast == NULL) { + zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments"); + } + + zend_verify_list_assign_target(var_ast, old_style); + + zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node); + zend_emit_assign_znode(var_ast, &fetch_result); + } +} +/* }}} */ + +static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style) /* {{{ */ +{ + zend_ast_list *list = zend_ast_get_list(ast); + + if (list->children > 0 && list->child[0] != NULL && list->child[0]->child[1] != NULL /* has key */) { + zend_compile_keyed_list_assign(list, expr_node, old_style); + } else { + zend_compile_unkeyed_list_assign(list, expr_node, old_style); + } *result = *expr_node; } @@ -2490,13 +2895,16 @@ zend_bool zend_list_has_assign_to(zend_ast *list_ast, zend_string *name) /* {{{ zend_ast_list *list = zend_ast_get_list(list_ast); uint32_t i; for (i = 0; i < list->children; i++) { - zend_ast *var_ast = list->child[i]; - if (!var_ast) { + zend_ast *elem_ast = list->child[i]; + zend_ast *var_ast; + + if (!elem_ast) { continue; } + var_ast = elem_ast->child[0]; /* Recursively check nested list()s */ - if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) { + if (var_ast->kind == ZEND_AST_ARRAY && zend_list_has_assign_to(var_ast, name)) { return 1; } @@ -2546,8 +2954,10 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ switch (var_ast->kind) { case ZEND_AST_VAR: case ZEND_AST_STATIC_PROP: - zend_compile_var(&var_node, var_ast, BP_VAR_W); + offset = zend_delayed_compile_begin(); + zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W); zend_compile_expr(&expr_node, expr_ast); + zend_delayed_compile_end(offset); zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node); return; case ZEND_AST_DIM: @@ -2576,7 +2986,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ zend_emit_op_data(&expr_node); return; - case ZEND_AST_LIST: + case ZEND_AST_ARRAY: if (zend_list_has_assign_to_self(var_ast, expr_ast)) { /* list($a, $b) = $a should evaluate the right $a first */ zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0); @@ -2584,7 +2994,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); } - zend_compile_list_assign(result, var_ast, &expr_node); + zend_compile_list_assign(result, var_ast, &expr_node, var_ast->attr); return; EMPTY_SWITCH_DEFAULT_CASE(); } @@ -2598,23 +3008,23 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */ znode target_node, source_node; zend_op *opline; + uint32_t offset; 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); - zend_compile_var(&source_node, source_ast, BP_VAR_REF); + offset = zend_delayed_compile_begin(); + zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W); + zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W); + zend_delayed_compile_end(offset); if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use result of built-in function in write context"); } opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node); - if (!result) { - opline->result_type |= EXT_TYPE_UNUSED; - } if (zend_is_call(source_ast)) { opline->extended_value = ZEND_RETURNS_FUNCTION; @@ -2645,8 +3055,10 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ switch (var_ast->kind) { case ZEND_AST_VAR: case ZEND_AST_STATIC_PROP: - zend_compile_var(&var_node, var_ast, BP_VAR_RW); + offset = zend_delayed_compile_begin(); + zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW); zend_compile_expr(&expr_node, expr_ast); + zend_delayed_compile_end(offset); zend_emit_op(result, opcode, &var_node, &expr_node); return; case ZEND_AST_DIM: @@ -2678,7 +3090,6 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ { - /* TODO.AST &var error */ zend_ast_list *args = zend_ast_get_list(ast); uint32_t i; zend_bool uses_arg_unpack = 0; @@ -2691,7 +3102,6 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ znode arg_node; zend_op *opline; zend_uchar opcode; - zend_ulong flags = 0; if (arg->kind == ZEND_AST_UNPACK) { uses_arg_unpack = 1; @@ -2717,13 +3127,16 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ /* Function call was converted into builtin instruction */ opcode = ZEND_SEND_VAL; } else { - 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; + if (fbc) { + if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) { + opcode = ZEND_SEND_VAR_NO_REF; + } else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) { + opcode = ZEND_SEND_VAL; + } else { + opcode = ZEND_SEND_VAR; } + } else { + opcode = ZEND_SEND_VAR_NO_REF_EX; } } } else if (fbc) { @@ -2741,10 +3154,19 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ } } else { zend_compile_expr(&arg_node, arg); - if (arg_node.op_type & (IS_VAR|IS_CV)) { - opcode = ZEND_SEND_VAR_NO_REF; - if (fbc && ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) { - flags |= ZEND_ARG_SEND_BY_REF; + ZEND_ASSERT(arg_node.op_type != IS_CV); + if (arg_node.op_type == IS_VAR) { + /* pass ++$a or something similar */ + if (fbc) { + if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) { + opcode = ZEND_SEND_VAR_NO_REF; + } else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) { + opcode = ZEND_SEND_VAL; + } else { + opcode = ZEND_SEND_VAR; + } + } else { + opcode = ZEND_SEND_VAR_NO_REF_EX; } } else { if (fbc) { @@ -2758,51 +3180,35 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ } } - opline = get_next_op(CG(active_op_array)); - opline->opcode = opcode; - SET_NODE(opline->op1, &arg_node); - SET_UNUSED(opline->op2); + opline = zend_emit_op(NULL, opcode, &arg_node, NULL); opline->op2.opline_num = arg_num; opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_num); - - if (opcode == ZEND_SEND_VAR_NO_REF) { - if (fbc) { - flags |= ZEND_ARG_COMPILE_TIME_BOUND; - } - if ((flags & ZEND_ARG_COMPILE_TIME_BOUND) && !(flags & ZEND_ARG_SEND_BY_REF)) { - opline->opcode = ZEND_SEND_VAR; - opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND; - } else { - opline->extended_value = flags; - } - } else if (fbc) { - opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND; - } } return arg_count; } /* }}} */ -ZEND_API zend_uchar zend_get_call_op(zend_uchar init_op, zend_function *fbc) /* {{{ */ +ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* {{{ */ { - if (fbc) { + if (fbc && init_op->opcode == ZEND_INIT_FCALL) { if (fbc->type == ZEND_INTERNAL_FUNCTION) { - if (!zend_execute_internal && - !fbc->common.scope && - !(fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_RETURN_REFERENCE))) { - return ZEND_DO_ICALL; + if (!zend_execute_internal) { + if (!(fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_RETURN_REFERENCE))) { + return ZEND_DO_ICALL; + } else { + return ZEND_DO_FCALL_BY_NAME; + } } } else { - if (zend_execute_ex == execute_ex && - !(fbc->common.fn_flags & ZEND_ACC_GENERATOR)) { + if (zend_execute_ex == execute_ex) { return ZEND_DO_UCALL; } } } else if (zend_execute_ex == execute_ex && !zend_execute_internal && - (init_op == ZEND_INIT_FCALL_BY_NAME || - init_op == ZEND_INIT_NS_FCALL_BY_NAME)) { + (init_op->opcode == ZEND_INIT_FCALL_BY_NAME || + init_op->opcode == ZEND_INIT_NS_FCALL_BY_NAME)) { return ZEND_DO_FCALL_BY_NAME; } return ZEND_DO_FCALL; @@ -2828,7 +3234,7 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function * } call_flags = (opline->opcode == ZEND_NEW ? ZEND_CALL_CTOR : 0); - opline = zend_emit_op(result, zend_get_call_op(opline->opcode, fbc), NULL, NULL); + opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL); opline->op1.num = call_flags; zend_do_extended_fcall_end(); @@ -2864,13 +3270,14 @@ void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast) / void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast) /* {{{ */ { - zend_op *opline = get_next_op(CG(active_op_array)); if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) { const char *colon; zend_string *str = Z_STR(name_node->u.constant); if ((colon = zend_memrchr(ZSTR_VAL(str), ':', ZSTR_LEN(str))) != NULL && colon > ZSTR_VAL(str) && *(colon - 1) == ':') { zend_string *class = zend_string_init(ZSTR_VAL(str), colon - ZSTR_VAL(str) - 1, 0); zend_string *method = zend_string_init(colon + 1, ZSTR_LEN(str) - (colon - ZSTR_VAL(str)) - 1, 0); + zend_op *opline = get_next_op(CG(active_op_array)); + opline->opcode = ZEND_INIT_STATIC_METHOD_CALL; opline->op1_type = IS_CONST; opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), class); @@ -2879,6 +3286,8 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a zend_alloc_cache_slot(opline->op2.constant); zval_ptr_dtor(&name_node->u.constant); } else { + zend_op *opline = get_next_op(CG(active_op_array)); + opline->opcode = ZEND_INIT_FCALL_BY_NAME; SET_UNUSED(opline->op1); opline->op2_type = IS_CONST; @@ -2886,9 +3295,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a zend_alloc_cache_slot(opline->op2.constant); } } else { - opline->opcode = ZEND_INIT_DYNAMIC_CALL; - SET_UNUSED(opline->op1); - SET_NODE(opline->op2, name_node); + zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node); } zend_compile_call_common(result, args_ast, NULL); @@ -2960,6 +3367,14 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */ return FAILURE; } + if (zend_try_ct_eval_const(&result->u.constant, name, 0)) { + zend_string_release(name); + zval_ptr_dtor(&result->u.constant); + ZVAL_TRUE(&result->u.constant); + result->op_type = IS_CONST; + return SUCCESS; + } + opline = zend_emit_op_tmp(result, ZEND_DEFINED, NULL, NULL); opline->op1_type = IS_CONST; LITERAL_STR(opline->op1, name); @@ -2976,6 +3391,46 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */ } /* }}} */ +int zend_compile_func_chr(znode *result, zend_ast_list *args) /* {{{ */ +{ + + if (args->children == 1 && + args->child[0]->kind == ZEND_AST_ZVAL && + Z_TYPE_P(zend_ast_get_zval(args->child[0])) == IS_LONG) { + + zend_long c = Z_LVAL_P(zend_ast_get_zval(args->child[0])) & 0xff; + + result->op_type = IS_CONST; + if (CG(one_char_string)[c]) { + ZVAL_INTERNED_STR(&result->u.constant, CG(one_char_string)[c]); + } else { + ZVAL_NEW_STR(&result->u.constant, zend_string_alloc(1, 0)); + Z_STRVAL_P(&result->u.constant)[0] = (char)c; + Z_STRVAL_P(&result->u.constant)[1] = '\0'; + } + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +int zend_compile_func_ord(znode *result, zend_ast_list *args) /* {{{ */ +{ + if (args->children == 1 && + args->child[0]->kind == ZEND_AST_ZVAL && + Z_TYPE_P(zend_ast_get_zval(args->child[0])) == IS_STRING) { + + result->op_type = IS_CONST; + ZVAL_LONG(&result->u.constant, (unsigned char)Z_STRVAL_P(zend_ast_get_zval(args->child[0]))[0]); + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + + static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t num_args) /* {{{ */ { zend_string *name, *lcname; @@ -2990,8 +3445,9 @@ static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t lcname = zend_string_tolower(name); fbc = zend_hash_find_ptr(CG(function_table), lcname); - if (!fbc || (fbc->type == ZEND_INTERNAL_FUNCTION && - (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) + if (!fbc + || (fbc->type == ZEND_INTERNAL_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) + || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS)) ) { zend_string_release(lcname); return FAILURE; @@ -3118,7 +3574,9 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string * zend_compile_call_common(result, (zend_ast*)args, fbc); - CG(active_op_array)->opcodes[check_op_number].op2.opline_num = get_next_op_number(CG(active_op_array)); + opline = &CG(active_op_array)->opcodes[check_op_number]; + opline->op2.opline_num = get_next_op_number(CG(active_op_array)); + SET_NODE(opline->result, result); } else { if (!fbc) { zend_string_release(name); @@ -3131,12 +3589,20 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string * } /* }}} */ -int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc) /* {{{ */ +int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */ { if (fbc->internal_function.handler == ZEND_FN(display_disabled_function)) { return FAILURE; } + if (zend_string_equals_literal(lcname, "assert")) { + return zend_compile_assert(result, args, lcname, fbc); + } + + if (CG(compiler_options) & ZEND_COMPILE_NO_BUILTINS) { + return FAILURE; + } + if (zend_string_equals_literal(lcname, "strlen")) { return zend_compile_func_strlen(result, args); } else if (zend_string_equals_literal(lcname, "is_null")) { @@ -3163,12 +3629,14 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l return zend_compile_func_typecheck(result, args, IS_RESOURCE); } else if (zend_string_equals_literal(lcname, "defined")) { return zend_compile_func_defined(result, args); + } else if (zend_string_equals_literal(lcname, "chr") && type == BP_VAR_R) { + return zend_compile_func_chr(result, args); + } else if (zend_string_equals_literal(lcname, "ord") && type == BP_VAR_R) { + return zend_compile_func_ord(result, args); } else if (zend_string_equals_literal(lcname, "call_user_func_array")) { return zend_compile_func_cufa(result, args, lcname); } else if (zend_string_equals_literal(lcname, "call_user_func")) { return zend_compile_func_cuf(result, args, lcname); - } else if (zend_string_equals_literal(lcname, "assert")) { - return zend_compile_assert(result, args, lcname, fbc); } else { return FAILURE; } @@ -3209,8 +3677,9 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ lcname = zend_string_tolower(Z_STR_P(name)); fbc = zend_hash_find_ptr(CG(function_table), lcname); - if (!fbc || (fbc->type == ZEND_INTERNAL_FUNCTION && - (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) + if (!fbc + || (fbc->type == ZEND_INTERNAL_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) + || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS)) ) { zend_string_release(lcname); zend_compile_dynamic_call(result, &name_node, args_ast); @@ -3218,7 +3687,7 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ } if (zend_try_compile_special_func(result, lcname, - zend_ast_get_list(args_ast), fbc) == SUCCESS + zend_ast_get_list(args_ast), fbc, type) == SUCCESS ) { zend_string_release(lcname); zval_ptr_dtor(&name_node.u.constant); @@ -3244,6 +3713,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{ znode obj_node, method_node; zend_op *opline; + zend_function *fbc = NULL; if (is_this_fetch(obj_ast)) { obj_node.op_type = IS_UNUSED; @@ -3267,7 +3737,20 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{ SET_NODE(opline->op2, &method_node); } - zend_compile_call_common(result, args_ast, NULL); + /* Check if this calls a known method on $this */ + if (opline->op1_type == IS_UNUSED && opline->op2_type == IS_CONST && + CG(active_class_entry) && zend_is_scope_known()) { + zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1); + fbc = zend_hash_find_ptr(&CG(active_class_entry)->function_table, lcname); + + /* We only know the exact method that is being called if it is either private or final. + * Otherwise an overriding method in a child class may be called. */ + if (fbc && !(fbc->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_FINAL))) { + fbc = NULL; + } + } + + zend_compile_call_common(result, args_ast, fbc); } /* }}} */ @@ -3285,15 +3768,9 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ znode class_node, method_node; zend_op *opline; - zend_ulong extended_value = 0; + zend_function *fbc = NULL; - if (zend_is_const_default_class_ref(class_ast)) { - class_node.op_type = IS_CONST; - ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast)); - } else { - opline = zend_compile_class_ref(&class_node, class_ast, 1); - extended_value = opline->extended_value; - } + zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); zend_compile_expr(&method_node, method_ast); if (method_node.op_type == IS_CONST) { @@ -3309,7 +3786,6 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_INIT_STATIC_METHOD_CALL; - opline->extended_value = extended_value; zend_set_class_name_op1(opline, &class_node); @@ -3325,8 +3801,30 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ } else { SET_NODE(opline->op2, &method_node); } + zend_check_live_ranges(opline); - zend_compile_call_common(result, args_ast, NULL); + /* Check if we already know which method we're calling */ + if (opline->op2_type == IS_CONST) { + zend_class_entry *ce = NULL; + if (opline->op1_type == IS_CONST) { + zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1); + ce = zend_hash_find_ptr(CG(class_table), lcname); + if (!ce && CG(active_class_entry) + && zend_string_equals_ci(CG(active_class_entry)->name, lcname)) { + ce = CG(active_class_entry); + } + } else if (opline->op1_type == IS_UNUSED + && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF + && zend_is_scope_known()) { + ce = CG(active_class_entry); + } + if (ce) { + zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1); + fbc = zend_hash_find_ptr(&ce->function_table, lcname); + } + } + + zend_compile_call_common(result, args_ast, fbc); } /* }}} */ @@ -3341,10 +3839,7 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ zend_op *opline; uint32_t opnum; - if (zend_is_const_default_class_ref(class_ast)) { - class_node.op_type = IS_CONST; - ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast)); - } else if (class_ast->kind == ZEND_AST_CLASS) { + if (class_ast->kind == ZEND_AST_CLASS) { uint32_t dcl_opnum = get_next_op_number(CG(active_op_array)); zend_compile_class_decl(class_ast); /* jump over anon class declaration */ @@ -3354,9 +3849,9 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ } class_node.op_type = opline->result_type; class_node.u.op.var = opline->result.var; - opline->op1.opline_num = get_next_op_number(CG(active_op_array)); + opline->extended_value = get_next_op_number(CG(active_op_array)); } else { - zend_compile_class_ref(&class_node, class_ast, 1); + zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); } opnum = get_next_op_number(CG(active_op_array)); @@ -3373,7 +3868,8 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ zend_compile_call_common(&ctor_result, args_ast, NULL); zend_do_free(&ctor_result); - /* New jumps over ctor call if ctor does not exist */ + /* We save the position of DO_FCALL for convenience in find_live_range(). + * This info is not preserved for runtime. */ opline = &CG(active_op_array)->opcodes[opnum]; opline->op2.opline_num = get_next_op_number(CG(active_op_array)); } @@ -3386,7 +3882,7 @@ void zend_compile_clone(znode *result, zend_ast *ast) /* {{{ */ znode obj_node; zend_compile_expr(&obj_node, obj_ast); - zend_emit_op(result, ZEND_CLONE, &obj_node, NULL); + zend_emit_op_tmp(result, ZEND_CLONE, &obj_node, NULL); } /* }}} */ @@ -3406,19 +3902,27 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */ zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node); zend_alloc_cache_slot(opline->op2.constant); } else { - zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL); + /* name_ast should be evaluated only. FETCH_GLOBAL_LOCK instructs FETCH_W + * to not free the name_node operand, so it can be reused in the following + * ASSIGN_REF, which then frees it. */ + zend_op *opline = zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL); + opline->extended_value = ZEND_FETCH_GLOBAL_LOCK; - // TODO.AST Avoid double fetch - //opline->extended_value = ZEND_FETCH_GLOBAL_LOCK; + if (name_node.op_type == IS_CONST) { + zend_string_addref(Z_STR(name_node.u.constant)); + } - zend_emit_assign_ref_znode(var_ast, &result); + zend_emit_assign_ref_znode( + zend_ast_create(ZEND_AST_VAR, zend_ast_create_znode(&name_node)), + &result + ); } } /* }}} */ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_bool by_ref) /* {{{ */ { - znode var_node, result; + znode var_node; zend_op *opline; zend_compile_expr(&var_node, var_ast); @@ -3439,16 +3943,10 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_ } zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value); - opline = zend_emit_op(&result, by_ref ? ZEND_FETCH_W : ZEND_FETCH_R, &var_node, NULL); - opline->extended_value = ZEND_FETCH_STATIC; - - if (by_ref) { - zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast); - zend_emit_assign_ref_znode(fetch_ast, &result); - } else { - zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast); - zend_emit_assign_znode(fetch_ast, &result); - } + opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &var_node); + opline->op1_type = IS_CV; + opline->op1.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR(var_node.u.constant))); + opline->extended_value = by_ref; } /* }}} */ @@ -3471,9 +3969,11 @@ void zend_compile_static_var(zend_ast *ast) /* {{{ */ void zend_compile_unset(zend_ast *ast) /* {{{ */ { zend_ast *var_ast = ast->child[0]; - znode var_node; zend_op *opline; + + zend_ensure_writable_variable(var_ast); + switch (var_ast->kind) { case ZEND_AST_VAR: if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { @@ -3494,19 +3994,73 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */ return; case ZEND_AST_STATIC_PROP: opline = zend_compile_static_prop_common(NULL, var_ast, BP_VAR_UNSET, 0); - opline->opcode = ZEND_UNSET_VAR; + opline->opcode = ZEND_UNSET_STATIC_PROP; return; EMPTY_SWITCH_DEFAULT_CASE() } } /* }}} */ -static void zend_free_foreach_and_switch_variables(void) /* {{{ */ +static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value) /* {{{ */ { - zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var); + zend_loop_var *base; + zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack)); + + if (!loop_var) { + return 1; + } + base = zend_stack_base(&CG(loop_var_stack)); + for (; loop_var >= base; loop_var--) { + if (loop_var->opcode == ZEND_FAST_CALL) { + zend_op *opline = get_next_op(CG(active_op_array)); + + opline->opcode = ZEND_FAST_CALL; + opline->result_type = IS_TMP_VAR; + opline->result.var = loop_var->var_num; + SET_UNUSED(opline->op1); + if (return_value) { + SET_NODE(opline->op2, return_value); + } else { + SET_UNUSED(opline->op2); + } + opline->op1.num = loop_var->u.try_catch_offset; + } else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) { + zend_op *opline = get_next_op(CG(active_op_array)); + opline->opcode = ZEND_DISCARD_EXCEPTION; + opline->op1_type = IS_TMP_VAR; + opline->op1.var = loop_var->var_num; + SET_UNUSED(opline->op2); + } else if (loop_var->opcode == ZEND_RETURN) { + /* Stack separator */ + break; + } else if (depth <= 1) { + return 1; + } else if (loop_var->opcode == ZEND_NOP) { + /* Loop doesn't have freeable variable */ + depth--; + } else { + zend_op *opline; + + ZEND_ASSERT(loop_var->var_type & (IS_VAR|IS_TMP_VAR)); + opline = get_next_op(CG(active_op_array)); + opline->opcode = loop_var->opcode; + opline->op1_type = loop_var->var_type; + opline->op1.var = loop_var->var_num; + SET_UNUSED(opline->op2); + opline->op2.num = loop_var->u.live_range_offset; + opline->extended_value = ZEND_FREE_ON_RETURN; + depth--; + } + } + return (depth == 0); } /* }}} */ +static int zend_handle_loops_and_finally(znode *return_value) /* {{{ */ +{ + return zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1, return_value); +} +/* }}} */ void zend_compile_return(zend_ast *ast) /* {{{ */ { @@ -3520,23 +4074,19 @@ void zend_compile_return(zend_ast *ast) /* {{{ */ expr_node.op_type = IS_CONST; ZVAL_NULL(&expr_node.u.constant); } else if (by_ref && zend_is_variable(expr_ast) && !zend_is_call(expr_ast)) { - zend_compile_var(&expr_node, expr_ast, BP_VAR_REF); + zend_compile_var(&expr_node, expr_ast, BP_VAR_W); } else { zend_compile_expr(&expr_node, expr_ast); } - zend_free_foreach_and_switch_variables(); - - if (CG(context).in_finally) { - opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL); - opline->op1_type = IS_TMP_VAR; - opline->op1.var = CG(context).fast_call_var; - } - /* Generator return types are handled separately */ if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1); + zend_emit_return_type_check( + expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0); } + + zend_handle_loops_and_finally((expr_node.op_type & (IS_TMP_VAR | IS_VAR)) ? &expr_node : NULL); + opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN, &expr_node, NULL); @@ -3552,12 +4102,14 @@ void zend_compile_return(zend_ast *ast) /* {{{ */ void zend_compile_echo(zend_ast *ast) /* {{{ */ { + zend_op *opline; zend_ast *expr_ast = ast->child[0]; znode expr_node; zend_compile_expr(&expr_node, expr_ast); - zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); + opline = zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); + opline->extended_value = 0; } /* }}} */ @@ -3603,24 +4155,11 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "'%s' not in the 'loop' or 'switch' context", ast->kind == ZEND_AST_BREAK ? "break" : "continue"); } else { - int array_offset = CG(context).current_brk_cont; - zend_long nest_level = depth; - znode *loop_var = zend_stack_top(&CG(loop_var_stack)); - - do { - if (array_offset == -1) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s", - ast->kind == ZEND_AST_BREAK ? "break" : "continue", - depth, depth == 1 ? "" : "s"); - } - - if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) { - generate_free_loop_var(loop_var); - loop_var--; - } - - array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent; - } while (--nest_level > 0); + if (!zend_handle_loops_and_finally_ex(depth, NULL)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s", + ast->kind == ZEND_AST_BREAK ? "break" : "continue", + depth, depth == 1 ? "" : "s"); + } } opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL); opline->op1.num = CG(context).current_brk_cont; @@ -3628,16 +4167,81 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */ } /* }}} */ +void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */ +{ + zend_label *dest; + int current, remove_oplines = opline->op1.num; + zval *label; + uint32_t opnum = opline - op_array->opcodes; + + label = CT_CONSTANT_EX(op_array, opline->op2.constant); + if (CG(context).labels == NULL || + (dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL + ) { + CG(in_compilation) = 1; + CG(active_op_array) = op_array; + CG(zend_lineno) = opline->lineno; + zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label)); + } + + zval_dtor(label); + ZVAL_NULL(label); + + current = opline->extended_value; + for (; current != dest->brk_cont; current = CG(context).brk_cont_array[current].parent) { + if (current == -1) { + CG(in_compilation) = 1; + CG(active_op_array) = op_array; + CG(zend_lineno) = opline->lineno; + zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed"); + } + if (CG(context).brk_cont_array[current].start >= 0) { + remove_oplines--; + } + } + + for (current = 0; current < op_array->last_try_catch; ++current) { + zend_try_catch_element *elem = &op_array->try_catch_array[current]; + if (elem->try_op > opnum) { + break; + } + if (elem->finally_op && opnum < elem->finally_op - 1 + && (dest->opline_num > elem->finally_end || dest->opline_num < elem->try_op) + ) { + remove_oplines--; + } + } + + opline->opcode = ZEND_JMP; + opline->op1.opline_num = dest->opline_num; + opline->extended_value = 0; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + SET_UNUSED(opline->result); + + ZEND_ASSERT(remove_oplines >= 0); + while (remove_oplines--) { + opline--; + MAKE_NOP(opline); + ZEND_VM_SET_OPCODE_HANDLER(opline); + } +} +/* }}} */ + void zend_compile_goto(zend_ast *ast) /* {{{ */ { zend_ast *label_ast = ast->child[0]; znode label_node; zend_op *opline; + uint32_t opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_expr(&label_node, label_ast); + + /* Label resolution and unwinding adjustments happen in pass two. */ + zend_handle_loops_and_finally(NULL); opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node); + opline->op1.num = get_next_op_number(CG(active_op_array)) - opnum_start - 1; opline->extended_value = CG(context).current_brk_cont; - zend_resolve_goto_label(CG(active_op_array), opline, 0); } /* }}} */ @@ -3648,7 +4252,7 @@ void zend_compile_label(zend_ast *ast) /* {{{ */ if (!CG(context).labels) { ALLOC_HASHTABLE(CG(context).labels); - zend_hash_init(CG(context).labels, 8, NULL, ptr_dtor, 0); + zend_hash_init(CG(context).labels, 8, NULL, label_ptr_dtor, 0); } dest.brk_cont = CG(context).current_brk_cont; @@ -3669,7 +4273,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */ opnum_jmp = zend_emit_jump(0); - zend_begin_loop(NULL); + zend_begin_loop(ZEND_NOP, NULL); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -3680,7 +4284,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */ zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start); - zend_end_loop(opnum_cond); + zend_end_loop(opnum_cond, NULL); } /* }}} */ @@ -3692,7 +4296,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */ znode cond_node; uint32_t opnum_start, opnum_cond; - zend_begin_loop(NULL); + zend_begin_loop(ZEND_NOP, NULL); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -3702,7 +4306,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */ zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start); - zend_end_loop(opnum_cond); + zend_end_loop(opnum_cond, NULL); } /* }}} */ @@ -3743,7 +4347,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */ opnum_jmp = zend_emit_jump(0); - zend_begin_loop(NULL); + zend_begin_loop(ZEND_NOP, NULL); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -3758,7 +4362,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */ zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start); - zend_end_loop(opnum_loop); + zend_end_loop(opnum_loop, NULL); } /* }}} */ @@ -3780,7 +4384,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ if (key_ast->kind == ZEND_AST_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); } - if (key_ast->kind == ZEND_AST_LIST) { + if (key_ast->kind == ZEND_AST_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); } } @@ -3802,6 +4406,8 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + zend_begin_loop(ZEND_FE_FREE, &reset_node); + opnum_fetch = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); @@ -3825,9 +4431,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_emit_assign_znode(key_ast, &key_node); } - reset_node.flag = 1; /* generate FE_FREE */ - zend_begin_loop(&reset_node); - zend_compile_stmt(stmt_ast); zend_emit_jump(opnum_fetch); @@ -3838,9 +4441,9 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opline = &CG(active_op_array)->opcodes[opnum_fetch]; opline->extended_value = get_next_op_number(CG(active_op_array)); - zend_end_loop(opnum_fetch); + zend_end_loop(opnum_fetch, &reset_node); - generate_free_loop_var(&reset_node); + opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL); } /* }}} */ @@ -3901,8 +4504,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); - expr_node.flag = 0; /* Generate normal FREE */ - zend_begin_loop(&expr_node); + zend_begin_loop(ZEND_FREE, &expr_node); case_node.op_type = IS_TMP_VAR; case_node.u.op.var = get_temporary_variable(CG(active_op_array)); @@ -3961,10 +4563,14 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_update_jump_target_to_next(opnum_default_jmp); } - zend_end_loop(get_next_op_number(CG(active_op_array))); + zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node); - if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) { - zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL); + if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) { + /* don't use emit_op() to prevent automatic live-range construction */ + opline = get_next_op(CG(active_op_array)); + opline->opcode = ZEND_FREE; + SET_NODE(opline->op1, &expr_node); + SET_UNUSED(opline->op2); } else if (expr_node.op_type == IS_CONST) { zval_dtor(&expr_node.u.constant); } @@ -3979,16 +4585,47 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ zend_ast_list *catches = zend_ast_get_list(ast->child[1]); zend_ast *finally_ast = ast->child[2]; - uint32_t i; + uint32_t i, j; zend_op *opline; - uint32_t try_catch_offset = zend_add_try_element( - get_next_op_number(CG(active_op_array))); + uint32_t try_catch_offset; uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0); + uint32_t orig_fast_call_var = CG(context).fast_call_var; + uint32_t orig_try_catch_offset = CG(context).try_catch_offset; if (catches->children == 0 && !finally_ast) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally"); } + /* label: try { } must not be equal to try { label: } */ + if (CG(context).labels) { + zend_label *label; + ZEND_HASH_REVERSE_FOREACH_PTR(CG(context).labels, label) { + if (label->opline_num == get_next_op_number(CG(active_op_array))) { + zend_emit_op(NULL, ZEND_NOP, NULL, NULL); + } + break; + } ZEND_HASH_FOREACH_END(); + } + + try_catch_offset = zend_add_try_element(get_next_op_number(CG(active_op_array))); + + if (finally_ast) { + zend_loop_var fast_call; + if (!(CG(active_op_array)->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) { + CG(active_op_array)->fn_flags |= ZEND_ACC_HAS_FINALLY_BLOCK; + } + CG(context).fast_call_var = get_temporary_variable(CG(active_op_array)); + + /* Push FAST_CALL on unwind stack */ + fast_call.opcode = ZEND_FAST_CALL; + fast_call.var_type = IS_TMP_VAR; + fast_call.var_num = CG(context).fast_call_var; + fast_call.u.try_catch_offset = try_catch_offset; + zend_stack_push(&CG(loop_var_stack), &fast_call); + } + + CG(context).try_catch_offset = try_catch_offset; + zend_compile_stmt(try_ast); if (catches->children != 0) { @@ -3997,34 +4634,53 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ for (i = 0; i < catches->children; ++i) { zend_ast *catch_ast = catches->child[i]; - zend_ast *class_ast = catch_ast->child[0]; + zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]); zend_ast *var_ast = catch_ast->child[1]; zend_ast *stmt_ast = catch_ast->child[2]; zval *var_name = zend_ast_get_zval(var_ast); zend_bool is_last_catch = (i + 1 == catches->children); + uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0); uint32_t opnum_catch; - if (!zend_is_const_default_class_ref(class_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement"); - } + CG(zend_lineno) = catch_ast->lineno; - opnum_catch = get_next_op_number(CG(active_op_array)); - if (i == 0) { - CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch; - } + for (j = 0; j < classes->children; j++) { - CG(zend_lineno) = catch_ast->lineno; + zend_ast *class_ast = classes->child[j]; + zend_bool is_last_class = (j + 1 == classes->children); - opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_CATCH; - opline->op1_type = IS_CONST; - opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), - zend_resolve_class_name_ast(class_ast)); + if (!zend_is_const_default_class_ref(class_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement"); + } - opline->op2_type = IS_CV; - opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name))); - opline->result.num = is_last_catch; + opnum_catch = get_next_op_number(CG(active_op_array)); + if (i == 0 && j == 0) { + CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch; + } + + opline = get_next_op(CG(active_op_array)); + opline->opcode = ZEND_CATCH; + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), + zend_resolve_class_name_ast(class_ast)); + + opline->op2_type = IS_CV; + opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name))); + + opline->result.num = is_last_catch && is_last_class; + + if (!is_last_class) { + jmp_multicatch[j] = zend_emit_jump(0); + opline->extended_value = get_next_op_number(CG(active_op_array)); + } + } + + for (j = 0; j < classes->children - 1; j++) { + zend_update_jump_target_to_next(jmp_multicatch[j]); + } + + efree(jmp_multicatch); zend_compile_stmt(stmt_ast); @@ -4033,7 +4689,9 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ } opline = &CG(active_op_array)->opcodes[opnum_catch]; - opline->extended_value = get_next_op_number(CG(active_op_array)); + if (!is_last_catch) { + opline->extended_value = get_next_op_number(CG(active_op_array)); + } } for (i = 0; i < catches->children; ++i) { @@ -4041,15 +4699,22 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ } if (finally_ast) { + zend_loop_var discard_exception; uint32_t opnum_jmp = get_next_op_number(CG(active_op_array)) + 1; + + /* Pop FAST_CALL from unwind stack */ + zend_stack_del_top(&CG(loop_var_stack)); - if (!(CG(active_op_array)->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) { - CG(active_op_array)->fn_flags |= ZEND_ACC_HAS_FINALLY_BLOCK; - CG(context).fast_call_var = get_temporary_variable(CG(active_op_array)); - } + /* Push DISCARD_EXCEPTION on unwind stack */ + discard_exception.opcode = ZEND_DISCARD_EXCEPTION; + discard_exception.var_type = IS_TMP_VAR; + discard_exception.var_num = CG(context).fast_call_var; + zend_stack_push(&CG(loop_var_stack), &discard_exception); + + CG(zend_lineno) = finally_ast->lineno; opline = zend_emit_op(NULL, ZEND_FAST_CALL, NULL, NULL); - opline->op1.opline_num = opnum_jmp + 1; + opline->op1.num = try_catch_offset; opline->result_type = IS_TMP_VAR; opline->result.var = CG(context).fast_call_var; @@ -4066,10 +4731,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ opline = zend_emit_op(NULL, ZEND_FAST_RET, NULL, NULL); opline->op1_type = IS_TMP_VAR; opline->op1.var = CG(context).fast_call_var; + opline->op2.num = orig_try_catch_offset; zend_update_jump_target_to_next(opnum_jmp); + + CG(context).fast_call_var = orig_fast_call_var; + + /* Pop DISCARD_EXCEPTION from unwind stack */ + zend_stack_del_top(&CG(loop_var_stack)); } + CG(context).try_catch_offset = orig_try_catch_offset; + efree(jmp_opnums); } /* }}} */ @@ -4249,6 +4922,11 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{ zend_uchar type = zend_lookup_builtin_type_by_name(class_name); if (type != 0) { + if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { + zend_error_noreturn(E_COMPILE_ERROR, + "Scalar type declaration '%s' must be unqualified", + ZSTR_VAL(zend_string_tolower(class_name))); + } arg_info->type_hint = type; } else { uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); @@ -4275,7 +4953,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ zend_arg_info *arg_infos; if (return_type_ast) { - /* Use op_array->arg_info[-1] for return type hinting */ + /* Use op_array->arg_info[-1] for return type */ arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children + 1, 0); arg_infos->name = NULL; arg_infos->pass_by_reference = (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; @@ -4284,8 +4962,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_infos->allow_null = 0; arg_infos->class_name = NULL; + if (return_type_ast->attr & ZEND_TYPE_NULLABLE) { + arg_infos->allow_null = 1; + return_type_ast->attr &= ~ZEND_TYPE_NULLABLE; + } + zend_compile_typename(return_type_ast, arg_infos); + if (arg_infos->type_hint == IS_VOID && arg_infos->allow_null) { + zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable"); + } + arg_infos++; op_array->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE; } else { @@ -4321,8 +5008,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "Redefinition of parameter $%s", ZSTR_VAL(name)); } else if (zend_string_equals_literal(name, "this")) { - if (op_array->scope && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + if ((op_array->scope || (op_array->fn_flags & ZEND_ACC_CLOSURE)) + && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter"); } op_array->this_var = var_node.u.op.var; } @@ -4371,12 +5059,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ && (Z_TYPE(default_node.u.constant) == IS_NULL || (Z_TYPE(default_node.u.constant) == IS_CONSTANT && strcasecmp(Z_STRVAL(default_node.u.constant), "NULL") == 0)); + zend_bool is_explicitly_nullable = (type_ast->attr & ZEND_TYPE_NULLABLE) == ZEND_TYPE_NULLABLE; op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; - arg_info->allow_null = has_null_default; + arg_info->allow_null = has_null_default || is_explicitly_nullable; zend_compile_typename(type_ast, arg_info); + if (arg_info->type_hint == IS_VOID) { + zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type"); + } + if (type_ast->kind == ZEND_AST_TYPE) { if (arg_info->type_hint == IS_ARRAY) { if (default_ast && !has_null_default @@ -4384,31 +5077,31 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ && !Z_CONSTANT(default_node.u.constant) ) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with array type hint can only be an array or NULL"); + "with array type can only be an array or NULL"); } } else if (arg_info->type_hint == IS_CALLABLE && default_ast) { if (!has_null_default && !Z_CONSTANT(default_node.u.constant)) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with callable type hint can only be NULL"); + "with callable type can only be NULL"); } } } else { if (default_ast && !has_null_default && !Z_CONSTANT(default_node.u.constant)) { if (arg_info->class_name) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with a class type hint can only be NULL"); + "with a class type can only be NULL"); } else switch (arg_info->type_hint) { case IS_DOUBLE: if (Z_TYPE(default_node.u.constant) != IS_DOUBLE && Z_TYPE(default_node.u.constant) != IS_LONG) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with a float type hint can only be float, integer, or NULL"); + "with a float type can only be float, integer, or NULL"); } break; default: if (!ZEND_SAME_FAKE_TYPE(arg_info->type_hint, Z_TYPE(default_node.u.constant))) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with a %s type hint can only be %s or NULL", + "with a %s type can only be %s or NULL", zend_get_type_by_const(arg_info->type_hint), zend_get_type_by_const(arg_info->type_hint)); } break; @@ -4452,23 +5145,61 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ } /* }}} */ +static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* {{{ */ +{ + zend_ast_list *list = zend_ast_get_list(uses_ast); + uint32_t i; + + for (i = 0; i < list->children; ++i) { + zend_ast *var_name_ast = list->child[i]; + zend_string *var_name = zend_ast_get_str(var_name_ast); + zend_bool by_ref = var_name_ast->attr; + zend_op *opline; + + if (zend_string_equals_literal(var_name, "this")) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable"); + } + + if (zend_is_auto_global(var_name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use auto-global as lexical variable"); + } + + opline = zend_emit_op(NULL, ZEND_BIND_LEXICAL, closure, NULL); + opline->op2_type = IS_CV; + opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(var_name)); + opline->extended_value = by_ref; + } +} +/* }}} */ + void zend_compile_closure_uses(zend_ast *ast) /* {{{ */ { + zend_op_array *op_array = CG(active_op_array); zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; for (i = 0; i < list->children; ++i) { zend_ast *var_ast = list->child[i]; - zend_string *name = zend_ast_get_str(var_ast); + zend_string *var_name = zend_ast_get_str(var_ast); zend_bool by_ref = var_ast->attr; zval zv; + ZVAL_NULL(&zv); - if (zend_string_equals_literal(name, "this")) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable"); + if (op_array->static_variables + && zend_hash_exists(op_array->static_variables, var_name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use variable $%s twice", ZSTR_VAL(var_name)); } - ZVAL_NULL(&zv); - Z_CONST_FLAGS(zv) = by_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR; + { + int i; + for (i = 0; i < op_array->last_var; i++) { + if (zend_string_equals(op_array->vars[i], var_name)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use lexical variable $%s as a parameter name", ZSTR_VAL(var_name)); + } + } + } zend_compile_static_var_common(var_ast, &zv, by_ref); } @@ -4486,7 +5217,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo zend_string *lcname; if (in_interface) { - if ((op_array->fn_flags & ZEND_ACC_PPP_MASK) != ZEND_ACC_PUBLIC) { + if (!is_public || (op_array->fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_ABSTRACT))) { zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method " "%s::%s() must be omitted", ZSTR_VAL(ce->name), ZSTR_VAL(name)); } @@ -4648,15 +5379,16 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) /* {{{ */ { zend_ast *params_ast = decl->child[0]; - zend_string *name = decl->name, *lcname; + zend_string *unqualified_name, *name, *lcname, *key; zend_op *opline; - op_array->function_name = name = zend_prefix_with_ns(name); - + unqualified_name = decl->name; + op_array->function_name = name = zend_prefix_with_ns(unqualified_name); lcname = zend_string_tolower(name); if (FC(imports_function)) { - zend_string *import_name = zend_hash_find_ptr(FC(imports_function), lcname); + zend_string *import_name = zend_hash_find_ptr_lc( + FC(imports_function), ZSTR_VAL(unqualified_name), ZSTR_LEN(unqualified_name)); if (import_name && !zend_string_equals_ci(lcname, import_name)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare function %s " "because the name is already in use", ZSTR_VAL(name)); @@ -4670,22 +5402,21 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as ZEND_AUTOLOAD_FUNC_NAME); } + key = zend_build_runtime_definition_key(lcname, decl->lex_pos); + zend_hash_update_ptr(CG(function_table), key, op_array); + if (op_array->fn_flags & ZEND_ACC_CLOSURE) { opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); + opline->op1_type = IS_CONST; + LITERAL_STR(opline->op1, key); } else { opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_DECLARE_FUNCTION; - opline->op2_type = IS_CONST; - LITERAL_STR(opline->op2, zend_string_copy(lcname)); - } - - { - zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos); - opline->op1_type = IS_CONST; - LITERAL_STR(opline->op1, key); - - zend_hash_update_ptr(CG(function_table), key, op_array); + LITERAL_STR(opline->op1, zend_string_copy(lcname)); + /* RTD key is placed after lcname literal in op1 */ + zend_add_literal_string(CG(active_op_array), &key); + SET_UNUSED(opline->op2); } zend_string_release(lcname); @@ -4723,9 +5454,13 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ zend_begin_method_decl(op_array, decl->name, has_body); } else { zend_begin_func_decl(result, op_array, decl); + if (uses_ast) { + zend_compile_closure_binding(result, uses_ast); + } } CG(active_op_array) = op_array; + zend_oparray_context_begin(&orig_oparray_context); if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) { @@ -4735,13 +5470,17 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ { /* Push a separator to the loop variable stack */ - znode dummy_var; - dummy_var.op_type = IS_UNUSED; + zend_loop_var dummy_var; + dummy_var.opcode = ZEND_RETURN; zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var); } zend_compile_params(params_ast, return_type_ast); + if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { + zend_mark_function_as_generator(); + zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL); + } if (uses_ast) { zend_compile_closure_uses(uses_ast); } @@ -4752,8 +5491,11 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ CG(active_class_entry), (zend_function *) op_array, E_COMPILE_ERROR); } + /* put the implicit return on the really last line */ + CG(zend_lineno) = decl->end_lineno; + zend_do_extended_info(); - zend_emit_final_return(NULL); + zend_emit_final_return(0); pass_two(CG(active_op_array)); zend_oparray_context_end(&orig_oparray_context); @@ -4771,7 +5513,6 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */ uint32_t flags = list->attr; zend_class_entry *ce = CG(active_class_entry); uint32_t i, children = list->children; - zend_string *doc_comment = NULL; if (ce->ce_flags & ZEND_ACC_INTERFACE) { zend_error_noreturn(E_COMPILE_ERROR, "Interfaces may not include member variables"); @@ -4781,19 +5522,20 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "Properties cannot be declared abstract"); } - /* Doc comment has been appended as last element in property list */ - if (list->child[children - 1]->kind == ZEND_AST_ZVAL) { - doc_comment = zend_string_copy(zend_ast_get_str(list->child[children - 1])); - children -= 1; - } - for (i = 0; i < children; ++i) { zend_ast *prop_ast = list->child[i]; zend_ast *name_ast = prop_ast->child[0]; zend_ast *value_ast = prop_ast->child[1]; + zend_ast *doc_comment_ast = prop_ast->child[2]; zend_string *name = zend_ast_get_str(name_ast); + zend_string *doc_comment = NULL; zval value_zv; + /* Doc comment has been appended as last element in ZEND_AST_PROP_ELEM ast */ + if (doc_comment_ast) { + doc_comment = zend_string_copy(zend_ast_get_str(doc_comment_ast)); + } + if (flags & ZEND_ACC_FINAL) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, " "the final modifier is allowed only for methods and classes", @@ -4813,9 +5555,6 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */ name = zend_new_interned_string_safe(name); zend_declare_property_ex(ce, name, &value_zv, flags, doc_comment); - - /* Doc comment is only assigned to first property */ - doc_comment = NULL; } } /* }}} */ @@ -4835,25 +5574,25 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; zend_ast *value_ast = const_ast->child[1]; + zend_ast *doc_comment_ast = const_ast->child[2]; zend_string *name = zend_ast_get_str(name_ast); + zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; zval value_zv; - if (zend_string_equals_literal_ci(name, "class")) { - zend_error(E_COMPILE_ERROR, - "A class constant must not be called 'class'; it is reserved for class name fetching"); + if (UNEXPECTED(ast->attr & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) { + if (ast->attr & ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'static' as constant modifier"); + } else if (ast->attr & ZEND_ACC_ABSTRACT) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'abstract' as constant modifier"); + } else if (ast->attr & ZEND_ACC_FINAL) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'final' as constant modifier"); + } } zend_const_expr_to_zval(&value_zv, value_ast); name = zend_new_interned_string_safe(name); - if (zend_hash_add(&ce->constants_table, name, &value_zv) == NULL) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot redefine class constant %s::%s", - ZSTR_VAL(ce->name), ZSTR_VAL(name)); - } - - if (Z_CONSTANT(value_zv)) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } + zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment); } } /* }}} */ @@ -5038,7 +5777,7 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ zend_ast *extends_ast = decl->child[0]; zend_ast *implements_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; - zend_string *name, *lcname, *import_name = NULL; + zend_string *name, *lcname; zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); zend_op *opline; znode declare_node, extends_node; @@ -5047,35 +5786,30 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ znode original_implementing_class = FC(implementing_class); if (EXPECTED((decl->flags & ZEND_ACC_ANON_CLASS) == 0)) { + zend_string *unqualified_name = decl->name; + if (CG(active_class_entry)) { zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); } - name = decl->name; - zend_assert_valid_class_name(name); - lcname = zend_string_tolower(name); - if (FC(current_namespace)) { - name = zend_prefix_with_ns(name); - zend_string_release(lcname); - lcname = zend_string_tolower(name); - } else { - zend_string_addref(name); - } + zend_assert_valid_class_name(unqualified_name); + name = zend_prefix_with_ns(unqualified_name); + name = zend_new_interned_string(name); + lcname = zend_string_tolower(name); if (FC(imports)) { - import_name = zend_hash_find_ptr(FC(imports), lcname); - } - - if (import_name && !zend_string_equals_ci(lcname, import_name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " - "because the name is already in use", ZSTR_VAL(name)); + zend_string *import_name = zend_hash_find_ptr_lc( + FC(imports), ZSTR_VAL(unqualified_name), ZSTR_LEN(unqualified_name)); + if (import_name && !zend_string_equals_ci(lcname, import_name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " + "because the name is already in use", ZSTR_VAL(name)); + } } - - name = zend_new_interned_string(name); - lcname = zend_new_interned_string(lcname); } else { - lcname = name = zend_generate_anon_class_name(decl->lex_pos); + name = zend_generate_anon_class_name(decl->lex_pos); + lcname = zend_string_tolower(name); } + lcname = zend_new_interned_string(lcname); ce->type = ZEND_USER_CLASS; ce->name = name; @@ -5109,49 +5843,45 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ opline = get_next_op(CG(active_op_array)); zend_make_var_result(&declare_node, opline); - // TODO.AST drop this GET_NODE(&FC(implementing_class), opline->result); - opline->op2_type = IS_CONST; - LITERAL_STR(opline->op2, lcname); + opline->op1_type = IS_CONST; + LITERAL_STR(opline->op1, lcname); if (decl->flags & ZEND_ACC_ANON_CLASS) { if (extends_ast) { opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS; - opline->extended_value = extends_node.u.op.var; + SET_NODE(opline->op2, &extends_node); } else { opline->opcode = ZEND_DECLARE_ANON_CLASS; } - opline->op1_type = IS_UNUSED; - zend_hash_update_ptr(CG(class_table), lcname, ce); } else { zend_string *key; if (extends_ast) { opline->opcode = ZEND_DECLARE_INHERITED_CLASS; - opline->extended_value = extends_node.u.op.var; + SET_NODE(opline->op2, &extends_node); } else { opline->opcode = ZEND_DECLARE_CLASS; + SET_UNUSED(opline->op2); } key = zend_build_runtime_definition_key(lcname, decl->lex_pos); - - opline->op1_type = IS_CONST; - LITERAL_STR(opline->op1, key); + /* RTD key is placed after lcname literal in op1 */ + zend_add_literal_string(CG(active_op_array), &key); zend_hash_update_ptr(CG(class_table), key, ce); } CG(active_class_entry) = ce; - if (implements_ast) { - zend_compile_implements(&declare_node, implements_ast); - } - zend_compile_stmt(stmt_ast); + /* Reset lineno for final opcodes and errors */ + CG(zend_lineno) = ast->lineno; + if (ce->num_traits == 0) { /* For traits this check is delayed until after trait binding */ zend_check_deprecated_constructor(ce); @@ -5203,11 +5933,15 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ zend_emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL); } + if (implements_ast) { + zend_compile_implements(&declare_node, implements_ast); + } + if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) - && (extends_ast || ce->num_interfaces > 0) + && (extends_ast || implements_ast) ) { zend_verify_abstract_class(ce); - if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { + if (implements_ast) { zend_emit_op(NULL, ZEND_VERIFY_ABSTRACT_CLASS, &declare_node, NULL); } } @@ -5418,26 +6152,26 @@ void zend_compile_const_decl(zend_ast *ast) /* {{{ */ zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; zend_ast *value_ast = const_ast->child[1]; - zend_string *name = zend_ast_get_str(name_ast); + zend_string *unqualified_name = zend_ast_get_str(name_ast); - zend_string *import_name; + zend_string *name; znode name_node, value_node; zval *value_zv = &value_node.u.constant; value_node.op_type = IS_CONST; zend_const_expr_to_zval(value_zv, value_ast); - if (zend_lookup_reserved_const(ZSTR_VAL(name), ZSTR_LEN(name))) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare constant '%s'", ZSTR_VAL(name)); + if (zend_lookup_reserved_const(ZSTR_VAL(unqualified_name), ZSTR_LEN(unqualified_name))) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot redeclare constant '%s'", ZSTR_VAL(unqualified_name)); } - name = zend_prefix_with_ns(name); + name = zend_prefix_with_ns(unqualified_name); name = zend_new_interned_string(name); - if (FC(imports_const) - && (import_name = zend_hash_find_ptr(FC(imports_const), name)) - ) { - if (!zend_string_equals(import_name, name)) { + if (FC(imports_const)) { + zend_string *import_name = zend_hash_find_ptr(FC(imports_const), unqualified_name); + if (import_name && !zend_string_equals(import_name, name)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare const %s because " "the name is already in use", ZSTR_VAL(name)); } @@ -5491,7 +6225,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */ } if (num > 0) { zend_error_noreturn(E_COMPILE_ERROR, "Namespace declaration statement has to be " - "the very first statement in the script"); + "the very first statement or after any declare call in the script"); } } @@ -5568,9 +6302,9 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ if (strcmp(ZSTR_VAL(dirname), ".") == 0) { dirname = zend_string_extend(dirname, MAXPATHLEN, 0); #if HAVE_GETCWD - VCWD_GETCWD(ZSTR_VAL(dirname), MAXPATHLEN); + ZEND_IGNORE_VALUE(VCWD_GETCWD(ZSTR_VAL(dirname), MAXPATHLEN)); #elif HAVE_GETWD - VCWD_GETWD(ZSTR_VAL(dirname)); + ZEND_IGNORE_VALUE(VCWD_GETWD(ZSTR_VAL(dirname))); #endif } @@ -5586,7 +6320,9 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ } break; case T_METHOD_C: - if (ce) { + if ((op_array && !op_array->scope && op_array->function_name) || (op_array->fn_flags & ZEND_ACC_CLOSURE)) { + ZVAL_STR_COPY(zv, op_array->function_name); + } else if (ce) { if (op_array && op_array->function_name) { ZVAL_NEW_STR(zv, zend_concat3(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), "::", 2, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name))); @@ -5631,14 +6367,50 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ } /* }}} */ +ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */ +{ + if (!(opcode == ZEND_ADD || opcode == ZEND_SUB || opcode == ZEND_MUL || opcode == ZEND_DIV + || opcode == ZEND_POW || opcode == ZEND_MOD || opcode == ZEND_SL || opcode == ZEND_SR + || opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)) { + return 0; + } + + /* While basic arithmetic operators always produce numeric string errors, + * bitwise operators don't produce errors if both operands are strings */ + if ((opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR) + && Z_TYPE_P(op1) == IS_STRING && Z_TYPE_P(op2) == IS_STRING) { + return 0; + } + + if (Z_TYPE_P(op1) == IS_STRING + && !is_numeric_string(Z_STRVAL_P(op1), Z_STRLEN_P(op1), NULL, NULL, 0)) { + return 1; + } + + if (Z_TYPE_P(op2) == IS_STRING + && !is_numeric_string(Z_STRVAL_P(op2), Z_STRLEN_P(op2), NULL, NULL, 0)) { + return 1; + } + + return 0; +} +/* }}} */ + static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */ { binary_op_type fn = get_binary_op(opcode); /* don't evaluate division by zero at compile-time */ - if (opcode == ZEND_MOD && zval_get_long(op2) == 0) { + if ((opcode == ZEND_DIV || opcode == ZEND_MOD) && + zval_get_long(op2) == 0) { return 0; - } else if ((opcode == ZEND_SL || opcode == ZEND_SR) && zval_get_long(op2) < 0) { + } else if ((opcode == ZEND_SL || opcode == ZEND_SR) && + zval_get_long(op2) < 0) { + return 0; + } + + /* don't evaluate numeric string error-producing operations at compile-time */ + if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) { return 0; } @@ -5654,14 +6426,11 @@ static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op } /* }}} */ -static inline void zend_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ +static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ { - binary_op_type fn = kind == ZEND_AST_UNARY_PLUS - ? add_function : sub_function; - zval left; - ZVAL_LONG(&left, 0); - fn(result, &left, op); + ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1); + return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op); } /* }}} */ @@ -5679,14 +6448,22 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */ uint32_t i; zend_bool is_constant = 1; + if (ast->attr) { + zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression"); + } + /* First ensure that *all* child nodes are constant and by-val */ for (i = 0; i < list->children; ++i) { zend_ast *elem_ast = list->child[i]; - zend_bool by_ref = elem_ast->attr; + + if (elem_ast == NULL) { + zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays"); + } + zend_eval_const_expr(&elem_ast->child[0]); zend_eval_const_expr(&elem_ast->child[1]); - if (by_ref || elem_ast->child[0]->kind != ZEND_AST_ZVAL + if (elem_ast->attr /* by_ref */ || elem_ast->child[0]->kind != ZEND_AST_ZVAL || (elem_ast->child[1] && elem_ast->child[1]->kind != ZEND_AST_ZVAL) ) { is_constant = 0; @@ -5851,24 +6628,24 @@ void zend_compile_unary_op(znode *result, zend_ast *ast) /* {{{ */ void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; - znode zero_node, expr_node; + znode expr_node; + znode lefthand_node; ZEND_ASSERT(ast->kind == ZEND_AST_UNARY_PLUS || ast->kind == ZEND_AST_UNARY_MINUS); zend_compile_expr(&expr_node, expr_ast); if (expr_node.op_type == IS_CONST) { - result->op_type = IS_CONST; - zend_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant); - zval_ptr_dtor(&expr_node.u.constant); - return; + if (zend_try_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant)) { + result->op_type = IS_CONST; + zval_ptr_dtor(&expr_node.u.constant); + return; + } } - zero_node.op_type = IS_CONST; - ZVAL_LONG(&zero_node.u.constant, 0); - - zend_emit_op_tmp(result, ast->kind == ZEND_AST_UNARY_PLUS ? ZEND_ADD : ZEND_SUB, - &zero_node, &expr_node); + lefthand_node.op_type = IS_CONST; + ZVAL_LONG(&lefthand_node.u.constant, (ast->kind == ZEND_AST_UNARY_PLUS) ? 1 : -1); + zend_emit_op_tmp(result, ZEND_MUL, &lefthand_node, &expr_node); } /* }}} */ @@ -5899,7 +6676,7 @@ void zend_compile_short_circuiting(znode *result, zend_ast *ast) /* {{{ */ zval_ptr_dtor(&right_node.u.constant); } else { - zend_emit_op(result, ZEND_BOOL, &right_node, NULL); + zend_emit_op_tmp(result, ZEND_BOOL, &right_node, NULL); } } @@ -5986,7 +6763,7 @@ static void zend_compile_shorthand_conditional(znode *result, zend_ast *ast) /* zend_ast *false_ast = ast->child[2]; znode cond_node, false_node; - zend_op *opline_jmp_set, *opline_qm_assign; + zend_op *opline_qm_assign; uint32_t opnum_jmp_set; ZEND_ASSERT(ast->child[1] == NULL); @@ -5998,10 +6775,10 @@ static void zend_compile_shorthand_conditional(znode *result, zend_ast *ast) /* zend_compile_expr(&false_node, false_ast); - opline_jmp_set = &CG(active_op_array)->opcodes[opnum_jmp_set]; - opline_jmp_set->op2.opline_num = get_next_op_number(CG(active_op_array)) + 1; opline_qm_assign = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &false_node, NULL); SET_NODE(opline_qm_assign->result, result); + + zend_update_jump_target_to_next(opnum_jmp_set); } /* }}} */ @@ -6012,8 +6789,8 @@ void zend_compile_conditional(znode *result, zend_ast *ast) /* {{{ */ zend_ast *false_ast = ast->child[2]; znode cond_node, true_node, false_node; - zend_op *opline_qm_assign1, *opline_qm_assign2; - uint32_t opnum_jmpz, opnum_jmp, opnum_qm_assign1; + zend_op *opline_qm_assign2; + uint32_t opnum_jmpz, opnum_jmp; if (!true_ast) { zend_compile_shorthand_conditional(result, ast); @@ -6026,7 +6803,6 @@ void zend_compile_conditional(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(&true_node, true_ast); - opnum_qm_assign1 = get_next_op_number(CG(active_op_array)); zend_emit_op_tmp(result, ZEND_QM_ASSIGN, &true_node, NULL); opnum_jmp = zend_emit_jump(0); @@ -6035,8 +6811,7 @@ void zend_compile_conditional(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(&false_node, false_ast); - opline_qm_assign1 = &CG(active_op_array)->opcodes[opnum_qm_assign1]; - opline_qm_assign2 = zend_emit_op(NULL, opline_qm_assign1->opcode, &false_node, NULL); + opline_qm_assign2 = zend_emit_op(NULL, ZEND_QM_ASSIGN, &false_node, NULL); SET_NODE(opline_qm_assign2->result, result); zend_update_jump_target_to_next(opnum_jmp); @@ -6069,12 +6844,14 @@ void zend_compile_coalesce(znode *result, zend_ast *ast) /* {{{ */ void zend_compile_print(znode *result, zend_ast *ast) /* {{{ */ { + zend_op *opline; zend_ast *expr_ast = ast->child[0]; znode expr_node; zend_compile_expr(&expr_node, expr_ast); - zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); + opline = zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); + opline->extended_value = 1; result->op_type = IS_CONST; ZVAL_LONG(&result->u.constant, 1); @@ -6098,32 +6875,6 @@ void zend_compile_exit(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ -static void zend_mark_function_as_generator() /* {{{ */ -{ - if (!CG(active_op_array)->function_name) { - zend_error_noreturn(E_COMPILE_ERROR, - "The \"yield\" expression can only be used inside a function"); - } - if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - const char *msg = "Generators may only declare a return type of Generator, Iterator or Traversable, %s is not permitted"; - if (!CG(active_op_array)->arg_info[-1].class_name) { - zend_error_noreturn(E_COMPILE_ERROR, msg, - zend_get_type_by_const(CG(active_op_array)->arg_info[-1].type_hint)); - } - if (!(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Traversable")-1 - && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Traversable")-1, "Traversable", sizeof("Traversable")-1) == 0) && - !(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Iterator")-1 - && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Iterator")-1, "Iterator", sizeof("Iterator")-1) == 0) && - !(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Generator")-1 - && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Generator")-1, "Generator", sizeof("Generator")-1) == 0)) { - zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name)); - } - } - - CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; -} -/* }}} */ - void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *value_ast = ast->child[0]; @@ -6143,7 +6894,7 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */ if (value_ast) { if (returns_by_ref && zend_is_variable(value_ast) && !zend_is_call(value_ast)) { - zend_compile_var(&value_node, value_ast, BP_VAR_REF); + zend_compile_var(&value_node, value_ast, BP_VAR_W); } else { zend_compile_expr(&value_node, value_ast); } @@ -6165,6 +6916,11 @@ void zend_compile_yield_from(znode *result, zend_ast *ast) /* {{{ */ zend_mark_function_as_generator(); + if (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use \"yield from\" inside a by-reference generator"); + } + zend_compile_expr(&expr_node, expr_ast); zend_emit_op_tmp(result, ZEND_YIELD_FROM, &expr_node, NULL); } @@ -6184,13 +6940,8 @@ void zend_compile_instanceof(znode *result, zend_ast *ast) /* {{{ */ "instanceof expects an object instance, constant given"); } - if (zend_is_const_default_class_ref(class_ast)) { - class_node.op_type = IS_CONST; - ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast)); - } else { - opline = zend_compile_class_ref(&class_node, class_ast, 0); - opline->extended_value |= ZEND_FETCH_CLASS_NO_AUTOLOAD; - } + zend_compile_class_ref_ex(&class_node, class_ast, + ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_EXCEPTION); opline = zend_emit_op_tmp(result, ZEND_INSTANCEOF, &obj_node, NULL); @@ -6262,7 +7013,7 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */ break; case ZEND_AST_STATIC_PROP: opline = zend_compile_static_prop_common(result, var_ast, BP_VAR_IS, 0); - opline->opcode = ZEND_ISSET_ISEMPTY_VAR; + opline->opcode = ZEND_ISSET_ISEMPTY_STATIC_PROP; break; EMPTY_SWITCH_DEFAULT_CASE() } @@ -6276,10 +7027,9 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; znode silence_node; - uint32_t begin_opline_num, end_opline_num; - zend_brk_cont_element *brk_cont_element; + uint32_t range; - begin_opline_num = get_next_op_number(CG(active_op_array)); + range = zend_start_live_range(CG(active_op_array), get_next_op_number(CG(active_op_array))); zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL); if (expr_ast->kind == ZEND_AST_VAR) { @@ -6290,15 +7040,12 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(result, expr_ast); } - end_opline_num = get_next_op_number(CG(active_op_array)); - zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL); - /* Store BEGIN_SILENCE/END_SILENCE pair to restore previous * EG(error_reporting) value on exception */ - brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); - brk_cont_element->start = begin_opline_num; - brk_cont_element->cont = brk_cont_element->brk = end_opline_num; - brk_cont_element->parent = -1; + zend_end_live_range(CG(active_op_array), range, get_next_op_number(CG(active_op_array)), + ZEND_LIVE_SILENCE, silence_node.u.op.var); + + zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL); } /* }}} */ @@ -6334,9 +7081,16 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ for (i = 0; i < list->children; ++i) { zend_ast *elem_ast = list->child[i]; - zend_ast *value_ast = elem_ast->child[0]; - zend_ast *key_ast = elem_ast->child[1]; - zend_bool by_ref = elem_ast->attr; + zend_ast *value_ast, *key_ast; + zend_bool by_ref; + + if (elem_ast == NULL) { + zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays"); + } + + value_ast = elem_ast->child[0]; + key_ast = elem_ast->child[1]; + by_ref = elem_ast->attr; znode value_node, key_node, *key_node_ptr = NULL; @@ -6376,7 +7130,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ /* Add a flag to INIT_ARRAY if we know this array cannot be packed */ if (!packed) { - ZEND_ASSERT(opnum_init != -1); + ZEND_ASSERT(opnum_init != (uint32_t)-1); opline = &CG(active_op_array)->opcodes[opnum_init]; opline->extended_value |= ZEND_ARRAY_NOT_PACKED; } @@ -6391,16 +7145,9 @@ void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */ zend_bool is_fully_qualified; zend_string *orig_name = zend_ast_get_str(name_ast); - zend_string *resolved_name = zend_resolve_const_name( - orig_name, name_ast->attr, &is_fully_qualified); + zend_string *resolved_name = zend_resolve_const_name(orig_name, name_ast->attr, &is_fully_qualified); - if (zend_try_ct_eval_const(&result->u.constant, resolved_name, is_fully_qualified)) { - result->op_type = IS_CONST; - zend_string_release(resolved_name); - return; - } - - if (zend_string_equals_literal(resolved_name, "__COMPILER_HALT_OFFSET__")) { + if (zend_string_equals_literal(resolved_name, "__COMPILER_HALT_OFFSET__") || (name_ast->attr != ZEND_NAME_RELATIVE && zend_string_equals_literal(orig_name, "__COMPILER_HALT_OFFSET__"))) { zend_ast *last = CG(ast); while (last->kind == ZEND_AST_STMT_LIST) { @@ -6409,13 +7156,18 @@ void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */ } if (last->kind == ZEND_AST_HALT_COMPILER) { result->op_type = IS_CONST; - ZVAL_LONG(&result->u.constant, - Z_LVAL_P(zend_ast_get_zval(last->child[0]))); + ZVAL_LONG(&result->u.constant, Z_LVAL_P(zend_ast_get_zval(last->child[0]))); zend_string_release(resolved_name); return; } } + if (zend_try_ct_eval_const(&result->u.constant, resolved_name, is_fully_qualified)) { + result->op_type = IS_CONST; + zend_string_release(resolved_name); + return; + } + opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL); opline->op2_type = IS_CONST; @@ -6444,7 +7196,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ znode class_node, const_node; zend_op *opline; - zend_string *resolved_name; if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) { if (Z_TYPE(result->u.constant) == IS_NULL) { @@ -6460,31 +7211,26 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ zend_eval_const_expr(&const_ast); if (class_ast->kind == ZEND_AST_ZVAL) { + zend_string *resolved_name; + resolved_name = zend_resolve_class_name_ast(class_ast); if (const_ast->kind == ZEND_AST_ZVAL && zend_try_ct_eval_class_const(&result->u.constant, resolved_name, zend_ast_get_str(const_ast))) { result->op_type = IS_CONST; zend_string_release(resolved_name); return; } + zend_string_release(resolved_name); } if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) { zend_error_noreturn(E_COMPILE_ERROR, "Dynamic class names are not allowed in compile-time ::class fetch"); } - if (zend_is_const_default_class_ref(class_ast)) { - class_node.op_type = IS_CONST; - ZVAL_STR(&class_node.u.constant, resolved_name); - } else { - if (class_ast->kind == ZEND_AST_ZVAL) { - zend_string_release(resolved_name); - } - zend_compile_class_ref(&class_node, class_ast, 1); - } + zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); zend_compile_expr(&const_node, const_ast); - opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, &const_node); + opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_CONSTANT, NULL, &const_node); zend_set_class_name_op1(opline, &class_node); @@ -6626,10 +7372,7 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ GET_NODE(result, opline->result); } else { uint32_t var; - zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array)); - info->start = rope_init_lineno; - info->parent = CG(context).current_brk_cont; - info->cont = info->brk = opline - CG(active_op_array)->opcodes; + uint32_t range = zend_start_live_range(CG(active_op_array), rope_init_lineno); init_opline->extended_value = j; opline->opcode = ZEND_ROPE_END; @@ -6643,15 +7386,19 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ get_temporary_variable(CG(active_op_array)); i--; } + + zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes, + ZEND_LIVE_ROPE, var); + /* Update all the previous opcodes to use the same variable */ while (opline != init_opline) { opline--; if (opline->opcode == ZEND_ROPE_ADD && - opline->result.var == -1) { + opline->result.var == (uint32_t)-1) { opline->op1.var = var; opline->result.var = var; } else if (opline->opcode == ZEND_ROPE_INIT && - opline->result.var == -1) { + opline->result.var == (uint32_t)-1) { opline->result.var = var; } } @@ -6687,7 +7434,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM || kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST - || kind == ZEND_AST_MAGIC_CONST; + || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE; } /* }}} */ @@ -6821,14 +7568,16 @@ void zend_const_expr_to_zval(zval *result, zend_ast *ast) /* {{{ */ zend_compile_const_expr(&ast); if (ast->kind == ZEND_AST_ZVAL) { ZVAL_COPY_VALUE(result, zend_ast_get_zval(ast)); - - /* Kill this branch of the original AST, as it was already destroyed. - * It would be nice to find a better solution to this problem in the - * future. */ - orig_ast->kind = 0; } else { ZVAL_NEW_AST(result, zend_ast_copy(ast)); + /* destroy the ast here, it might have been replaced */ + zend_ast_destroy(ast); } + + /* Kill this branch of the original AST, as it was already destroyed. + * It would be nice to find a better solution to this problem in the + * future. */ + orig_ast->kind = 0; } /* }}} */ @@ -6868,6 +7617,10 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ CG(zend_lineno) = ast->lineno; + if ((CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) && !zend_is_unticked_stmt(ast)) { + zend_do_extended_info(); + } + switch (ast->kind) { case ZEND_AST_STMT_LIST: zend_compile_stmt_list(ast); @@ -7121,9 +7874,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ *result = *zend_ast_get_znode(ast); return; default: - if (type == BP_VAR_W || type == BP_VAR_REF - || type == BP_VAR_RW || type == BP_VAR_UNSET - ) { + if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use temporary expression in write context"); } @@ -7233,8 +7984,34 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ return; } - zend_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0])); + if (!zend_try_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0]))) { + return; + } break; + case ZEND_AST_COALESCE: + /* Set isset fetch indicator here, opcache disallows runtime altering of the AST */ + if (ast->child[0]->kind == ZEND_AST_DIM) { + ast->child[0]->attr = ZEND_DIM_IS; + } + zend_eval_const_expr(&ast->child[0]); + + if (ast->child[0]->kind != ZEND_AST_ZVAL) { + /* ensure everything was compile-time evaluated at least once */ + zend_eval_const_expr(&ast->child[1]); + return; + } + + if (Z_TYPE_P(zend_ast_get_zval(ast->child[0])) == IS_NULL) { + zend_eval_const_expr(&ast->child[1]); + *ast_ptr = ast->child[1]; + ast->child[1] = NULL; + zend_ast_destroy(ast); + } else { + *ast_ptr = ast->child[0]; + ast->child[0] = NULL; + zend_ast_destroy(ast); + } + return; case ZEND_AST_CONDITIONAL: { zend_ast **child, *child_ast; @@ -7262,9 +8039,17 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_DIM: { /* constant expression should be always read context ... */ - zval *container, *dim; + if (ast->child[1] == NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading"); + } + + /* Set isset fetch indicator here, opcache disallows runtime altering of the AST */ + if (ast->attr == ZEND_DIM_IS && ast->child[0]->kind == ZEND_AST_DIM) { + ast->child[0]->attr = ZEND_DIM_IS; + } + zend_eval_const_expr(&ast->child[0]); zend_eval_const_expr(&ast->child[1]); if (ast->child[0]->kind != ZEND_AST_ZVAL || ast->child[1]->kind != ZEND_AST_ZVAL) { @@ -7301,7 +8086,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ } else if (Z_TYPE_P(dim) != IS_STRING || is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, 1) != IS_LONG) { return; } - if (offset < 0 || offset >= Z_STRLEN_P(container)) { + if (offset < 0 || (size_t)offset >= Z_STRLEN_P(container)) { return; } c = (zend_uchar) Z_STRVAL_P(container)[offset]; @@ -7350,10 +8135,13 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, name_ast, 0)) { if (Z_TYPE(result) == IS_NULL) { + if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) { + zend_ast_destroy(ast); + *ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C); + } return; - } else { - break; } + break; } zend_eval_const_expr(&class_ast); |