summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c195
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);