diff options
author | Jakub Zelenka <bukka@php.net> | 2016-06-19 17:05:48 +0100 |
---|---|---|
committer | Jakub Zelenka <bukka@php.net> | 2016-06-19 17:05:48 +0100 |
commit | e63a8540a60e95aa5bd8e269add1b02afcc1b79b (patch) | |
tree | b83a144eec24cc81adab0b9a778f7a730d8df79e /Zend/zend_compile.c | |
parent | 7a4cc73641bb3eb878f7184bcbd026ee663cf2a9 (diff) | |
parent | 53071e647049f099f7f7a0771ddb63fc2cdd621c (diff) | |
download | php-git-e63a8540a60e95aa5bd8e269add1b02afcc1b79b.tar.gz |
Merge branch 'openssl_error_store' into openssl_aead
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 730 |
1 files changed, 516 insertions, 214 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7cf00a0ebe..1d544c6561 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -234,6 +234,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */ 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; @@ -724,12 +725,25 @@ static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */ void zend_do_free(znode *op1) /* {{{ */ { - if (op1->op_type==IS_TMP_VAR) { - zend_emit_op(NULL, ZEND_FREE, op1, NULL); - } 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 @@ -742,6 +756,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; } @@ -753,7 +770,7 @@ void zend_do_free(znode *op1) /* {{{ */ 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) { zend_emit_op(NULL, ZEND_FREE, op1, NULL); @@ -858,6 +875,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); } @@ -1301,7 +1319,7 @@ ZEND_API int zend_unmangle_property_name_ex(const zend_string *name, const char *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); @@ -1913,6 +1931,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; @@ -2249,15 +2271,51 @@ 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 (expr && return_info->type_hint == IS_VOID) { - zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value"); + 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)); @@ -2278,8 +2336,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; @@ -2475,9 +2534,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; } @@ -2508,22 +2564,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 = zend_string_init("this", sizeof("this") - 1, 0); - 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); } @@ -2546,13 +2611,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)); @@ -2604,17 +2669,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]; @@ -2696,19 +2750,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; @@ -2718,14 +2788,69 @@ 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); } - if (!has_elems) { + if (has_elems == 0) { 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"); + } + + 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) { + Z_TRY_ADDREF(expr_node->u.constant); + } + + if (var_ast == NULL) { + zend_error(E_COMPILE_ERROR, "Cannot use empty array entries in keyed array"); + } + + 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; } /* }}} */ @@ -2773,13 +2898,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; } @@ -2839,7 +2967,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 { @@ -2861,7 +2990,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); @@ -2869,7 +2998,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(); } @@ -2977,7 +3106,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; @@ -3003,13 +3131,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) { @@ -3029,9 +3160,17 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ zend_compile_expr(&arg_node, arg); ZEND_ASSERT(arg_node.op_type != IS_CV); if (arg_node.op_type == IS_VAR) { - opcode = ZEND_SEND_VAR_NO_REF; - if (fbc && ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) { - flags |= ZEND_ARG_SEND_BY_REF; + /* 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) { @@ -3048,45 +3187,32 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ 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; @@ -3112,7 +3238,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(); @@ -3419,24 +3545,6 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam } /* }}} */ -static void zend_compile_assert_side_effects(zend_ast *ast) /* {{{ */ -{ - int i; - int children = zend_ast_is_list(ast) ? zend_ast_get_list(ast)->children : zend_ast_get_num_children(ast); - - for (i = 0; i < children; i++) { - zend_ast *child = (zend_ast_is_list(ast) ? zend_ast_get_list(ast)->child : ast->child)[i]; - if (child) { - if (child->kind == ZEND_AST_YIELD) { - zend_mark_function_as_generator(); - } else if (ast->kind >= ZEND_AST_IS_LIST_SHIFT) { - zend_compile_assert_side_effects(child); - } - } - } -} -/* }}} */ - static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *name, zend_function *fbc) /* {{{ */ { if (EG(assertions) >= 0) { @@ -3479,8 +3587,6 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string * } result->op_type = IS_CONST; ZVAL_TRUE(&result->u.constant); - - zend_compile_assert_side_effects((zend_ast *) args); } return SUCCESS; @@ -3493,6 +3599,14 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l 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")) { @@ -3527,8 +3641,6 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l 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; } @@ -3605,6 +3717,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; @@ -3628,7 +3741,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); } /* }}} */ @@ -3646,6 +3772,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ znode class_node, method_node; zend_op *opline; + zend_function *fbc = NULL; zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); @@ -3680,7 +3807,28 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ } 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); } /* }}} */ @@ -3724,7 +3872,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)); } @@ -3753,7 +3902,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 { @@ -3763,6 +3914,10 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */ zend_op *opline = zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL); 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( zend_ast_create(ZEND_AST_VAR, zend_ast_create_znode(&name_node)), &result @@ -3794,6 +3949,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))); @@ -3827,7 +3986,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 { @@ -3852,7 +4013,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)); @@ -3869,8 +4030,18 @@ 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)); + 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; @@ -3897,9 +4068,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); } /* }}} */ @@ -3920,18 +4091,13 @@ void zend_compile_return(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); } - 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(); + 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); @@ -4001,7 +4167,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"); @@ -4084,7 +4250,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; @@ -4230,7 +4396,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"); } } @@ -4257,7 +4423,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 { @@ -4431,10 +4599,12 @@ 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; 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"); @@ -4457,8 +4627,8 @@ void zend_compile_try(zend_ast *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)); } + CG(context).fast_call_var = get_temporary_variable(CG(active_op_array)); /* Push FAST_CALL on unwind stack */ fast_call.opcode = ZEND_FAST_CALL; @@ -4468,6 +4638,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ 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) { @@ -4476,34 +4648,57 @@ 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)); + + 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))); + + 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); @@ -4522,11 +4717,18 @@ 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)); + /* 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); @@ -4547,10 +4749,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); } /* }}} */ @@ -4730,7 +4940,7 @@ 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) { + 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))); @@ -4770,8 +4980,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 { @@ -4807,10 +5026,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_STATIC) == 0) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); - } - 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) { @@ -4857,9 +5073,10 @@ 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); @@ -5014,7 +5231,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)); } @@ -5176,15 +5393,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, *key; + 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)); @@ -5212,6 +5430,7 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as 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); @@ -5272,6 +5491,10 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ } 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); } @@ -5568,7 +5791,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; @@ -5577,31 +5800,25 @@ 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); } else { name = zend_generate_anon_class_name(decl->lex_pos); lcname = zend_string_tolower(name); @@ -5662,6 +5879,7 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ 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); @@ -5673,10 +5891,6 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ 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 */ @@ -5733,11 +5947,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); } } @@ -5948,26 +6166,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)); } @@ -6163,6 +6381,35 @@ 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); @@ -6176,6 +6423,11 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode 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; + } + fn(result, op1, op2); return 1; } @@ -6188,11 +6440,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) /* {{{ */ { zval left; ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1); - mul_function(result, &left, op); + return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op); } /* }}} */ @@ -6210,14 +6462,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; @@ -6390,10 +6650,11 @@ void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */ 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; + } } lefthand_node.op_type = IS_CONST; @@ -6429,7 +6690,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); } } @@ -6748,7 +7009,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 { @@ -6834,12 +7097,18 @@ 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; 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"); + } + + value_ast = elem_ast->child[0]; + key_ast = elem_ast->child[1]; + by_ref = elem_ast->attr; + if (key_ast) { zend_compile_expr(&key_node, key_ast); zend_handle_numeric_op(&key_node); @@ -6876,7 +7145,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; } @@ -7140,11 +7409,11 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ 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; } } @@ -7180,7 +7449,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; } /* }}} */ @@ -7314,14 +7583,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; } /* }}} */ @@ -7728,8 +7999,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; @@ -7763,6 +8060,11 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ 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) { |