diff options
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 195 |
1 files changed, 120 insertions, 75 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 62fa76b5b1..03ab7d177e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -667,7 +667,7 @@ static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32 { zend_live_range *range = op_array->live_range + offset; - if (range->start == end && offset == op_array->last_live_range - 1) { + if (range->start == end && offset == (uint32_t)op_array->last_live_range - 1) { op_array->last_live_range--; } else { range->end = end; @@ -758,6 +758,9 @@ void zend_do_free(znode *op1) /* {{{ */ additional FREE opcode and simplify the FETCH handlers their selves */ zend_emit_op(NULL, ZEND_FREE, op1, NULL); + } else if (opline->opcode == ZEND_FETCH_THIS) { + opline->opcode = ZEND_NOP; + opline->result_type = IS_UNUSED; } else { opline->result_type = IS_UNUSED; } @@ -1150,7 +1153,7 @@ 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, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname)), Z_STRVAL_P(lcname)); + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname))); } return NULL; } @@ -1933,6 +1936,10 @@ 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; + if (opline->opcode == ZEND_FETCH_THIS) { + return; + } + switch (type & BP_VAR_MASK) { case BP_VAR_R: return; @@ -2269,12 +2276,19 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */ } /* }}} */ -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) { - zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value"); + 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; @@ -2283,6 +2297,17 @@ static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) if (return_info->type_hint != IS_UNDEF) { 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) @@ -2316,8 +2341,9 @@ void zend_emit_final_return(int return_one) /* {{{ */ 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; @@ -2513,9 +2539,6 @@ static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ /* lookup_cv may be using another zend_string instance */ name = CG(active_op_array)->vars[EX_VAR_TO_NUM(result->u.op.var)]; - if (zend_string_equals_literal(name, "this")) { - CG(active_op_array)->this_var = result->u.op.var; - } return SUCCESS; } @@ -2546,22 +2569,31 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint opline->extended_value = ZEND_FETCH_GLOBAL; } else { opline->extended_value = ZEND_FETCH_LOCAL; - /* there is a chance someone is accessing $this */ - if (ast->kind != ZEND_AST_ZVAL - && CG(active_op_array)->scope && CG(active_op_array)->this_var == (uint32_t)-1 - ) { - zend_string *key = CG(known_strings)[ZEND_STR_THIS]; - CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key); - } } return opline; } /* }}} */ +static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */ +{ + if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { + zval *name = zend_ast_get_zval(ast->child[0]); + return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this"); + } + + return 0; +} +/* }}} */ + static void zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */ { - if (zend_try_compile_cv(result, ast) == FAILURE) { + zend_op *opline; + + if (is_this_fetch(ast)) { + opline = zend_emit_op(result, ZEND_FETCH_THIS, NULL, NULL); + zend_adjust_for_fetch_type(opline, type); + } else if (zend_try_compile_cv(result, ast) == FAILURE) { zend_op *opline = zend_compile_simple_var_no_cv(result, ast, type, delayed); zend_adjust_for_fetch_type(opline, type); } @@ -2642,17 +2674,6 @@ void zend_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ } /* }}} */ -static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */ -{ - if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { - zval *name = zend_ast_get_zval(ast->child[0]); - return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this"); - } - - return 0; -} -/* }}} */ - static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ { zend_ast *obj_ast = ast->child[0]; @@ -2782,9 +2803,10 @@ static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_no zend_emit_assign_znode(var_ast, &fetch_result); } - if (!has_elems) { + if (has_elems == 0) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use empty list"); } + } /* }}} */ @@ -2802,6 +2824,10 @@ static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference"); } + if (key_ast == NULL) { + zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments"); + } + zend_compile_expr(&dim_node, key_ast); if (expr_node->op_type == IS_CONST) { @@ -2812,10 +2838,6 @@ static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node 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); @@ -2950,7 +2972,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ offset = zend_delayed_compile_begin(); zend_delayed_compile_dim(result, var_ast, BP_VAR_W); - if (zend_is_assign_to_self(var_ast, expr_ast)) { + if (zend_is_assign_to_self(var_ast, expr_ast) + && !is_this_fetch(expr_ast)) { /* $a[0] = $a should evaluate the right $a first */ zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0); } else { @@ -3177,9 +3200,9 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* {{{ */ { - if (fbc && init_op->opcode == ZEND_INIT_FCALL) { + if (fbc) { if (fbc->type == ZEND_INTERNAL_FUNCTION) { - if (!zend_execute_internal) { + if (init_op->opcode == ZEND_INIT_FCALL && !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 { @@ -3187,12 +3210,8 @@ ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc) } } } else { - if (zend_execute_ex == execute_ex) { - if (!(fbc->common.fn_flags & ZEND_ACC_GENERATOR)) { - return ZEND_DO_UCALL; - } else { - return ZEND_DO_FCALL_BY_NAME; - } + if (zend_execute_ex == execute_ex && !(fbc->common.fn_flags & ZEND_ACC_ABSTRACT)) { + return ZEND_DO_UCALL; } } } else if (zend_execute_ex == execute_ex && @@ -3504,19 +3523,9 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam zend_ast *arg_ast = args->child[i]; znode arg_node; zend_op *opline; - zend_bool send_user = 0; - - if (zend_is_variable(arg_ast) && !zend_is_call(arg_ast)) { - zend_compile_var(&arg_node, arg_ast, BP_VAR_FUNC_ARG | (i << BP_VAR_SHIFT)); - send_user = 1; - } else { - zend_compile_expr(&arg_node, arg_ast); - if (arg_node.op_type & (IS_VAR|IS_CV)) { - send_user = 1; - } - } - if (send_user) { + zend_compile_expr(&arg_node, arg_ast); + if (arg_node.op_type & (IS_VAR|IS_CV)) { opline = zend_emit_op(NULL, ZEND_SEND_USER, &arg_node, NULL); } else { opline = zend_emit_op(NULL, ZEND_SEND_VAL, &arg_node, NULL); @@ -3888,7 +3897,9 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */ convert_to_string(&name_node.u.constant); } - if (zend_try_compile_cv(&result, var_ast) == SUCCESS) { + if (is_this_fetch(var_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable"); + } else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) { zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node); zend_alloc_cache_slot(opline->op2.constant); } else { @@ -3933,6 +3944,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); + if (zend_string_equals_literal(Z_STR(var_node.u.constant), "this")) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as static variable"); + } + 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))); @@ -3966,7 +3981,9 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */ switch (var_ast->kind) { case ZEND_AST_VAR: - if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { + if (is_this_fetch(var_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot unset $this"); + } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL); opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET; } else { @@ -3991,7 +4008,7 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */ } /* }}} */ -static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */ +static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value) /* {{{ */ { zend_loop_var *base; zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack)); @@ -4008,7 +4025,11 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */ opline->result_type = IS_TMP_VAR; opline->result.var = loop_var->var_num; SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); + 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)); @@ -4042,9 +4063,9 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */ } /* }}} */ -static int zend_handle_loops_and_finally(void) /* {{{ */ +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 zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1, return_value); } /* }}} */ @@ -4067,10 +4088,11 @@ void zend_compile_return(zend_ast *ast) /* {{{ */ /* 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(); + 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); @@ -4140,7 +4162,7 @@ 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 { - if (!zend_handle_loops_and_finally_ex(depth)) { + 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"); @@ -4223,7 +4245,7 @@ void zend_compile_goto(zend_ast *ast) /* {{{ */ zend_compile_expr(&label_node, label_ast); /* Label resolution and unwinding adjustments happen in pass two. */ - zend_handle_loops_and_finally(); + 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; @@ -4396,7 +4418,9 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ 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); - if (value_ast->kind == ZEND_AST_VAR && + if (is_this_fetch(value_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } else if (value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { SET_NODE(opline->op2, &value_node); } else { @@ -4484,16 +4508,32 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ znode expr_node, case_node; zend_op *opline; - uint32_t *jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0); - uint32_t opnum_default_jmp; + uint32_t *jmpnz_opnums, opnum_default_jmp; zend_compile_expr(&expr_node, expr_ast); + if (cases->children == 1 && cases->child[0]->child[0] == NULL) { + /* we have to take care about the case that only have one default branch, + * expr result will not be unrefed in this case, but it should be like in ZEND_CASE */ + zend_ast *stmt_ast = cases->child[0]->child[1]; + if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) { + zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL); + } else if (expr_node.op_type == IS_CONST) { + zval_dtor(&expr_node.u.constant); + } + + zend_begin_loop(ZEND_NOP, NULL); + zend_compile_stmt(stmt_ast); + zend_end_loop(get_next_op_number(CG(active_op_array)), NULL); + return; + } + 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)); + jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0); for (i = 0; i < cases->children; ++i) { zend_ast *case_ast = cases->child[i]; zend_ast *cond_ast = case_ast->child[0]; @@ -4650,6 +4690,10 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), zend_resolve_class_name_ast(class_ast)); + if (zend_string_equals_literal(Z_STR_P(var_name), "this")) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } + opline->op2_type = IS_CV; opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name))); @@ -4954,6 +4998,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ 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 { @@ -4989,11 +5037,7 @@ 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_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; + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter"); } if (op_array->fn_flags & ZEND_ACC_VARIADIC) { @@ -6983,7 +7027,9 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */ switch (var_ast->kind) { case ZEND_AST_VAR: - if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { + if (is_this_fetch(var_ast)) { + opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_THIS, NULL, NULL); + } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL); opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET; } else { @@ -7071,6 +7117,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ zend_ast *elem_ast = list->child[i]; zend_ast *value_ast, *key_ast; zend_bool by_ref; + znode value_node, key_node, *key_node_ptr = NULL; if (elem_ast == NULL) { zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays"); @@ -7080,8 +7127,6 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ key_ast = elem_ast->child[1]; by_ref = elem_ast->attr; - znode value_node, key_node, *key_node_ptr = NULL; - if (key_ast) { zend_compile_expr(&key_node, key_ast); zend_handle_numeric_op(&key_node); |