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.c171
1 files changed, 131 insertions, 40 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index bc114530e3..4720b4ff59 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -436,14 +436,6 @@ static int zend_add_const_name_literal(zend_op_array *op_array, zend_string *nam
op.constant = zend_add_literal(CG(active_op_array), &_c); \
} while (0)
-#define MAKE_NOP(opline) do { \
- opline->opcode = ZEND_NOP; \
- memset(&opline->result, 0, sizeof(opline->result)); \
- memset(&opline->op1, 0, sizeof(opline->op1)); \
- memset(&opline->op2, 0, sizeof(opline->op2)); \
- opline->result_type = opline->op1_type = opline->op2_type = IS_UNUSED; \
-} while (0)
-
void zend_stop_lexing(void) {
LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit);
}
@@ -1040,7 +1032,7 @@ void zend_do_early_binding(void) /* {{{ */
if (((ce = zend_lookup_class(Z_STR_P(parent_name))) == NULL) ||
((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES) &&
(ce->type == ZEND_INTERNAL_CLASS))) {
- if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) {
+ if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) {
uint32_t *opline_num = &CG(active_op_array)->early_binding;
while (*opline_num != (uint32_t)-1) {
@@ -1797,8 +1789,16 @@ static inline zend_op *zend_delayed_emit_op(znode *result, zend_uchar opcode, zn
zend_op tmp_opline;
init_op(&tmp_opline);
tmp_opline.opcode = opcode;
- SET_NODE(tmp_opline.op1, op1);
- SET_NODE(tmp_opline.op2, op2);
+ if (op1 == NULL) {
+ SET_UNUSED(tmp_opline.op1);
+ } else {
+ SET_NODE(tmp_opline.op1, op1);
+ }
+ if (op2 == NULL) {
+ SET_UNUSED(tmp_opline.op2);
+ } else {
+ SET_NODE(tmp_opline.op2, op2);
+ }
if (result) {
zend_make_var_result(result, &tmp_opline);
}
@@ -1829,11 +1829,25 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
}
/* }}} */
+
+static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */
+{
+ if (return_info->type_hint != IS_UNDEF) {
+ zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
+ }
+}
+/* }}} */
+
+
void zend_emit_final_return(zval *zv) /* {{{ */
{
znode zn;
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);
+ }
+
zn.op_type = IS_CONST;
if (zv) {
ZVAL_COPY_VALUE(&zn.u.constant, zv);
@@ -1978,7 +1992,7 @@ static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
+static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
znode name_node;
@@ -1997,7 +2011,11 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
convert_to_string(&name_node.u.constant);
}
- opline = zend_emit_op(result, ZEND_FETCH_R, &name_node, NULL);
+ if (delayed) {
+ opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &name_node, NULL);
+ } else {
+ opline = zend_emit_op(result, ZEND_FETCH_R, &name_node, NULL);
+ }
opline->extended_value = ZEND_FETCH_LOCAL;
if (name_node.op_type == IS_CONST) {
@@ -2010,10 +2028,10 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
}
/* }}} */
-static void zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
+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 = zend_compile_simple_var_no_cv(result, ast, type);
+ zend_op *opline = zend_compile_simple_var_no_cv(result, ast, type, delayed);
zend_adjust_for_fetch_type(opline, type);
}
}
@@ -2145,7 +2163,7 @@ void zend_compile_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
}
/* }}} */
-zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
+zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */
{
zend_ast *class_ast = ast->child[0];
zend_ast *prop_ast = ast->child[1];
@@ -2162,7 +2180,11 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t
zend_compile_expr(&prop_node, prop_ast);
- opline = zend_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ if (delayed) {
+ opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ } else {
+ opline = zend_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ }
if (opline->op1_type == IS_CONST) {
zend_alloc_polymorphic_cache_slot(opline->op1.constant);
}
@@ -2179,9 +2201,9 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t
}
/* }}} */
-void zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
+void zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */
{
- zend_op *opline = zend_compile_static_prop_common(result, ast, type);
+ zend_op *opline = zend_compile_static_prop_common(result, ast, type, delayed);
zend_adjust_for_fetch_type(opline, type);
}
/* }}} */
@@ -2285,7 +2307,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
if (zend_is_assign_to_self(var_ast, expr_ast)) {
/* $a[0] = $a should evaluate the right $a first */
- zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R);
+ zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
} else {
zend_compile_expr(&expr_node, expr_ast);
}
@@ -2341,9 +2363,6 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
if (zend_is_call(source_ast)) {
opline->extended_value = ZEND_RETURNS_FUNCTION;
- } else if (source_ast->kind == ZEND_AST_NEW) {
- zend_error(E_DEPRECATED, "Assigning the return value of new by reference is deprecated");
- opline->extended_value = ZEND_RETURNS_NEW;
}
}
/* }}} */
@@ -3085,7 +3104,7 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL);
opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET;
} else {
- opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET);
+ opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET, 0);
opline->opcode = ZEND_UNSET_VAR;
}
return;
@@ -3098,7 +3117,7 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
opline->opcode = ZEND_UNSET_OBJ;
return;
case ZEND_AST_STATIC_PROP:
- opline = zend_compile_static_prop_common(NULL, var_ast, BP_VAR_UNSET);
+ opline = zend_compile_static_prop_common(NULL, var_ast, BP_VAR_UNSET, 0);
opline->opcode = ZEND_UNSET_VAR;
return;
EMPTY_SWITCH_DEFAULT_CASE()
@@ -3122,6 +3141,7 @@ static void zend_free_foreach_and_switch_variables(void) /* {{{ */
}
/* }}} */
+
void zend_compile_return(zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
@@ -3147,6 +3167,12 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
opline->op1.var = CG(context).fast_call_var;
}
+ if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_emit_return_type_check(&expr_node, CG(active_op_array)->arg_info - 1);
+ if (expr_node.op_type == IS_CONST) {
+ zval_copy_ctor(&expr_node.u.constant);
+ }
+ }
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
&expr_node, NULL);
@@ -3783,18 +3809,51 @@ void zend_compile_stmt_list(zend_ast *ast) /* {{{ */
}
/* }}} */
-void zend_compile_params(zend_ast *ast) /* {{{ */
+void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_method) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
zend_op_array *op_array = CG(active_op_array);
zend_arg_info *arg_infos;
+
+ if (return_type_ast) {
+ /* Use op_array->arg_info[-1] for return type hinting */
+ arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children + 1, 0);
+ arg_infos->name = NULL;
+ arg_infos->pass_by_reference = (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
+ arg_infos->is_variadic = 0;
+ arg_infos->type_hint = 0;
+ arg_infos->allow_null = 0;
+ arg_infos->class_name = NULL;
+
+ if (return_type_ast->kind == ZEND_AST_TYPE) {
+ arg_infos->type_hint = return_type_ast->attr;
+ } else {
+ zend_string *class_name = zend_ast_get_str(return_type_ast);
- if (list->children == 0) {
- return;
+ if (zend_is_const_default_class_ref(return_type_ast)) {
+ class_name = zend_resolve_class_name_ast(return_type_ast);
+ } else {
+ zend_string_addref(class_name);
+ if (!is_method) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare a return type of %s outside of a class scope", class_name->val);
+ return;
+ }
+ }
+
+ arg_infos->type_hint = IS_OBJECT;
+ arg_infos->class_name = class_name;
+ }
+
+ arg_infos++;
+ op_array->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
+ } else {
+ if (list->children == 0) {
+ return;
+ }
+ arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children, 0);
}
- arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children, 0);
for (i = 0; i < list->children; ++i) {
zend_ast *param_ast = list->child[i];
zend_ast *type_ast = param_ast->child[0];
@@ -4167,6 +4226,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
zend_ast *params_ast = decl->child[0];
zend_ast *uses_ast = decl->child[1];
zend_ast *stmt_ast = decl->child[2];
+ zend_ast *return_type_ast = decl->child[3];
zend_bool is_method = decl->kind == ZEND_AST_METHOD;
zend_op_array *orig_op_array = CG(active_op_array);
@@ -4210,7 +4270,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
}
- zend_compile_params(params_ast);
+ zend_compile_params(params_ast, return_type_ast, is_method);
if (uses_ast) {
zend_compile_closure_uses(uses_ast);
}
@@ -4588,12 +4648,21 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "Constructor %s::%s() cannot be static",
ce->name->val, ce->constructor->common.function_name->val);
}
+ if (ce->constructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Constructor %s::%s() cannot declare a return type",
+ ce->name->val, ce->constructor->common.function_name->val);
+ }
}
if (ce->destructor) {
ce->destructor->common.fn_flags |= ZEND_ACC_DTOR;
if (ce->destructor->common.fn_flags & ZEND_ACC_STATIC) {
zend_error_noreturn(E_COMPILE_ERROR, "Destructor %s::%s() cannot be static",
ce->name->val, ce->destructor->common.function_name->val);
+ } else if (ce->destructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Destructor %s::%s() cannot declare a return type",
+ ce->name->val, ce->destructor->common.function_name->val);
}
}
if (ce->clone) {
@@ -4601,6 +4670,10 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
if (ce->clone->common.fn_flags & ZEND_ACC_STATIC) {
zend_error_noreturn(E_COMPILE_ERROR, "Clone method %s::%s() cannot be static",
ce->name->val, ce->clone->common.function_name->val);
+ } else if (ce->clone->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "%s::%s() cannot declare a return type",
+ ce->name->val, ce->clone->common.function_name->val);
}
}
@@ -5438,6 +5511,21 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR,
"The \"yield\" expression can only be used inside a function");
}
+ if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ const char *msg = "Generators may only declare a return type of Generator, Iterator or Traversable, %s is not permitted";
+ if (!CG(active_op_array)->arg_info[-1].class_name) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg,
+ zend_get_type_by_const(CG(active_op_array)->arg_info[-1].type_hint));
+ }
+ if (!(CG(active_op_array)->arg_info[-1].class_name->len == sizeof("Traversable")-1
+ && zend_binary_strcasecmp(CG(active_op_array)->arg_info[-1].class_name->val, sizeof("Traversable")-1, "Traversable", sizeof("Traversable")-1) == 0) &&
+ !(CG(active_op_array)->arg_info[-1].class_name->len == sizeof("Iterator")-1
+ && zend_binary_strcasecmp(CG(active_op_array)->arg_info[-1].class_name->val, sizeof("Iterator")-1, "Iterator", sizeof("Iterator")-1) == 0) &&
+ !(CG(active_op_array)->arg_info[-1].class_name->len == sizeof("Generator")-1
+ && zend_binary_strcasecmp(CG(active_op_array)->arg_info[-1].class_name->val, sizeof("Generator")-1, "Generator", sizeof("Generator")-1) == 0)) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, CG(active_op_array)->arg_info[-1].class_name->val);
+ }
+ }
CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR;
@@ -5541,7 +5629,7 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL);
opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET;
} else {
- opline = zend_compile_simple_var_no_cv(result, var_ast, BP_VAR_IS);
+ opline = zend_compile_simple_var_no_cv(result, var_ast, BP_VAR_IS, 0);
opline->opcode = ZEND_ISSET_ISEMPTY_VAR;
}
break;
@@ -5554,7 +5642,7 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
opline->opcode = ZEND_ISSET_ISEMPTY_PROP_OBJ;
break;
case ZEND_AST_STATIC_PROP:
- opline = zend_compile_static_prop_common(result, var_ast, BP_VAR_IS);
+ opline = zend_compile_static_prop_common(result, var_ast, BP_VAR_IS, 0);
opline->opcode = ZEND_ISSET_ISEMPTY_VAR;
break;
EMPTY_SWITCH_DEFAULT_CASE()
@@ -5578,7 +5666,7 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
if (expr_ast->kind == ZEND_AST_VAR) {
/* For @$var we need to force a FETCH instruction, otherwise the CV access will
* happen outside the silenced section. */
- zend_compile_simple_var_no_cv(result, expr_ast, BP_VAR_R);
+ zend_compile_simple_var_no_cv(result, expr_ast, BP_VAR_R, 0 );
} else {
zend_compile_expr(result, expr_ast);
}
@@ -6334,7 +6422,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
switch (ast->kind) {
case ZEND_AST_VAR:
- zend_compile_simple_var(result, ast, type);
+ zend_compile_simple_var(result, ast, type, 0);
return;
case ZEND_AST_DIM:
zend_compile_dim(result, ast, type);
@@ -6343,7 +6431,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
zend_compile_prop(result, ast, type);
return;
case ZEND_AST_STATIC_PROP:
- zend_compile_static_prop(result, ast, type);
+ zend_compile_static_prop(result, ast, type, 0);
return;
case ZEND_AST_CALL:
zend_compile_call(result, ast, type);
@@ -6361,11 +6449,8 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
if (type == BP_VAR_W || type == BP_VAR_REF
|| type == BP_VAR_RW || type == BP_VAR_UNSET
) {
- /* For BC reasons =& new Foo is allowed */
- if (type != BP_VAR_REF || ast->kind != ZEND_AST_NEW) {
- zend_error_noreturn(E_COMPILE_ERROR,
- "Cannot use temporary expression in write context");
- }
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use temporary expression in write context");
}
zend_compile_expr(result, ast);
@@ -6378,6 +6463,9 @@ void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{
{
zend_op *opline;
switch (ast->kind) {
+ case ZEND_AST_VAR:
+ zend_compile_simple_var(result, ast, type, 1);
+ return;
case ZEND_AST_DIM:
opline = zend_delayed_compile_dim(result, ast, type);
zend_adjust_for_fetch_type(opline, type);
@@ -6386,6 +6474,9 @@ void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{
opline = zend_delayed_compile_prop(result, ast, type);
zend_adjust_for_fetch_type(opline, type);
return;
+ case ZEND_AST_STATIC_PROP:
+ zend_compile_static_prop(result, ast, type, 1);
+ return;
default:
zend_compile_var(result, ast, type);
return;