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.c158
1 files changed, 115 insertions, 43 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index da2c83c55e..3adcc55f4e 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -871,6 +871,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);
}
@@ -3132,26 +3133,30 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
}
/* }}} */
-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.scope &&
- !(fbc->common.fn_flags & ZEND_ACC_GENERATOR)) {
- return ZEND_DO_UCALL;
+ 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;
+ }
}
}
} 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;
@@ -3177,7 +3182,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();
@@ -3670,6 +3675,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;
@@ -3693,7 +3699,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);
}
/* }}} */
@@ -4523,7 +4542,7 @@ 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);
@@ -4568,34 +4587,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);
@@ -4899,8 +4937,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;
}
@@ -5304,6 +5343,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);
@@ -5754,6 +5794,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);
@@ -7003,7 +7044,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;
}
@@ -7267,11 +7308,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;
}
}
@@ -7307,7 +7348,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;
}
/* }}} */
@@ -7441,14 +7482,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;
}
/* }}} */
@@ -7859,6 +7902,30 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
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;
@@ -7892,6 +7959,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) {