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.c103
1 files changed, 89 insertions, 14 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index e3b15e0f49..7476d475de 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) {
@@ -1829,11 +1821,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);
@@ -3119,6 +3125,7 @@ static void zend_free_foreach_and_switch_variables(void) /* {{{ */
}
/* }}} */
+
void zend_compile_return(zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
@@ -3144,6 +3151,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);
@@ -3780,18 +3793,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];
@@ -4164,6 +4210,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);
@@ -4207,7 +4254,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);
}
@@ -4585,12 +4632,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) {
@@ -4598,6 +4654,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);
}
}
@@ -5435,6 +5495,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;